‪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 #[AsController]
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:core/Resources/Private/Language/locallang_core.xlf');
83  $this->pageRenderer->addInlineLanguageLabelFile('EXT:workspaces/Resources/Private/Language/locallang.xlf');
84 
85  $backendUser = $this->‪getBackendUser();
86  $pageTitle = '';
87  $pageRecord = [];
88  if ($pageUid) {
89  $pageRecord = BackendUtility::getRecord('pages', $pageUid);
90  if ($pageRecord) {
91  $pageTitle = BackendUtility::getRecordTitle('pages', $pageRecord);
92  }
93  } else {
94  $pageTitle = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?? '';
95  }
96  $availableWorkspaces = $this->workspaceService->getAvailableWorkspaces();
97  $customWorkspaceExists = $this->‪customWorkspaceExists($availableWorkspaces);
98  $activeWorkspace = $backendUser->workspace;
99  $activeWorkspaceTitle = $this->workspaceService->getWorkspaceTitle($activeWorkspace);
100  $workspaceSwitched = '';
101  if (isset($queryParams['workspace'])) {
102  $switchWs = (int)$queryParams['workspace'];
103  if (array_key_exists($switchWs, $availableWorkspaces) && $activeWorkspace !== $switchWs) {
104  $activeWorkspace = $switchWs;
105  $backendUser->setWorkspace($activeWorkspace);
106  $activeWorkspaceTitle = $this->workspaceService->getWorkspaceTitle($activeWorkspace);
107  $workspaceSwitched = GeneralUtility::jsonEncodeForHtmlAttribute(['id' => $activeWorkspace, 'title' => $activeWorkspaceTitle]);
108  }
109  }
110  $workspaceIsAccessible = $backendUser->workspace !== ‪WorkspaceService::LIVE_WORKSPACE_ID && $pageUid > 0;
111 
112  $selectedLanguage = (string)$moduleData->get('language');
113  $view = $this->moduleTemplateFactory->create($request);
114  $view->assignMultiple([
115  'isAdmin' => $backendUser->isAdmin(),
116  'customWorkspaceExists' => $customWorkspaceExists,
117  'showGrid' => $workspaceIsAccessible,
118  'showLegend' => $workspaceIsAccessible,
119  'pageUid' => $pageUid,
120  'pageTitle' => $pageTitle,
121  'activeWorkspaceUid' => $activeWorkspace,
122  'activeWorkspaceTitle' => $activeWorkspaceTitle,
123  'availableLanguages' => $this->‪getSystemLanguages($pageUid, $selectedLanguage),
124  'availableStages' => $this->stagesService->getStagesForWSUser(),
125  'availableSelectStages' => $this->‪getAvailableSelectStages(),
126  'stageActions' => $this->‪getStageActions(),
127  'showEntireWorkspaceDropDown' => !(($backendUser->workspaceRec['publish_access'] ?? 0) & ‪WorkspaceService::PUBLISH_ACCESS_HIDE_ENTIRE_WORKSPACE_ACTION_DROPDOWN),
128  'selectedLanguage' => $selectedLanguage,
129  'selectedDepth' => (int)$moduleData->get('depth', ($pageUid === 0 ? 999 : 1)),
130  'selectedStage' => (int)$moduleData->get('stage'),
131  'workspaceSwitched' => $workspaceSwitched,
132  ]);
133  $view->setTitle(
134  $this->‪getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab') . ' [' . $activeWorkspaceTitle . ']',
135  $pageTitle
136  );
137  $view->getDocHeaderComponent()->setMetaInformation($pageRecord);
138  $this->‪addWorkspaceSelector($view, $availableWorkspaces, $activeWorkspace, $pageUid);
139  $this->‪addPreviewLink($view, $pageUid, $activeWorkspace);
140  $this->‪addEditWorkspaceRecordButton($view, $pageUid, $activeWorkspace);
141  $this->‪addShortcutButton($view, $activeWorkspaceTitle, $pageTitle, $pageUid);
142  return $view->renderResponse('Review/Index');
143  }
144 
148  protected function ‪addWorkspaceSelector(‪ModuleTemplate $view, array $availableWorkspaces, int $activeWorkspace, int $pageUid): void
149  {
150  $items = [];
151  $items[] = [
152  'title' => $availableWorkspaces[$activeWorkspace],
153  'active' => true,
154  'url' => $this->‪getModuleUri($pageUid),
155  ];
156  foreach ($availableWorkspaces as $workspaceId => $workspaceTitle) {
157  if ($workspaceId === $activeWorkspace) {
158  continue;
159  }
160  $items[] = [
161  'title' => $workspaceTitle,
162  'active' => false,
163  'url' => $this->‪getModuleUri($pageUid, (int)$workspaceId),
164  ];
165  }
166  $actionMenu = $view->‪getDocHeaderComponent()->getMenuRegistry()->makeMenu();
167  $actionMenu->setIdentifier('workspaceSelector');
168  $actionMenu->setLabel(
169  $this->‪getLanguageService()->sL(
170  'LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:moduleMenu.dropdown.label'
171  )
172  );
173  foreach ($items as $workspaceData) {
174  $menuItem = $actionMenu
175  ->makeMenuItem()
176  ->setTitle($workspaceData['title'])
177  ->setHref($workspaceData['url']);
178  if ($workspaceData['active']) {
179  $menuItem->setActive(true);
180  }
181  $actionMenu->addMenuItem($menuItem);
182  }
183  $view->‪getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
184  }
185 
186  protected function ‪addShortcutButton(‪ModuleTemplate $view, string $activeWorkspaceTitle, string $pageTitle, int $pageId): void
187  {
188  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
189  $shortcutButton = $buttonBar->makeShortcutButton()
190  ->setRouteIdentifier('workspaces_admin')
191  ->setDisplayName(sprintf('%s: %s [%d]', $activeWorkspaceTitle, $pageTitle, $pageId))
192  ->setArguments(['id' => (int)$pageId]);
193  $buttonBar->addButton($shortcutButton);
194  }
195 
196  protected function ‪addPreviewLink(‪ModuleTemplate $view, int $pageUid, int $activeWorkspace): void
197  {
198  $canCreatePreviewLink = false;
199  if ($pageUid > 0 && $activeWorkspace > 0) {
200  $pageRecord = BackendUtility::getRecord('pages', $pageUid);
201  BackendUtility::workspaceOL('pages', $pageRecord, $activeWorkspace);
202  if (VersionState::tryFrom($pageRecord['t3ver_state'] ?? 0) !== VersionState::DELETE_PLACEHOLDER->value) {
203  $canCreatePreviewLink = true;
204  }
205  }
206  if ($canCreatePreviewLink) {
207  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
208  $showButton = $buttonBar->makeLinkButton()
209  ->setHref('#')
210  ->setClasses('t3js-preview-link')
211  ->setShowLabelText(true)
212  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:tooltip.generatePagePreview'))
213  ->setIcon($this->iconFactory->getIcon('actions-version-workspaces-preview-link', IconSize::SMALL));
214  $buttonBar->addButton($showButton);
215  }
216  }
217 
218  protected function ‪addEditWorkspaceRecordButton(‪ModuleTemplate $view, int $pageUid, int $activeWorkspace): void
219  {
220  $backendUser = $this->‪getBackendUser();
221  if ($backendUser->isAdmin() && $activeWorkspace > 0) {
222  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
223  $editWorkspaceRecordUrl = (string)$this->uriBuilder->buildUriFromRoute('record_edit', [
224  'edit' => [
225  'sys_workspace' => [
226  $activeWorkspace => 'edit',
227  ],
228  ],
229  'returnUrl' => (string)$this->uriBuilder->buildUriFromRoute('workspaces_admin', ['id' => $pageUid]),
230  ]);
231  $editSettingsButton = $buttonBar->makeLinkButton()
232  ->setHref($editWorkspaceRecordUrl)
233  ->setShowLabelText(true)
234  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:button.editWorkspaceSettings'))
235  ->setIcon($this->iconFactory->getIcon('actions-cog-alt', IconSize::SMALL));
236  $buttonBar->addButton(
237  $editSettingsButton,
239  90
240  );
241  }
242  }
243 
244  protected function ‪getModuleUri(int $pageUid, int $workspaceId = null): string
245  {
246  $parameters = [
247  'id' => $pageUid,
248  ];
249  if ($workspaceId !== null) {
250  $parameters['workspace'] = $workspaceId;
251  }
252  return (string)$this->uriBuilder->buildUriFromRoute('workspaces_admin', $parameters);
253  }
254 
258  protected function ‪customWorkspaceExists(array $workspaceList): bool
259  {
260  foreach (array_keys($workspaceList) as $workspaceId) {
261  if ($workspaceId > 0) {
262  return true;
263  }
264  }
265  return false;
266  }
267 
271  protected function ‪getSystemLanguages(int $pageId, string $selectedLanguage): array
272  {
273  ‪$languages = $this->translationConfigurationProvider->getSystemLanguages($pageId);
274  if (isset(‪$languages[-1])) {
275  ‪$languages[-1]['uid'] = 'all';
276  }
277  foreach (‪$languages as &$language) {
278  // needs to be strict type checking as this is not possible in fluid
279  if ((string)$language['uid'] === $selectedLanguage) {
280  $language['active'] = true;
281  }
282  }
283  return ‪$languages;
284  }
285 
289  protected function ‪getStageActions(): array
290  {
291  $languageService = $this->‪getLanguageService();
292  $currentWorkspace = $this->workspaceService->getCurrentWorkspace();
293  $backendUser = $this->‪getBackendUser();
294  $actions = [];
295  $massActionsEnabled = (bool)($backendUser->getTSConfig()['options.']['workspaces.']['enableMassActions'] ?? true);
296  if ($massActionsEnabled) {
297  if ($this->workspacePublishGate->isGranted($backendUser, $currentWorkspace)
298  && !(($backendUser->workspaceRec['publish_access'] ?? 0) & ‪WorkspaceService::PUBLISH_ACCESS_ONLY_IN_PUBLISH_STAGE)
299  ) {
300  $actions[] = ['action' => 'publish', 'title' => $languageService->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:label_doaction_publish')];
301  }
302  if ($currentWorkspace !== ‪WorkspaceService::LIVE_WORKSPACE_ID) {
303  $actions[] = ['action' => 'discard', 'title' => $languageService->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:label_doaction_discard')];
304  }
305  }
306  return $actions;
307  }
308 
313  protected function ‪getAvailableSelectStages(): array
314  {
315  $languageService = $this->‪getLanguageService();
316  $stages = $this->stagesService->getStagesForWSUser();
317  return array_merge([
318  [
319  'uid' => -99,
320  'label' => $languageService->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:stage_all'),
321  ],
322  ], array_filter($stages, static fn(array $stage): bool => (int)($stage['uid'] ?? 0) !== ‪StagesService::STAGE_PUBLISH_EXECUTE_ID));
323  }
324 
326  {
327  return ‪$GLOBALS['LANG'];
328  }
329 
331  {
332  return ‪$GLOBALS['BE_USER'];
333  }
334 }
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getSystemLanguages
‪getSystemLanguages(int $pageId, string $selectedLanguage)
Definition: ReviewController.php:271
‪TYPO3\CMS\Workspaces\Controller\ReviewController\handleRequest
‪handleRequest(ServerRequestInterface $request)
Definition: ReviewController.php:61
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getBackendUser
‪getBackendUser()
Definition: ReviewController.php:330
‪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:313
‪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:47
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getLanguageService
‪getLanguageService()
Definition: ReviewController.php:325
‪TYPO3\CMS\Workspaces\Controller\ReviewController\customWorkspaceExists
‪customWorkspaceExists(array $workspaceList)
Definition: ReviewController.php:258
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\LIVE_WORKSPACE_ID
‪const LIVE_WORKSPACE_ID
Definition: WorkspaceService.php:43
‪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:36
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Core\Page\PageRenderer
Definition: PageRenderer.php:44
‪TYPO3\CMS\Workspaces\Controller\ReviewController\addShortcutButton
‪addShortcutButton(ModuleTemplate $view, string $activeWorkspaceTitle, string $pageTitle, int $pageId)
Definition: ReviewController.php:186
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Workspaces\Controller\ReviewController\getModuleUri
‪getModuleUri(int $pageUid, int $workspaceId=null)
Definition: ReviewController.php:244
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Workspaces\Controller\ReviewController\addPreviewLink
‪addPreviewLink(ModuleTemplate $view, int $pageUid, int $activeWorkspace)
Definition: ReviewController.php:196
‪TYPO3\CMS\Workspaces\Service\WorkspaceService\PUBLISH_ACCESS_ONLY_IN_PUBLISH_STAGE
‪const PUBLISH_ACCESS_ONLY_IN_PUBLISH_STAGE
Definition: WorkspaceService.php:45
‪TYPO3\CMS\Workspaces\Controller\ReviewController\addWorkspaceSelector
‪addWorkspaceSelector(ModuleTemplate $view, array $availableWorkspaces, int $activeWorkspace, int $pageUid)
Definition: ReviewController.php:148
‪TYPO3\CMS\Workspaces\Service\WorkspaceService
Definition: WorkspaceService.php:38
‪TYPO3\CMS\Workspaces\Service\StagesService
Definition: StagesService.php:33
‪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\Backend\Attribute\AsController
Definition: AsController.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:52
‪TYPO3\CMS\Workspaces\Controller\ReviewController\addEditWorkspaceRecordButton
‪addEditWorkspaceRecordButton(ModuleTemplate $view, int $pageUid, int $activeWorkspace)
Definition: ReviewController.php:218
‪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:289