[DOCS] Remove confusing "cron command" ambiguity.
[Packages/TYPO3.CMS.git] / typo3 / sysext / workspaces / Classes / Controller / PreviewController.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Workspaces\Controller;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Backend\Routing\UriBuilder;
21 use TYPO3\CMS\Backend\Template\ModuleTemplate;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
23 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
24 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
25 use TYPO3\CMS\Core\Http\HtmlResponse;
26 use TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException;
27 use TYPO3\CMS\Core\Routing\UnableToLinkToPageException;
28 use TYPO3\CMS\Core\Site\SiteFinder;
29 use TYPO3\CMS\Core\Utility\GeneralUtility;
30 use TYPO3\CMS\Fluid\View\StandaloneView;
31 use TYPO3\CMS\Workspaces\Service\StagesService;
32 use TYPO3\CMS\Workspaces\Service\WorkspaceService;
33 use TYPO3Fluid\Fluid\View\ViewInterface;
34
35 /**
36 * Implements the preview controller of the workspace module.
37 * @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
38 */
39 class PreviewController
40 {
41 /**
42 * @var StagesService
43 */
44 protected $stageService;
45
46 /**
47 * @var WorkspaceService
48 */
49 protected $workspaceService;
50
51 /**
52 * @var int
53 */
54 protected $pageId;
55
56 /**
57 * ModuleTemplate object
58 *
59 * @var ModuleTemplate
60 */
61 protected $moduleTemplate;
62
63 /**
64 * @var ViewInterface
65 */
66 protected $view;
67
68 /**
69 * Set up the module template
70 */
71 public function __construct()
72 {
73 $this->stageService = GeneralUtility::makeInstance(StagesService::class);
74 $this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class);
75 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
76 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Workspaces/Preview');
77 $this->moduleTemplate->getDocHeaderComponent()->disable();
78 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
79 $states = $this->getBackendUser()->uc['moduleData']['Workspaces']['States'];
80 $this->moduleTemplate->getPageRenderer()->addInlineSetting('Workspaces', 'States', $states);
81 $this->moduleTemplate->getPageRenderer()->addInlineSetting('FormEngine', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_edit'));
82 $this->moduleTemplate->getPageRenderer()->addInlineSetting('RecordHistory', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('record_history'));
83 $this->moduleTemplate->getPageRenderer()->addJsInlineCode('workspace-inline-code', $this->generateJavascript());
84 $this->moduleTemplate->getPageRenderer()->addCssFile('EXT:workspaces/Resources/Public/Css/preview.css');
85 $this->moduleTemplate->getPageRenderer()->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/wizard.xlf');
86 $this->moduleTemplate->getPageRenderer()->addInlineLanguageLabelFile('EXT:workspaces/Resources/Private/Language/locallang.xlf');
87 }
88
89 /**
90 * Sets up the view
91 *
92 * @param string $templateName
93 */
94 protected function initializeView(string $templateName)
95 {
96 $this->view = GeneralUtility::makeInstance(StandaloneView::class);
97 $this->view->setTemplate($templateName);
98 $this->view->setTemplateRootPaths(['EXT:workspaces/Resources/Private/Templates/Preview']);
99 $this->view->setPartialRootPaths(['EXT:workspaces/Resources/Private/Partials']);
100 $this->view->setLayoutRootPaths(['EXT:workspaces/Resources/Private/Layouts']);
101 }
102
103 /**
104 * Basically makes sure that the workspace preview is rendered.
105 * The preview itself consists of three frames, so there are
106 * only the frames-urls we have to generate here
107 *
108 * @param ServerRequestInterface $request
109 * @return ResponseInterface
110 * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
111 */
112 public function handleRequest(ServerRequestInterface $request): ResponseInterface
113 {
114 $this->initializeView('Index');
115
116 // Get all the GET parameters to pass them on to the frames
117 $queryParameters = $request->getQueryParams();
118
119 $previewWS = $queryParameters['previewWS'] ?? null;
120 $this->pageId = (int)$queryParameters['id'];
121
122 // Remove the GET parameters related to the workspaces module
123 unset($queryParameters['route'], $queryParameters['token'], $queryParameters['previewWS']);
124
125 // fetch the next and previous stage
126 $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace(
127 $this->stageService->getWorkspaceId(),
128 $filter = 1,
129 $stage = -99,
130 $this->pageId,
131 $recursionLevel = 0,
132 $selectionType = 'tables_modify'
133 );
134 list(, $nextStage) = $this->stageService->getNextStageForElementCollection($workspaceItemsArray);
135 list(, $previousStage) = $this->stageService->getPreviousStageForElementCollection($workspaceItemsArray);
136 $availableWorkspaces = $this->workspaceService->getAvailableWorkspaces();
137 $activeWorkspace = $this->getBackendUser()->workspace;
138 if ($previewWS !== null && array_key_exists($previewWS, $availableWorkspaces) && $activeWorkspace != $previewWS) {
139 $activeWorkspace = $previewWS;
140 $this->getBackendUser()->setWorkspace($activeWorkspace);
141 BackendUtility::setUpdateSignal('updatePageTree');
142 }
143
144 $siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
145 try {
146 $site = $siteFinder->getSiteByPageId($this->pageId);
147 if (isset($queryParameters['L'])) {
148 $queryParameters['_language'] = $site->getLanguageById((int)$queryParameters['L']);
149 unset($queryParameters['L']);
150 }
151 if (!WorkspaceService::isNewPage($this->pageId)) {
152 $parameters = $queryParameters;
153 $parameters['ADMCMD_noBeUser'] = 1;
154 $parameters['ADMCMD_prev'] = 'IGNORE';
155 $liveUrl = (string)$site->getRouter()->generateUri($this->pageId, $parameters);
156 }
157
158 $parameters = $queryParameters;
159 $parameters['ADMCMD_view'] = 1;
160 $parameters['ADMCMD_editIcons'] = 1;
161 $parameters['ADMCMD_prev'] = 'IGNORE';
162 $wsUrl = (string)$site->getRouter()->generateUri($this->pageId, $parameters);
163 } catch (SiteNotFoundException | InvalidRouteArgumentsException $e) {
164 throw new UnableToLinkToPageException('The page ' . $this->pageId . ' had no proper connection to a site, no link could be built.', 1559794913);
165 }
166
167 // Build the "list view" link to the review controller
168 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
169 $wsSettingsUrl = $uriBuilder->buildUriFromRoute('web_WorkspacesWorkspaces', [
170 'tx_workspaces_web_workspacesworkspaces' => ['action' => 'singleIndex'],
171 'id' => $this->pageId
172 ], UriBuilder::ABSOLUTE_URL);
173
174 // Evaluate available preview modes
175 $splitPreviewModes = GeneralUtility::trimExplode(
176 ',',
177 BackendUtility::getPagesTSconfig($this->pageId)['workspaces.']['splitPreviewModes'] ?? '',
178 true
179 );
180 $allPreviewModes = ['slider', 'vbox', 'hbox'];
181 if (!array_intersect($splitPreviewModes, $allPreviewModes)) {
182 $splitPreviewModes = $allPreviewModes;
183 }
184 $this->moduleTemplate->getPageRenderer()->addJsFile('EXT:backend/Resources/Public/JavaScript/backend.js');
185 $this->moduleTemplate->getPageRenderer()->addInlineSetting('Workspaces', 'SplitPreviewModes', $splitPreviewModes);
186 $this->moduleTemplate->getPageRenderer()->addInlineSetting('Workspaces', 'id', $this->pageId);
187
188 $this->view->assignMultiple([
189 'logoLink' => TYPO3_URL_GENERAL,
190 'liveUrl' => $liveUrl ?? false,
191 'wsUrl' => $wsUrl,
192 'wsSettingsUrl' => $wsSettingsUrl,
193 'activeWorkspace' => $availableWorkspaces[$activeWorkspace],
194 'splitPreviewModes' => $splitPreviewModes,
195 'firstPreviewMode' => current($splitPreviewModes),
196 'enablePreviousStageButton' => $this->isValidStage($previousStage),
197 'enableNextStageButton' => $this->isValidStage($nextStage),
198 'enableDiscardStageButton' => $this->isValidStage($nextStage) || $this->isValidStage($previousStage),
199 'nextStage' => $nextStage['title'],
200 'nextStageId' => $nextStage['uid'],
201 'prevStage' => $previousStage['title'],
202 'prevStageId' => $previousStage['uid'],
203 ]);
204
205 $this->moduleTemplate->setContent($this->view->render());
206 return new HtmlResponse($this->moduleTemplate->renderContent());
207 }
208
209 /**
210 * Evaluate the activate state based on given $stageArray.
211 *
212 * @param array $stageArray
213 * @return bool
214 */
215 protected function isValidStage($stageArray): bool
216 {
217 return is_array($stageArray) && !empty($stageArray);
218 }
219
220 /**
221 * Generates the JavaScript code for the backend,
222 * and since we're loading a backend module outside of the actual backend
223 * this copies parts of the backend main script.
224 *
225 * @return string
226 */
227 protected function generateJavascript(): string
228 {
229 // Needed for FormEngine manipulation (date picker)
230 $dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? ['MM-DD-YYYY', 'HH:mm MM-DD-YYYY'] : ['DD-MM-YYYY', 'HH:mm DD-MM-YYYY']);
231 $this->moduleTemplate->getPageRenderer()->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
232
233 // If another page module was specified, replace the default Page module with the new one
234 $pageModule = \trim($this->getBackendUser()->getTSConfig()['options.']['overridePageModule'] ?? '');
235 $pageModule = BackendUtility::isModuleSetInTBE_MODULES($pageModule) ? $pageModule : 'web_layout';
236 $pageModuleUrl = '';
237 if (!$this->getBackendUser()->check('modules', $pageModule)) {
238 $pageModule = '';
239 } else {
240 $pageModuleUrl = (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute($pageModule);
241 }
242 $t3Configuration = [
243 'username' => htmlspecialchars($this->getBackendUser()->user['username']),
244 'pageModule' => $pageModule,
245 'pageModuleUrl' => $pageModuleUrl,
246 'inWorkspace' => $this->getBackendUser()->workspace !== 0,
247 'showRefreshLoginPopup' => (bool)($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] ?? false)
248 ];
249
250 return 'TYPO3.configuration = ' . json_encode($t3Configuration) . ';';
251 }
252
253 /**
254 * @return BackendUserAuthentication
255 */
256 protected function getBackendUser(): BackendUserAuthentication
257 {
258 return $GLOBALS['BE_USER'];
259 }
260 }