Passed
Push — master ( ff1a76...9dbd27 )
by
unknown
13:17
created

ViewModuleController::getTypeParameterIfSet()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
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
18
namespace TYPO3\CMS\Viewpage\Controller;
19
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use TYPO3\CMS\Backend\Routing\UriBuilder;
23
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
24
use TYPO3\CMS\Backend\Template\ModuleTemplate;
25
use TYPO3\CMS\Backend\Utility\BackendUtility;
26
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
27
use TYPO3\CMS\Core\Context\LanguageAspectFactory;
28
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
29
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
30
use TYPO3\CMS\Core\Http\HtmlResponse;
31
use TYPO3\CMS\Core\Imaging\Icon;
32
use TYPO3\CMS\Core\Imaging\IconFactory;
33
use TYPO3\CMS\Core\Localization\LanguageService;
34
use TYPO3\CMS\Core\Messaging\FlashMessage;
35
use TYPO3\CMS\Core\Messaging\FlashMessageService;
36
use TYPO3\CMS\Core\Page\PageRenderer;
37
use TYPO3\CMS\Core\Routing\UnableToLinkToPageException;
38
use TYPO3\CMS\Core\Site\SiteFinder;
39
use TYPO3\CMS\Core\Utility\GeneralUtility;
40
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
41
use TYPO3\CMS\Fluid\View\StandaloneView;
42
43
/**
44
 * Controller for viewing the frontend
45
 * @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
46
 */
