‪TYPO3CMS  ‪main
ReviewController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
20 use Psr\Http\Message\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
28 use TYPO3\CMS\Backend\Utility\BackendUtility;
31 use TYPO3\CMS\Core\Imaging\IconSize;
39 
43 #[Controller]
45 {
46  public function ‪__construct(
47  protected readonly ‪WorkspaceService $workspaceService,
48  protected readonly ‪StagesService $stagesService,
49  protected readonly ‪IconFactory $iconFactory,
50  protected readonly ‪PageRenderer $pageRenderer,
51  protected readonly ‪UriBuilder $uriBuilder,
52  protected readonly ‪ModuleTemplateFactory $moduleTemplateFactory,
53  protected readonly ‪WorkspacePublishGate $workspacePublishGate,
54  protected readonly ‪TranslationConfigurationProvider $translationConfigurationProvider
55  ) {}
56 
61  public function ‪handleRequest(ServerRequestInterface $request): ResponseInterface
62  {
63  $queryParams = $request->getQueryParams();
64  $moduleData = $request->getAttribute('moduleData');
65  $pageUid = (int)($queryParams['id'] ?? 0);
66 
67  $icons = [
68  'language' => $this->iconFactory->getIcon('flags-multiple', IconSize::SMALL)->render(),
69  'integrity' => $this->iconFactory->getIcon('status-dialog-information', IconSize::SMALL)->render(),
70  'success' => $this->iconFactory->getIcon('status-dialog-ok', IconSize::SMALL)->render(),
71  'info' => $this->iconFactory->getIcon('status-dialog-information', IconSize::SMALL)->render(),
72  'warning' => $this->iconFactory->getIcon('status-dialog-warning', IconSize::SMALL)->render(),
73  'error' => $this->iconFactory->getIcon('status-dialog-error', IconSize::SMALL)->render(),
74  ];
75  $this->pageRenderer->addInlineSetting('Workspaces', 'icons', $icons);
76  $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('record_edit'));
77  $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('record_history'));
78  $this->pageRenderer->addInlineSetting('Workspaces', 'id', $pageUid);
79  $this->pageRenderer->addInlineSetting('WebLayout', 'moduleUrl', (string)$this->uriBuilder->buildUriFromRoute('web_layout'));
80  $this->pageRenderer->loadJavaScriptModule('@typo3/workspaces/backend.js');
81  $this->pageRenderer->loadJavaScriptModule('@typo3/backend/multi-record-selection.js');
82  $this->pageRenderer->addInlineLanguageLabelFile('EXT:workspaces/Resources/Private/Language/locallang.xlf');
83 
84  $backendUser = $this->‪getBackendUser();
85  $pageTitle = '';
86  $pageRecord = [];
87  if ($pageUid) {
88  $pageRecord = BackendUtility::getRecord('pages', $pageUid);
89  if ($pageRecord) {
90  $pageTitle = BackendUtility::getRecordTitle('pages', $pageRecord);
91  }
92  } else {
93  $pageTitle = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?? '';
94  }
95  $availableWorkspaces = $this->workspaceService->getAvailableWorkspaces();
96  $customWorkspaceExists = $this->‪customWorkspaceExists($availableWorkspaces);
97  $activeWorkspace = $backendUser->workspace;
98  $activeWorkspaceTitle = $this->workspaceService->getWorkspaceTitle($activeWorkspace);
99  $workspaceSwitched = '';
100  if (isset($queryParams['workspace'])) {
101  $switchWs = (int)$queryParams['workspace'];
102  if (array_key_exists($switchWs, $availableWorkspaces) && $activeWorkspace !== $switchWs) {
103  $activeWorkspace = $switchWs;
104  $backendUser->setWorkspace($activeWorkspace);
105  $activeWorkspaceTitle = $this->workspaceService->getWorkspaceTitle($activeWorkspace);
106  $workspaceSwitched = GeneralUtility::jsonEncodeForHtmlAttribute(['id' => $activeWorkspace, 'title' => $activeWorkspaceTitle]);
107  }
108  }
109  $workspaceIsAccessible = $backendUser->workspace !== ‪WorkspaceService::LIVE_WORKSPACE_ID && $pageUid > 0;
110 
111  $selectedLanguage = (string)$moduleData->get('language');
112  $view = $this->moduleTemplateFactory->create($request);
113  $view->assignMultiple([
114  'isAdmin' => $backendUser->isAdmin(),
115  'customWorkspaceExists' => $customWorkspaceExists,
116  'showGrid' => $workspaceIsAccessible,
117  'showLegend' => $workspaceIsAccessible,
118  'pageUid' => $pageUid,
119  'pageTitle' => $pageTitle,
120  'activeWorkspaceUid' => $activeWorkspace,
121  'activeWorkspaceTitle' => $activeWorkspaceTitle,
122  'availableLanguages' => $this->‪getSystemLanguages($pageUid, $selectedLanguage),
123  'availableStages' => $this->stagesService->getStagesForWSUser(),
124  'availableSelectStages' => $this->‪getAvailableSelectStages(),
125  'stageActions' => $this->‪getStageActions(),
126  'showEntireWorkspaceDropDown' => !(($backendUser->workspaceRec['publish_access'] ?? 0) & ‪WorkspaceService::PUBLISH_ACCESS_HIDE_ENTIRE_WORKSPACE_ACTION_DROPDOWN),
127  'selectedLanguage' => $selectedLanguage,
128  'selectedDepth' => (int)$moduleData->get('depth', ($pageUid === 0 ? 999 : 1)),
129  'selectedStage' => (int)$moduleData->get('stage'),
130  'workspaceSwitched' => $workspaceSwitched,
131  ]);
132  $view->setTitle(
133  $this->‪getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab') . ' [' . $activeWorkspaceTitle . ']',
134  $pageTitle
135  );
136  $view->getDocHeaderComponent()->setMetaInformation($pageRecord);
137  $this->‪addWorkspaceSelector($view, $availableWorkspaces, $activeWorkspace, $pageUid);
138  $this->‪addPreviewLink($view, $pageUid, $activeWorkspace);
139  $this->‪addEditWorkspaceRecordButton($view, $pageUid, $activeWorkspace);
140  $this->‪addShortcutButton($view, $activeWorkspaceTitle, $pageTitle, $pageUid);
141  return $view->renderResponse('Review/Index');
142  }
143 
147  protected function ‪addWorkspaceSelector(‪ModuleTemplate $view, array $availableWorkspaces, int $activeWorkspace, int $pageUid): void
148  {
149  $items = [];
150  $items[] = [
151  'title' => $availableWorkspaces[$activeWorkspace],
152  'active' => true,
153  'url' => $this->‪getModuleUri($pageUid),
154  ];
155  foreach ($availableWorkspaces as $workspaceId => $workspaceTitle) {
156  if ($workspaceId === $activeWorkspace) {
157  continue;
158  }
159  $items[] = [
160  'title' => $workspaceTitle,
161  'active' => false,
162  'url' => $this->‪getModuleUri($pageUid, (int)$workspaceId),
163  ];
164  }
165  $actionMenu = $view->‪getDocHeaderComponent()->getMenuRegistry()->makeMenu();
166  $actionMenu->setIdentifier('workspaceSelector');
167  $actionMenu->setLabel('');
168  foreach ($items as $workspaceData) {
169  $menuItem = $actionMenu
170  ->makeMenuItem()
171  ->setTitle($workspaceData['title'])
172  ->setHref($workspaceData['url']);
173  if ($workspaceData['active']) {
174  $menuItem->setActive(true);
175  }
176  $actionMenu->addMenuItem($menuItem);
177  }
178  $view->‪getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
179  }
180 
181  protected function ‪addShortcutButton(‪ModuleTemplate $view, string $activeWorkspaceTitle, string $pageTitle, int $pageId): void
182  {
183  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
184  $shortcutButton = $buttonBar->makeShortcutButton()
185  ->setRouteIdentifier('workspaces_admin')
186  ->setDisplayName(sprintf('%s: %s [%d]', $activeWorkspaceTitle, $pageTitle, $pageId))
187  ->setArguments(['id' => (int)$pageId]);
188  $buttonBar->addButton($shortcutButton);
189  }
190 
191  protected function ‪addPreviewLink(‪ModuleTemplate $view, int $pageUid, int $activeWorkspace): void
192  {
193  $canCreatePreviewLink = false;
194  if ($pageUid > 0 && $activeWorkspace > 0) {
195  $pageRecord = BackendUtility::getRecord('pages', $pageUid);
196  BackendUtility::workspaceOL('pages', $pageRecord, $activeWorkspace);
197  if (VersionState::tryFrom($pageRecord['t3ver_state'] ?? 0) !== VersionState::DELETE_PLACEHOLDER->value) {
198  $canCreatePreviewLink = true;
199  }
200  }
201  if ($canCreatePreviewLink) {
202  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
203  $showButton = $buttonBar->makeLinkButton()
204  ->setHref('#')
205  ->setClasses('t3js-preview-link')
206  ->setShowLabelText(true)
207  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:tooltip.generatePagePreview'))
208  ->setIcon($this->iconFactory->getIcon('actions-version-workspaces-preview-link', IconSize::SMALL));
209  $buttonBar->addButton($showButton);
210  }
211  }
212 
213  protected function ‪addEditWorkspaceRecordButton(‪ModuleTemplate $view, int $pageUid, int $activeWorkspace): void
214  {
215  $backendUser = $this->‪getBackendUser();
216  if ($backendUser->isAdmin() && $activeWorkspace > 0) {
217  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
218  $editWorkspaceRecordUrl = (string)$this->uriBuilder->buildUriFromRoute('record_edit', [
219  'edit' => [
220  'sys_workspace' => [
221  $activeWorkspace => 'edit',
222  ],
223  ],
224  'returnUrl' => (string)$this->uriBuilder->buildUriFromRoute('workspaces_admin', ['id' => $pageUid]),
225  ]);
226  $editSettingsButton = $buttonBar->makeLinkButton()
227  ->setHref($editWorkspaceRecordUrl)
228  ->setShowLabelText(true)
229  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:button.editWorkspaceSettings'))
230  ->setIcon($this->iconFactory->getIcon('actions-cog-alt', IconSize::SMALL));
231  $buttonBar->addButton(
232  $editSettingsButton,
234  90
235  );
236  }
237  }
238 
239  protected function ‪getModuleUri(int $pageUid, int $workspaceId = null): string
240  {
241  $parameters = [
242  'id' => $pageUid,
243  ];
244  if ($workspaceId !== null) {
245  $parameters['workspace'] = $workspaceId;
246  }
247  return (string)$this->uriBuilder->buildUriFromRoute('workspaces_admin', $parameters);
248  }
249 
253  protected function ‪customWorkspaceExists(array $workspaceList): bool
254  {
255  foreach (array_keys($workspaceList) as $workspaceId) {
256  if ($workspaceId > 0) {
257  return true;
258  }
259  }
260  return false;
261  }
262 
266  protected function ‪getSystemLanguages(int $pageId, string $selectedLanguage): array
267  {
268  ‪$languages = $this->translationConfigurationProvider->getSystemLanguages($pageId);
269  if (isset(‪$languages[-1])) {
270  ‪$languages[-1]['uid'] = 'all';
271  }
272  foreach (‪$languages as &$language) {
273  // needs to be strict type checking as this is not possible in fluid
274  if ((string)$language['uid'] === $selectedLanguage) {
275  $language['active'] = true;
276  }
277  }
278  return ‪$languages;
279  }
280 
284  protected function ‪getStageActions(): array
285  {
286  $languageService = $this->‪getLanguageService();
287  $currentWorkspace = $this->workspaceService->getCurrentWorkspace();
288  $backendUser = $this->‪getBackendUser();
289  $actions = [];
290  $massActionsEnabled = (bool)($backendUser->getTSConfig()['options.']['workspaces.']['enableMassActions'] ?? true);
291  if ($massActionsEnabled) {
292  if ($this->workspacePublishGate->isGranted($backendUser, $currentWorkspace)
293  && !(($backendUser->workspaceRec['publish_access'] ?? 0) & ‪WorkspaceService::PUBLISH_ACCESS_ONLY_IN_PUBLISH_STAGE)
294  ) {
295  $actions[] = ['action' => 'publish', 'title' => $languageService->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:label_doaction_publish')];
296  }
297  if ($currentWorkspace !== ‪WorkspaceService::LIVE_WORKSPACE_ID) {
298  $actions[] = ['action' => 'discard', 'title' => $languageService->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:label_doaction_discard')];
299  }
300  }
301  return $actions;
302  }
303 
308  protected function ‪getAvailableSelectStages(): array
309  {
310  $languageService = $this->‪getLanguageService();
311  $stages = $this->stagesService->getStagesForWSUser();
312  return array_merge([
313  [
314  'uid' => -99,
315  'label' => $languageService->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:stage_all'),
316  ],
317  ], array_filter($stages, static fn(array $stage): bool => (int)($stage['uid'] ?? 0) !== ‪StagesService::STAGE_PUBLISH_EXECUTE_ID));
318  }
319 
321  {
322  return ‪$GLOBALS['LANG'];
323  }
324 
326  {
327  return ‪$GLOBALS['BE_USER'];
328  }
329 }
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getSystemLanguages
‪getSystemLanguages(int $pageId, string $selectedLanguage)
Definition: ReviewController.php:266
‪TYPO3\CMS\Workspaces\Controller\ReviewController\handleRequest
‪handleRequest(ServerRequestInterface $request)
Definition: ReviewController.php:61
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getBackendUser
‪getBackendUser()
Definition: ReviewController.php:325
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_LEFT
‪const BUTTON_POSITION_LEFT
Definition: ButtonBar.php:37
‪TYPO3\CMS\Backend\Template\Components\ButtonBar
Definition: ButtonBar.php:33
‪TYPO3\CMS\Workspaces\Authorization\WorkspacePublishGate
Definition: WorkspacePublishGate.php:29
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getAvailableSelectStages
‪getAvailableSelectStages()
Definition: ReviewController.php:308
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:33
‪$languages
‪$languages
Definition: updateIsoDatabase.php:104
‪TYPO3\CMS\Core\Versioning\VersionState
‪VersionState
Definition: VersionState.php:22
‪TYPO3\CMS\Workspaces\Controller\ReviewController
Definition: ReviewController.php:45
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\PUBLISH_ACCESS_HIDE_ENTIRE_WORKSPACE_ACTION_DROPDOWN
‪const PUBLISH_ACCESS_HIDE_ENTIRE_WORKSPACE_ACTION_DROPDOWN
Definition: WorkspaceService.php:44
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getLanguageService
‪getLanguageService()
Definition: ReviewController.php:320
‪TYPO3\CMS\Workspaces\Controller\ReviewController\customWorkspaceExists
‪customWorkspaceExists(array $workspaceList)
Definition: ReviewController.php:253
‪TYPO3\CMS\Backend\Attribute\Controller
Definition: Controller.php:25
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\LIVE_WORKSPACE_ID
‪const LIVE_WORKSPACE_ID
Definition: WorkspaceService.php:40
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Workspaces\Service\StagesService\STAGE_PUBLISH_EXECUTE_ID
‪const STAGE_PUBLISH_EXECUTE_ID
Definition: StagesService.php:33
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Core\Page\PageRenderer
Definition: PageRenderer.php:46
‪TYPO3\CMS\Workspaces\Controller\ReviewController\addShortcutButton
‪addShortcutButton(ModuleTemplate $view, string $activeWorkspaceTitle, string $pageTitle, int $pageId)
Definition: ReviewController.php:181
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getModuleUri
‪getModuleUri(int $pageUid, int $workspaceId=null)
Definition: ReviewController.php:239
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:60
‪TYPO3\CMS\Workspaces\Controller\ReviewController\addPreviewLink
‪addPreviewLink(ModuleTemplate $view, int $pageUid, int $activeWorkspace)
Definition: ReviewController.php:191
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\PUBLISH_ACCESS_ONLY_IN_PUBLISH_STAGE
‪const PUBLISH_ACCESS_ONLY_IN_PUBLISH_STAGE
Definition: WorkspaceService.php:42
‪TYPO3\CMS\Workspaces\Controller\ReviewController\addWorkspaceSelector
‪addWorkspaceSelector(ModuleTemplate $view, array $availableWorkspaces, int $activeWorkspace, int $pageUid)
Definition: ReviewController.php:147
‪TYPO3\CMS\Workspaces\Service\WorkspaceService
Definition: WorkspaceService.php:35
‪TYPO3\CMS\Workspaces\Service\StagesService
Definition: StagesService.php:30
‪TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider
Definition: TranslationConfigurationProvider.php:39
‪TYPO3\CMS\Backend\Template\ModuleTemplate\getDocHeaderComponent
‪getDocHeaderComponent()
Definition: ModuleTemplate.php:181
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Workspaces\Controller
Definition: AjaxController.php:18
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Workspaces\Controller\ReviewController\addEditWorkspaceRecordButton
‪addEditWorkspaceRecordButton(ModuleTemplate $view, int $pageUid, int $activeWorkspace)
Definition: ReviewController.php:213
‪TYPO3\CMS\Workspaces\Controller\ReviewController\__construct
‪__construct(protected readonly WorkspaceService $workspaceService, protected readonly StagesService $stagesService, protected readonly IconFactory $iconFactory, protected readonly PageRenderer $pageRenderer, protected readonly UriBuilder $uriBuilder, protected readonly ModuleTemplateFactory $moduleTemplateFactory, protected readonly WorkspacePublishGate $workspacePublishGate, protected readonly TranslationConfigurationProvider $translationConfigurationProvider)
Definition: ReviewController.php:46
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getStageActions
‪getStageActions()
Definition: ReviewController.php:284