‪TYPO3CMS  ‪main
ViewModuleController.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;
34 use TYPO3\CMS\Core\Imaging\IconSize;
47 
53 #[AsController]
55 {
56  public function ‪__construct(
57  protected readonly ‪ModuleTemplateFactory $moduleTemplateFactory,
58  protected readonly ‪IconFactory $iconFactory,
59  protected readonly ‪UriBuilder $uriBuilder,
60  protected readonly ‪PageRepository $pageRepository,
61  protected readonly ‪SiteFinder $siteFinder,
62  protected readonly ‪PolicyRegistry $policyRegistry,
63  ) {}
64 
68  public function ‪handleRequest(ServerRequestInterface $request): ResponseInterface
69  {
70  $languageService = $this->‪getLanguageService();
71  $pageId = (int)($request->getQueryParams()['id'] ?? 0);
72  $moduleData = $request->getAttribute('moduleData');
73  $pageInfo = BackendUtility::readPageAccess($pageId, $this->‪getBackendUser()->getPagePermsClause(‪Permission::PAGE_SHOW));
74 
75  $view = $this->moduleTemplateFactory->create($request);
76  $view->setBodyTag('<body class="typo3-module-viewpage">');
77  $view->setModuleId('typo3-module-viewpage');
78  $view->setTitle(
79  $languageService->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
80  $pageInfo['title'] ?? ''
81  );
82 
83  if (!$this->‪isValidDoktype($pageId)) {
84  $view->addFlashMessage(
85  $languageService->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang.xlf:noValidPageSelected'),
86  '',
87  ContextualFeedbackSeverity::INFO
88  );
89  return $view->renderResponse('Empty');
90  }
91 
92  $previewLanguages = $this->‪getPreviewLanguages($pageId);
93  if ($previewLanguages !== [] && $moduleData->clean('language', array_keys($previewLanguages))) {
94  $this->‪getBackendUser()->pushModuleData($moduleData->getModuleIdentifier(), $moduleData->toArray());
95  }
96  $languageId = (int)$moduleData->get('language');
97  $targetUri = ‪PreviewUriBuilder::create($pageId)
98  ->withAdditionalQueryParameters($this->‪getTypeParameterIfSet($pageId))
99  ->withLanguage($languageId)
100  ->buildUri();
101  ‪$targetUrl = (string)$targetUri;
102  if ($targetUri === null || ‪$targetUrl === '') {
103  $view->addFlashMessage(
104  $languageService->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang.xlf:noSiteConfiguration'),
105  '',
106  ContextualFeedbackSeverity::WARNING
107  );
108  return $view->renderResponse('Empty');
109  }
110 
111  $this->‪registerDocHeader($view, $pageId, $languageId, ‪$targetUrl);
112  $current = $moduleData->get('States')['current'] ?? [];
113  $current['label'] = ($current['label'] ?? $languageService->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang.xlf:custom'));
114  $current['width'] = ‪MathUtility::forceIntegerInRange($current['width'] ?? 320, 300);
115  $current['height'] = ‪MathUtility::forceIntegerInRange($current['height'] ?? 480, 300);
116 
117  $custom = $moduleData->get('States')['custom'] ?? [];
118  $custom['width'] = ‪MathUtility::forceIntegerInRange($custom['width'] ?? 320, 300);
119  $custom['height'] = ‪MathUtility::forceIntegerInRange($custom['height'] ?? 480, 300);
120 
121  $view->assignMultiple([
122  'current' => $current,
123  'custom' => $custom,
124  'presetGroups' => $this->‪getPreviewPresets($pageId),
125  'url' => ‪$targetUrl,
126  ]);
127 
128  if ($targetUri->getScheme() !== '' && $targetUri->getHost() !== '') {
129  // temporarily(!) extend the CSP `frame-src` directive with the URL to be shown in the `<iframe>`
130  $mutation = new ‪Mutation(MutationMode::Extend, Directive::FrameSrc, ‪UriValue::fromUri($targetUri));
131  $this->policyRegistry->appendMutationCollection(new ‪MutationCollection($mutation));
132  }
133  return $view->renderResponse('Show');
134  }
135 
136  protected function ‪registerDocHeader(‪ModuleTemplate $view, int $pageId, int $languageId, string ‪$targetUrl)
137  {
138  $languageService = $this->‪getLanguageService();
139  ‪$languages = $this->‪getPreviewLanguages($pageId);
140  if (count(‪$languages) > 1) {
141  $languageMenu = $view->‪getDocHeaderComponent()->getMenuRegistry()->makeMenu();
142  $languageMenu->setIdentifier('_langSelector');
143  $languageMenu->setLabel(
144  $languageService->sL(
145  'LLL:EXT:viewpage/Resources/Private/Language/locallang.xlf:moduleMenu.dropdown.label'
146  )
147  );
148  foreach (‪$languages as $value => $label) {
149  $href = (string)$this->uriBuilder->buildUriFromRoute(
150  'page_preview',
151  [
152  'id' => $pageId,
153  'language' => (int)$value,
154  ]
155  );
156  $menuItem = $languageMenu->makeMenuItem()
157  ->setTitle($label)
158  ->setHref($href);
159  if ($languageId === (int)$value) {
160  $menuItem->setActive(true);
161  }
162  $languageMenu->addMenuItem($menuItem);
163  }
164  $view->‪getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
165  }
166 
167  $buttonBar = $view->‪getDocHeaderComponent()->getButtonBar();
168  $showButton = $buttonBar->makeLinkButton()
169  ->setHref(‪$targetUrl)
170  ->setDataAttributes([
171  'dispatch-action' => 'TYPO3.WindowManager.localOpen',
172  'dispatch-args' => GeneralUtility::jsonEncodeForHtmlAttribute([
174  true, // switchFocus
175  'newTYPO3frontendWindow', // windowName,
176  ]),
177  ])
178  ->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
179  ->setShowLabelText(true)
180  ->setIcon($this->iconFactory->getIcon('actions-view-page', IconSize::SMALL));
181  $buttonBar->addButton($showButton);
182 
183  $refreshButton = $buttonBar->makeLinkButton()
184  ->setHref('#')
185  ->setClasses('t3js-viewpage-refresh')
186  ->setTitle($languageService->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang.xlf:refreshPage'))
187  ->setIcon($this->iconFactory->getIcon('actions-refresh', IconSize::SMALL));
188  $buttonBar->addButton($refreshButton, ‪ButtonBar::BUTTON_POSITION_RIGHT);
189 
190  // Shortcut
191  $shortcutButton = $buttonBar->makeShortcutButton()
192  ->setRouteIdentifier('page_preview')
193  ->setDisplayName($this->‪getShortcutTitle($pageId))
194  ->setArguments(['id' => $pageId]);
195  $buttonBar->addButton($shortcutButton, ‪ButtonBar::BUTTON_POSITION_RIGHT);
196  }
197 
202  protected function ‪getTypeParameterIfSet(int $pageId): string
203  {
204  $typeParameter = '';
205  $typeId = (int)(BackendUtility::getPagesTSconfig($pageId)['mod.']['web_view.']['type'] ?? 0);
206  if ($typeId > 0) {
207  $typeParameter = '&type=' . $typeId;
208  }
209  return $typeParameter;
210  }
211 
215  protected function ‪getPreviewPresets(int $pageId): array
216  {
217  $presetGroups = [
218  'desktop' => [],
219  'tablet' => [],
220  'mobile' => [],
221  'unidentified' => [],
222  ];
223  $previewFrameWidthConfig = BackendUtility::getPagesTSconfig($pageId)['mod.']['web_view.']['previewFrameWidths.'] ?? [];
224  foreach ($previewFrameWidthConfig as $item => $conf) {
225  $data = [
226  'key' => substr($item, 0, -1),
227  'label' => $conf['label'] ?? null,
228  'type' => $conf['type'] ?? 'unknown',
229  'width' => (isset($conf['width']) && (int)$conf['width'] > 0 && !str_contains($conf['width'], '%')) ? (int)$conf['width'] : null,
230  'height' => (isset($conf['height']) && (int)$conf['height'] > 0 && !str_contains($conf['height'], '%')) ? (int)$conf['height'] : null,
231  ];
232  $width = (int)substr($item, 0, -1);
233  if (!isset($data['width']) && $width > 0) {
234  $data['width'] = $width;
235  }
236  if (!isset($data['label'])) {
237  $data['label'] = $data['key'];
238  } else {
239  $data['label'] = $this->‪getLanguageService()->sL(trim($data['label']));
240  }
241 
242  if (array_key_exists($data['type'], $presetGroups)) {
243  $presetGroups[$data['type']][$data['key']] = $data;
244  } else {
245  $presetGroups['unidentified'][$data['key']] = $data;
246  }
247  }
248 
249  return $presetGroups;
250  }
251 
255  protected function ‪getPreviewLanguages(int $pageId): array
256  {
257  ‪$languages = [];
258  $modSharedTSconfig = BackendUtility::getPagesTSconfig($pageId)['mod.']['SHARED.'] ?? [];
259  if (($modSharedTSconfig['view.']['disableLanguageSelector'] ?? false) === '1') {
260  return ‪$languages;
261  }
262 
263  try {
264  $site = $this->siteFinder->getSiteByPageId($pageId);
265  $siteLanguages = $site->getAvailableLanguages($this->‪getBackendUser(), false, $pageId);
266 
267  foreach ($siteLanguages as $siteLanguage) {
268  $languageAspectToTest = ‪LanguageAspectFactory::createFromSiteLanguage($siteLanguage);
269  $page = $this->pageRepository->getPageOverlay($this->pageRepository->getPage($pageId), $siteLanguage->getLanguageId());
270 
271  if ($this->pageRepository->isPageSuitableForLanguage($page, $languageAspectToTest)) {
272  ‪$languages[$siteLanguage->getLanguageId()] = $siteLanguage->getTitle();
273  }
274  }
275  } catch (‪SiteNotFoundException $e) {
276  // do nothing
277  }
278  return ‪$languages;
279  }
280 
284  protected function ‪isValidDoktype(int $pageId = 0): bool
285  {
286  if ($pageId === 0) {
287  return false;
288  }
289  $page = BackendUtility::getRecord('pages', $pageId);
290  $pageType = (int)($page['doktype'] ?? 0);
291  return $pageType !== 0
292  && !in_array($pageType, [
295  ], true);
296  }
297 
301  protected function ‪getShortcutTitle(int $pageId): string
302  {
303  $pageTitle = '';
304  $pageRow = BackendUtility::getRecord('pages', $pageId) ?? [];
305  if ($pageRow !== []) {
306  $pageTitle = BackendUtility::getRecordTitle('pages', $pageRow);
307  }
308  return sprintf(
309  '%s: %s [%d]',
310  $this->‪getLanguageService()->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tablabel'),
311  $pageTitle,
312  $pageId
313  );
314  }
315 
317  {
318  return ‪$GLOBALS['BE_USER'];
319  }
320 
322  {
323  return ‪$GLOBALS['LANG'];
324  }
325 }
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Directive
‪Directive
Definition: Directive.php:25
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\PolicyRegistry
Definition: PolicyRegistry.php:28
‪TYPO3\CMS\Backend\Template\Components\ButtonBar
Definition: ButtonBar.php:33
‪TYPO3\CMS\Core\Context\LanguageAspectFactory
Definition: LanguageAspectFactory.php:27
‪TYPO3\CMS\Viewpage\Controller
Definition: ViewModuleController.php:18
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:33
‪$languages
‪$languages
Definition: updateIsoDatabase.php:104
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\registerDocHeader
‪registerDocHeader(ModuleTemplate $view, int $pageId, int $languageId, string $targetUrl)
Definition: ViewModuleController.php:136
‪TYPO3\CMS\Core\Context\LanguageAspectFactory\createFromSiteLanguage
‪static createFromSiteLanguage(SiteLanguage $language)
Definition: LanguageAspectFactory.php:31
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:25
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Redirects\Message\$targetUrl
‪identifier readonly UriInterface $targetUrl
Definition: RedirectWasHitMessage.php:33
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\handleRequest
‪handleRequest(ServerRequestInterface $request)
Definition: ViewModuleController.php:68
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\getPreviewPresets
‪getPreviewPresets(int $pageId)
Definition: ViewModuleController.php:215
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\isValidDoktype
‪isValidDoktype(int $pageId=0)
Definition: ViewModuleController.php:284
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\MutationMode
‪MutationMode
Definition: MutationMode.php:24
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\getShortcutTitle
‪getShortcutTitle(int $pageId)
Definition: ViewModuleController.php:301
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder\create
‪static static create(int $pageId)
Definition: PreviewUriBuilder.php:65
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SPACER
‪const DOKTYPE_SPACER
Definition: PageRepository.php:103
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SYSFOLDER
‪const DOKTYPE_SYSFOLDER
Definition: PageRepository.php:104
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\getLanguageService
‪getLanguageService()
Definition: ViewModuleController.php:321
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\getPreviewLanguages
‪getPreviewLanguages(int $pageId)
Definition: ViewModuleController.php:255
‪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\Viewpage\Controller\ViewModuleController\getBackendUser
‪getBackendUser()
Definition: ViewModuleController.php:316
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder
Definition: PreviewUriBuilder.php:44
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Backend\Attribute\AsController
Definition: AsController.php:25
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\UriValue
Definition: UriValue.php:29
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Mutation
Definition: Mutation.php:26
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\MutationCollection
Definition: MutationCollection.php:24
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_RIGHT
‪const BUTTON_POSITION_RIGHT
Definition: ButtonBar.php:42
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController
Definition: ViewModuleController.php:55
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\getTypeParameterIfSet
‪getTypeParameterIfSet(int $pageId)
Definition: ViewModuleController.php:202
‪TYPO3\CMS\Viewpage\Controller\ViewModuleController\__construct
‪__construct(protected readonly ModuleTemplateFactory $moduleTemplateFactory, protected readonly IconFactory $iconFactory, protected readonly UriBuilder $uriBuilder, protected readonly PageRepository $pageRepository, protected readonly SiteFinder $siteFinder, protected readonly PolicyRegistry $policyRegistry,)
Definition: ViewModuleController.php:56
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\UriValue\fromUri
‪static fromUri(UriInterface $other)
Definition: UriValue.php:34