[TASK] The Number of The Workspace
[Packages/TYPO3.CMS.git] / typo3 / sysext / workspaces / Classes / Controller / PreviewController.php
1 <?php
2 namespace TYPO3\CMS\Workspaces\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Backend\View\BackendTemplateView;
19 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
20 use TYPO3\CMS\Core\Messaging\FlashMessage;
21 use TYPO3\CMS\Core\Messaging\FlashMessageService;
22 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Core\Utility\PathUtility;
25 use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
26 use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
27 use TYPO3\CMS\Workspaces\Service\StagesService;
28 use TYPO3\CMS\Workspaces\Service\WorkspaceService;
29
30 /**
31 * Implements the preview controller of the workspace module.
32 */
33 class PreviewController extends AbstractController
34 {
35 /**
36 * @var StagesService
37 */
38 protected $stageService;
39
40 /**
41 * @var WorkspaceService
42 */
43 protected $workspaceService;
44
45 /**
46 * Set up the doc header properly here
47 *
48 * @param ViewInterface $view
49 */
50 protected function initializeView(ViewInterface $view)
51 {
52 if ($view instanceof BackendTemplateView) {
53 /** @var BackendTemplateView $view */
54 parent::initializeView($view);
55 $view->getModuleTemplate()->getDocHeaderComponent()->disable();
56 $this->view->getModuleTemplate()->setFlashMessageQueue($this->controllerContext->getFlashMessageQueue());
57 }
58 }
59
60 /**
61 * Initializes the controller before invoking an action method.
62 *
63 * @return void
64 */
65 protected function initializeAction()
66 {
67 parent::initializeAction();
68 $backendRelPath = ExtensionManagementUtility::extRelPath('backend');
69 $this->stageService = GeneralUtility::makeInstance(StagesService::class);
70 $this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class);
71 $this->pageRenderer->addJsFile($backendRelPath . 'Resources/Public/JavaScript/ExtDirect.StateProvider.js');
72 $this->pageRenderer->loadExtJS(false, false);
73 // Load JavaScript:
74 $this->pageRenderer->addExtDirectCode(array(
75 'TYPO3.Workspaces',
76 'TYPO3.ExtDirectStateProvider'
77 ));
78 $states = $this->getBackendUser()->uc['moduleData']['Workspaces']['States'];
79 $this->pageRenderer->addInlineSetting('Workspaces', 'States', $states);
80 $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', BackendUtility::getModuleUrl('record_edit'));
81 $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', BackendUtility::getModuleUrl('record_history'));
82 // @todo this part should be done with inlineLocallanglabels
83 $this->pageRenderer->addJsInlineCode('workspace-inline-code', $this->generateJavascript());
84 }
85
86 /**
87 * Basically makes sure that the workspace preview is rendered.
88 * The preview itself consists of three frames, so there are
89 * only the frames-urls we've to generate here
90 *
91 * @param int $previewWS
92 * @return void
93 */
94 public function indexAction($previewWS = null)
95 {
96 $backendUser = $this->getBackendUser();
97
98 // Get all the GET parameters to pass them on to the frames
99 $queryParameters = GeneralUtility::_GET();
100
101 // Remove the GET parameters related to the workspaces module and the page id
102 unset($queryParameters['tx_workspaces_web_workspacesworkspaces']);
103 unset($queryParameters['M']);
104 unset($queryParameters['id']);
105
106 // Assemble a query string from the retrieved parameters
107 $queryString = GeneralUtility::implodeArrayForUrl('', $queryParameters);
108
109 // fetch the next and previous stage
110 $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $this->pageId, ($recursionLevel = 0), ($selectionType = 'tables_modify'));
111 list(, $nextStage) = $this->stageService->getNextStageForElementCollection($workspaceItemsArray);
112 list(, $previousStage) = $this->stageService->getPreviousStageForElementCollection($workspaceItemsArray);
113 /** @var $wsService WorkspaceService */
114 $wsService = GeneralUtility::makeInstance(WorkspaceService::class);
115 $wsList = $wsService->getAvailableWorkspaces();
116 $activeWorkspace = $backendUser->workspace;
117 if (!is_null($previewWS)) {
118 if (in_array($previewWS, array_keys($wsList)) && $activeWorkspace != $previewWS) {
119 $activeWorkspace = $previewWS;
120 $backendUser->setWorkspace($activeWorkspace);
121 BackendUtility::setUpdateSignal('updatePageTree');
122 }
123 }
124 /** @var $uriBuilder UriBuilder */
125 $uriBuilder = $this->objectManager->get(UriBuilder::class);
126 $wsSettingsPath = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
127 $wsSettingsUri = $uriBuilder->uriFor('singleIndex', array(), ReviewController::class, 'workspaces', 'web_workspacesworkspaces');
128 $wsSettingsParams = '&tx_workspaces_web_workspacesworkspaces[controller]=Review';
129 $wsSettingsUrl = $wsSettingsPath . $wsSettingsUri . $wsSettingsParams;
130 $viewDomain = BackendUtility::getViewDomain($this->pageId);
131 $wsBaseUrl = $viewDomain . '/index.php?id=' . $this->pageId . $queryString;
132 // @todo - handle new pages here
133 // branchpoints are not handled anymore because this feature is not supposed anymore
134 if (WorkspaceService::isNewPage($this->pageId)) {
135 $wsNewPageUri = $uriBuilder->uriFor('newPage', array(), PreviewController::class, 'workspaces', 'web_workspacesworkspaces');
136 $wsNewPageParams = '&tx_workspaces_web_workspacesworkspaces[controller]=Preview';
137 $liveUrl = $wsSettingsPath . $wsNewPageUri . $wsNewPageParams . '&ADMCMD_prev=IGNORE';
138 } else {
139 $liveUrl = $wsBaseUrl . '&ADMCMD_noBeUser=1&ADMCMD_prev=IGNORE';
140 }
141 $wsUrl = $wsBaseUrl . '&ADMCMD_prev=IGNORE&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS=' . $backendUser->workspace;
142 $backendDomain = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY');
143 $splitPreviewTsConfig = BackendUtility::getModTSconfig($this->pageId, 'workspaces.splitPreviewModes');
144 $splitPreviewModes = GeneralUtility::trimExplode(',', $splitPreviewTsConfig['value']);
145 $allPreviewModes = array('slider', 'vbox', 'hbox');
146 if (!array_intersect($splitPreviewModes, $allPreviewModes)) {
147 $splitPreviewModes = $allPreviewModes;
148 }
149
150 $wsList = $wsService->getAvailableWorkspaces();
151 $activeWorkspace = $backendUser->workspace;
152
153 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Workspaces/Preview');
154 $this->pageRenderer->addInlineSetting('Workspaces', 'SplitPreviewModes', $splitPreviewModes);
155 $this->pageRenderer->addInlineSetting('Workspaces', 'token', FormProtectionFactory::get('backend')->generateToken('extDirect'));
156
157 $cssFile = 'EXT:workspaces/Resources/Public/Css/preview.css';
158 $cssFile = GeneralUtility::getFileAbsFileName($cssFile);
159 $this->pageRenderer->addCssFile(PathUtility::getAbsoluteWebPath($cssFile));
160
161 $backendUser->setAndSaveSessionData('workspaces.backend_domain', GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'));
162
163 $logoPath = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Images/typo3-topbar@2x.png');
164 list($logoWidth, $logoHeight) = @getimagesize($logoPath);
165
166 // High-resolution?
167 $logoWidth = $logoWidth/2;
168 $logoHeight = $logoHeight/2;
169
170 $this->view->assignMultiple([
171 'logoUrl' => PathUtility::getAbsoluteWebPath($logoPath),
172 'logoLink' => TYPO3_URL_GENERAL,
173 'logoWidth' => $logoWidth,
174 'logoHeight' => $logoHeight,
175 'liveUrl' => $liveUrl,
176 'wsUrl' => $wsUrl,
177 'wsSettingsUrl' => $wsSettingsUrl,
178 'backendDomain' => $backendDomain,
179 'activeWorkspace' => $wsList[$activeWorkspace],
180 'splitPreviewModes' => $splitPreviewModes,
181 'firstPreviewMode' => current($splitPreviewModes),
182 'enablePreviousStageButton' => !$this->isInvalidStage($previousStage),
183 'enableNextStageButton' => !$this->isInvalidStage($nextStage),
184 'enableDiscardStageButton' => !$this->isInvalidStage($nextStage) || !$this->isInvalidStage($previousStage),
185 'nextStage' => $nextStage['title'],
186 'nextStageId' => $nextStage['uid'],
187 'prevStage' => $previousStage['title'],
188 'prevStageId' => $previousStage['uid'],
189 ]);
190 foreach ($this->getAdditionalResourceService()->getLocalizationResources() as $localizationResource) {
191 $this->pageRenderer->addInlineLanguageLabelFile($localizationResource);
192 }
193 }
194
195 /**
196 * Evaluate the activate state based on given $stageArray.
197 *
198 * @param array $stageArray
199 * @return bool
200 */
201 protected function isInvalidStage($stageArray)
202 {
203 return !(is_array($stageArray) && !empty($stageArray));
204 }
205
206 /**
207 * @return void
208 */
209 public function newPageAction()
210 {
211 /** @var FlashMessage $flashMessage */
212 $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);
213 /** @var $flashMessageService FlashMessageService */
214 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
215 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
216 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
217 $defaultFlashMessageQueue->enqueue($flashMessage);
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 index.php?M=main module
224 *
225 * @return string
226 */
227 protected function generateJavascript()
228 {
229 $backendUser = $this->getBackendUser();
230 $lang = $this->getLanguageService();
231 // If another page module was specified, replace the default Page module with the new one
232 $newPageModule = trim($backendUser->getTSConfigVal('options.overridePageModule'));
233 $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
234 if (!$backendUser->check('modules', $pageModule)) {
235 $pageModule = '';
236 }
237 $t3Configuration = array(
238 'siteUrl' => GeneralUtility::getIndpEnv('TYPO3_SITE_URL'),
239 'username' => htmlspecialchars($backendUser->user['username']),
240 'uniqueID' => GeneralUtility::shortMD5(uniqid('', true)),
241 'pageModule' => $pageModule,
242 'inWorkspace' => $backendUser->workspace !== 0,
243 'workspaceFrontendPreviewEnabled' => $backendUser->user['workspace_preview'] ? 1 : 0,
244 'topBarHeight' => isset($GLOBALS['TBE_STYLES']['dims']['topFrameH']) ? (int)$GLOBALS['TBE_STYLES']['dims']['topFrameH'] : 30,
245 'showRefreshLoginPopup' => isset($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup']) ? (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] : false,
246 'debugInWindow' => $backendUser->uc['debugInWindow'] ? 1 : 0,
247 'ContextHelpWindows' => array(
248 'width' => 600,
249 'height' => 400
250 )
251 );
252 $t3LLLcore = array(
253 'waitTitle' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_logging_in'),
254 'refresh_login_failed' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed'),
255 'refresh_login_failed_message' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed_message'),
256 'refresh_login_title' => sprintf($lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_title'), htmlspecialchars($backendUser->user['username'])),
257 'login_expired' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_expired'),
258 'refresh_login_username' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_username'),
259 'refresh_login_password' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_password'),
260 'refresh_login_emptyPassword' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_emptyPassword'),
261 'refresh_login_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_button'),
262 'refresh_logout_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_logout_button'),
263 'refresh_exit_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_exit_button'),
264 'please_wait' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.please_wait'),
265 'loadingIndicator' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:loadingIndicator'),
266 'be_locked' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.be_locked'),
267 'refresh_login_countdown_singular' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown_singular'),
268 'refresh_login_countdown' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown'),
269 'login_about_to_expire' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire'),
270 'login_about_to_expire_title' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire_title'),
271 'refresh_login_refresh_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_refresh_button'),
272 'refresh_direct_logout_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_direct_logout_button'),
273 'tabs_closeAll' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeAll'),
274 'tabs_closeOther' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeOther'),
275 'tabs_close' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.close'),
276 'tabs_openInBrowserWindow' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.openInBrowserWindow'),
277 'donateWindow_title' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.title'),
278 'donateWindow_message' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.message'),
279 'donateWindow_button_donate' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_donate'),
280 'donateWindow_button_disable' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_disable'),
281 'donateWindow_button_postpone' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_postpone')
282 );
283 return '
284 TYPO3.configuration = ' . json_encode($t3Configuration) . ';
285 TYPO3.LLL = {
286 core : ' . json_encode($t3LLLcore) . '
287 };
288
289 /**
290 * TypoSetup object.
291 */
292 function typoSetup() { //
293 this.username = TYPO3.configuration.username;
294 this.uniqueID = TYPO3.configuration.uniqueID;
295 }
296 var TS = new typoSetup();
297 //backwards compatibility
298 ';
299 }
300
301 /**
302 * @return \TYPO3\CMS\Lang\LanguageService
303 */
304 protected function getLanguageService()
305 {
306 return $GLOBALS['LANG'];
307 }
308
309 /**
310 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
311 */
312 protected function getBackendUser()
313 {
314 return $GLOBALS['BE_USER'];
315 }
316 }