47
class ViewModuleController
48
{
49
    /**
50
     * ModuleTemplate object
51
     *
52
     * @var ModuleTemplate
53
     */
54
    protected $moduleTemplate;
55
56
    /**
57
     * View
58
     *
59
     * @var ViewInterface
60
     */
61
    protected $view;
62
63
    /**
64
     * Initialize module template and language service
65
     */
66
    public function __construct()
67
    {
68
        $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
69
        $this->getLanguageService()->includeLLFile('EXT:viewpage/Resources/Private/Language/locallang.xlf');
70
        $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
71
        $pageRenderer->addInlineLanguageLabelFile('EXT:viewpage/Resources/Private/Language/locallang.xlf');
72
    }
73
74
    /**
75
     * Initialize view
76
     *
77
     * @param string $templateName
78
     */
79
    protected function initializeView(string $templateName)
80
    {
81
        $this->view = GeneralUtility::makeInstance(StandaloneView::class);
82
        $this->view->getRequest()->setControllerExtensionName('Viewpage');
83
        $this->view->setTemplate($templateName);
84
        $this->view->setTemplateRootPaths(['EXT:viewpage/Resources/Private/Templates/ViewModule']);
85
        $this->view->setPartialRootPaths(['EXT:viewpage/Resources/Private/Partials']);
86
        $this->view->setLayoutRootPaths(['EXT:viewpage/Resources/Private/Layouts']);
87
    }
88
89
    /**
90
     * Register the doc header
91
92
     * @param int $pageId
93
     * @param int $languageId
94
     * @param string $targetUrl
95
     * @param string $route
96
     */
97
    protected function registerDocHeader(int $pageId, int $languageId, string $targetUrl, string $route)
98
    {
99
        $languages = $this->getPreviewLanguages($pageId);
100
        if (count($languages) > 1) {
101
            $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
102
            $languageMenu->setIdentifier('_langSelector');
103
            $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
104
            foreach ($languages as $value => $label) {
105
                $href = (string)$uriBuilder->buildUriFromRoute(
106
                    'web_ViewpageView',
107
                    [
108
                        'id' => $pageId,
109
                        'language' => (int)$value
110
                    ]
111
                );
112
                $menuItem = $languageMenu->makeMenuItem()
113
                    ->setTitle($label)
114
                    ->setHref($href);
115
                if ($languageId === (int)$value) {
116
                    $menuItem->setActive(true);
117
                }
118
                $languageMenu->addMenuItem($menuItem);
119
            }
120
            $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
121
        }
122
123
        $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
124
        $showButton = $buttonBar->makeLinkButton()
125
            ->setHref($targetUrl)
126
            ->setOnClick('window.open(this.href, \'newTYPO3frontendWindow\').focus();return false;')
127
            ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
128
            ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-page', Icon::SIZE_SMALL));
129
        $buttonBar->addButton($showButton);
130
131
        $refreshButton = $buttonBar->makeLinkButton()
132
            ->setHref('javascript:document.getElementById(\'tx_viewpage_iframe\').contentWindow.location.reload(true);')
133
            ->setTitle($this->getLanguageService()->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang.xlf:refreshPage'))
134
            ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-refresh', Icon::SIZE_SMALL));
135
        $buttonBar->addButton($refreshButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
136
137
        // Shortcut
138
        $shortcutButton = $buttonBar->makeShortcutButton()
139
            ->setModuleName('web_ViewpageView')
140
            ->setDisplayName($this->getShortcutTitle($pageId))
141
            ->setArguments([
142
                'route' => $route,
143
                'id' => $pageId,
144
            ]);
145
        $buttonBar->addButton($shortcutButton, ButtonBar::BUTTON_POSITION_RIGHT);
146
    }
147
148
    /**
149
     * Show selected page from pagetree in iframe
150
     *
151
     * @param ServerRequestInterface $request
152
     * @return ResponseInterface
153
     * @throws \TYPO3\CMS\Core\Exception
154
     */
155
    public function showAction(ServerRequestInterface $request): ResponseInterface
156
    {
157
        $pageId = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0);
158
159
        $this->initializeView('show');
160
        $this->moduleTemplate->setBodyTag('<body class="typo3-module-viewpage">');
161
        $this->moduleTemplate->setModuleName('typo3-module-viewpage');
162
        $this->moduleTemplate->setModuleId('typo3-module-viewpage');
163
164
        if (!$this->isValidDoktype($pageId)) {
165
            $flashMessage = GeneralUtility::makeInstance(
166
                FlashMessage::class,
167
                $this->getLanguageService()->getLL('noValidPageSelected'),
168
                '',
169
                FlashMessage::INFO
170
            );
171
            return $this->renderFlashMessage($flashMessage);
172
        }
173
174
        $languageId = $this->getCurrentLanguage($pageId, $request->getParsedBody()['language'] ?? $request->getQueryParams()['language'] ?? null);
175
        try {
176
            $targetUrl = BackendUtility::getPreviewUrl(
177
                $pageId,
178
                '',
179
                null,
180
                '',
181
                '',
182
                $this->getTypeParameterIfSet($pageId) . '&L=' . $languageId
183
            );
184
        } catch (UnableToLinkToPageException $e) {
185
            $flashMessage = GeneralUtility::makeInstance(
186
                FlashMessage::class,
187
                $this->getLanguageService()->getLL('noSiteConfiguration'),
188
                '',
189
                FlashMessage::WARNING
190
            );
191
            return $this->renderFlashMessage($flashMessage);
192
        }
193
194
        $this->registerDocHeader($pageId, $languageId, $targetUrl, $request->getQueryParams()['route']);
195
196
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
197
        $icons = [];
198
        $icons['orientation'] = $iconFactory->getIcon('actions-device-orientation-change', Icon::SIZE_SMALL)->render('inline');
199
        $icons['fullscreen'] = $iconFactory->getIcon('actions-fullscreen', Icon::SIZE_SMALL)->render('inline');
200
        $icons['expand'] = $iconFactory->getIcon('actions-expand', Icon::SIZE_SMALL)->render('inline');
201
        $icons['desktop'] = $iconFactory->getIcon('actions-device-desktop', Icon::SIZE_SMALL)->render('inline');
202
        $icons['tablet'] = $iconFactory->getIcon('actions-device-tablet', Icon::SIZE_SMALL)->render('inline');
203
        $icons['mobile'] = $iconFactory->getIcon('actions-device-mobile', Icon::SIZE_SMALL)->render('inline');
204
        $icons['unidentified'] = $iconFactory->getIcon('actions-device-unidentified', Icon::SIZE_SMALL)->render('inline');
205
206
        $current = ($this->getBackendUser()->uc['moduleData']['web_view']['States']['current'] ?: []);
207
        $current['label'] = ($current['label'] ?? $this->getLanguageService()->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang.xlf:custom'));
208
        $current['width'] = (isset($current['width']) && (int)$current['width'] >= 300 ? (int)$current['width'] : 320);
209
        $current['height'] = (isset($current['height']) && (int)$current['height'] >= 300 ? (int)$current['height'] : 480);
210
211
        $custom = ($this->getBackendUser()->uc['moduleData']['web_view']['States']['custom'] ?: []);
212
        $custom['width'] = (isset($current['custom']) && (int)$current['custom'] >= 300 ? (int)$current['custom'] : 320);
213
        $custom['height'] = (isset($current['custom']) && (int)$current['custom'] >= 300 ? (int)$current['custom'] : 480);
214
215
        $this->view->assign('icons', $icons);
216
        $this->view->assign('current', $current);
217
        $this->view->assign('custom', $custom);
218
        $this->view->assign('presetGroups', $this->getPreviewPresets($pageId));
219
        $this->view->assign('url', $targetUrl);
220
221
        $this->moduleTemplate->setContent($this->view->render());
222
        return new HtmlResponse($this->moduleTemplate->renderContent());
223
    }
224
225
    protected function renderFlashMessage(FlashMessage $flashMessage): HtmlResponse
226
    {
227
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
228
        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
229
        $defaultFlashMessageQueue->enqueue($flashMessage);
230
231
        $this->moduleTemplate->setContent($this->view->render());
232
        return new HtmlResponse($this->moduleTemplate->renderContent());
233
    }
234
235
    /**
236
     * With page TS config it is possible to force a specific type id via mod.web_view.type
237
     * for a page id or a page tree.
238
     * The method checks if a type is set for the given id and returns the additional GET string.
239
     *
240
     * @param int $pageId
241
     * @return string
242
     */
243
    protected function getTypeParameterIfSet(int $pageId): string
244
    {
245
        $typeParameter = '';
246
        $typeId = (int)(BackendUtility::getPagesTSconfig($pageId)['mod.']['web_view.']['type'] ?? 0);
247
        if ($typeId > 0) {
248
            $typeParameter = '&type=' . $typeId;
249
        }
250
        return $typeParameter;
251
    }
252
253
    /**
254
     * Get available presets for page id
255
     *
256
     * @param int $pageId
257
     * @return array
258
     */
259
    protected function getPreviewPresets(int $pageId): array
260
    {
261
        $presetGroups = [
262
            'desktop' => [],
263
            'tablet' => [],
264
            'mobile' => [],
265
            'unidentified' => []
266
        ];
267
        $previewFrameWidthConfig = BackendUtility::getPagesTSconfig($pageId)['mod.']['web_view.']['previewFrameWidths.'] ?? [];
268
        foreach ($previewFrameWidthConfig as $item => $conf) {
269
            $data = [
270
                'key' => substr($item, 0, -1),
271
                'label' => $conf['label'] ?? null,
272
                'type' => $conf['type'] ?? 'unknown',
273
                'width' => (isset($conf['width']) && (int)$conf['width'] > 0 && strpos($conf['width'], '%') === false) ? (int)$conf['width'] : null,
274
                'height' => (isset($conf['height']) && (int)$conf['height'] > 0 && strpos($conf['height'], '%') === false) ? (int)$conf['height'] : null,
275
            ];
276
            $width = (int)substr($item, 0, -1);
277
            if (!isset($data['width']) && $width > 0) {
278
                $data['width'] = $width;
279
            }
280
            if (!isset($data['label'])) {
281
                $data['label'] = $data['key'];
282
            } elseif (strpos($data['label'], 'LLL:') === 0) {
283
                $data['label'] = $this->getLanguageService()->sL(trim($data['label']));
284
            }
285
286
            if (array_key_exists($data['type'], $presetGroups)) {
287
                $presetGroups[$data['type']][$data['key']] = $data;
288
            } else {
289
                $presetGroups['unidentified'][$data['key']] = $data;
290
            }
291
        }
292
293
        return $presetGroups;
294
    }
295
296
    /**
297
     * Returns the preview languages
298
     *
299
     * @param int $pageId
300
     * @return array
301
     */
302
    protected function getPreviewLanguages(int $pageId): array
303
    {
304
        $languages = [];
305
        $modSharedTSconfig = BackendUtility::getPagesTSconfig($pageId)['mod.']['SHARED.'] ?? [];
306
        if ($modSharedTSconfig['view.']['disableLanguageSelector'] === '1') {
307
            return $languages;
308
        }
309
310
        try {
311
            $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
312
            $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($pageId);
313
            $siteLanguages = $site->getAvailableLanguages($this->getBackendUser(), false, $pageId);
314
315
            foreach ($siteLanguages as $siteLanguage) {
316
                $languageAspectToTest = LanguageAspectFactory::createFromSiteLanguage($siteLanguage);
317
                $page = $pageRepository->getPageOverlay($pageRepository->getPage($pageId), $siteLanguage->getLanguageId());
318
319
                if ($pageRepository->isPageSuitableForLanguage($page, $languageAspectToTest)) {
320
                    $languages[$siteLanguage->getLanguageId()] = $siteLanguage->getTitle();
321
                }
322
            }
323
        } catch (SiteNotFoundException $e) {
324
            // do nothing
325
        }
326
        return $languages;
327
    }
328
329
    /**
330
     * Returns the current language
331
     *
332
     * @param int $pageId
333
     * @param string $languageParam
334
     * @return int
335
     */
336
    protected function getCurrentLanguage(int $pageId, string $languageParam = null): int
337
    {
338
        $languageId = (int)$languageParam;
339
        if ($languageParam === null) {
340
            $states = $this->getBackendUser()->uc['moduleData']['web_view']['States'];
341
            $languages = $this->getPreviewLanguages($pageId);
342
            if (isset($states['languageSelectorValue']) && isset($languages[$states['languageSelectorValue']])) {
343
                $languageId = (int)$states['languageSelectorValue'];
344
            }
345
        } else {
346
            $this->getBackendUser()->uc['moduleData']['web_view']['States']['languageSelectorValue'] = $languageId;
347
            $this->getBackendUser()->writeUC($this->getBackendUser()->uc);
348
        }
349
        return $languageId;
350
    }
351
352
    /**
353
     * Verifies if doktype of given page is valid
354
     *
355
     * @param int $pageId
356
     * @return bool
357
     */
358
    protected function isValidDoktype(int $pageId = 0): bool
359
    {
360
        if ($pageId === 0) {
361
            return false;
362
        }
363
364
        $page = BackendUtility::getRecord('pages', $pageId);
365
        $pageType = (int)($page['doktype'] ?? 0);
366
367
        return $pageType !== 0
368
            && !in_array($pageType, [
369
                PageRepository::DOKTYPE_SPACER,
370
                PageRepository::DOKTYPE_SYSFOLDER,
371
                PageRepository::DOKTYPE_RECYCLER
372
            ], true);
373
    }
374
375
    /**
376
     * Returns the shortcut title for the current page
377
     *
378
     * @param int $pageId
379
     * @return string
380
     */
381
    protected function getShortcutTitle(int $pageId): string
382
    {
383
        $pageTitle = '';
384
        $pageRow = BackendUtility::getRecord('pages', $pageId);
385
        if ($pageRow !== []) {
386
            $pageTitle = BackendUtility::getRecordTitle('pages', $pageRow);
387
        }
388
        return sprintf(
389
            '%s: %s [%d]',
390
            $this->getLanguageService()->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tablabel'),
391
            $pageTitle,
392
            $pageId
393
        );
394
    }
395
396
    /**
397
     * @return BackendUserAuthentication
398
     */
399
    protected function getBackendUser(): BackendUserAuthentication
400
    {
401
        return $GLOBALS['BE_USER'];
402
    }
403
404
    /**
405
     * @return LanguageService
406
     */
407
    protected function getLanguageService(): LanguageService
408
    {
409
        return $GLOBALS['LANG'];
410
    }
411
}
412