Passed
Push — master ( cf2c30...1aaee6 )
by
unknown
18:36
created

ViewModuleController   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 343
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 173
dl 0
loc 343
rs 8.4
c 1
b 0
f 0
wmc 50

12 Methods

Rating   Name   Duplication   Size   Complexity  
A registerDocHeader() 0 50 5
A initializeView() 0 8 1
A __construct() 0 6 1
F getPreviewPresets() 0 35 13
A getPreviewLanguages() 0 25 5
C showAction() 0 68 13
A getCurrentLanguage() 0 14 4
A renderFlashMessage() 0 8 1
A isValidDoktype() 0 15 3
A getLanguageService() 0 3 1
A getTypeParameterIfSet() 0 8 2
A getBackendUser() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like ViewModuleController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ViewModuleController, and based on these observations, apply Extract Interface, too.

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
     * Registers the docheader
91
     *
92
     * @param int $pageId
93
     * @param int $languageId
94
     * @param string $targetUrl
95
     */
96
    protected function registerDocHeader(int $pageId, int $languageId, string $targetUrl)
97
    {
98
        $languages = $this->getPreviewLanguages($pageId);
99
        if (count($languages) > 1) {
100
            $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
101
            $languageMenu->setIdentifier('_langSelector');
102
            /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
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
        $mayMakeShortcut = $this->getBackendUser()->mayMakeShortcut();
139
        if ($mayMakeShortcut) {
140
            $getVars = ['id', 'route'];
141
142
            $shortcutButton = $buttonBar->makeShortcutButton()
143
                ->setModuleName('web_ViewpageView')
144
                ->setGetVariables($getVars);
145
            $buttonBar->addButton($shortcutButton, ButtonBar::BUTTON_POSITION_RIGHT);
146
        }
147
    }
148
149
    /**
150
     * Show selected page from pagetree in iframe
151
     *
152
     * @param ServerRequestInterface $request
153
     * @return ResponseInterface
154
     * @throws \TYPO3\CMS\Core\Exception
155
     */
156
    public function showAction(ServerRequestInterface $request): ResponseInterface
157
    {
158
        $pageId = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0);
159
160
        $this->initializeView('show');
161
        $this->moduleTemplate->setBodyTag('<body class="typo3-module-viewpage">');
162
        $this->moduleTemplate->setModuleName('typo3-module-viewpage');
163
        $this->moduleTemplate->setModuleId('typo3-module-viewpage');
164
165
        if (!$this->isValidDoktype($pageId)) {
166
            $flashMessage = GeneralUtility::makeInstance(
167
                FlashMessage::class,
168
                $this->getLanguageService()->getLL('noValidPageSelected'),
169
                '',
170
                FlashMessage::INFO
171
            );
172
            return $this->renderFlashMessage($flashMessage);
173
        }
174
175
        $languageId = $this->getCurrentLanguage($pageId, $request->getParsedBody()['language'] ?? $request->getQueryParams()['language'] ?? null);
176
        try {
177
            $targetUrl = BackendUtility::getPreviewUrl(
178
                $pageId,
179
                '',
180
                null,
181
                '',
182
                '',
183
                $this->getTypeParameterIfSet($pageId) . '&L=' . $languageId
184
            );
185
        } catch (UnableToLinkToPageException $e) {
186
            $flashMessage = GeneralUtility::makeInstance(
187
                FlashMessage::class,
188
                $this->getLanguageService()->getLL('noSiteConfiguration'),
189
                '',
190
                FlashMessage::WARNING
191
            );
192
            return $this->renderFlashMessage($flashMessage);
193
        }
194
195
        $this->registerDocHeader($pageId, $languageId, $targetUrl);
196
197
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
198
        $icons = [];
199
        $icons['orientation'] = $iconFactory->getIcon('actions-device-orientation-change', Icon::SIZE_SMALL)->render('inline');
200
        $icons['fullscreen'] = $iconFactory->getIcon('actions-fullscreen', Icon::SIZE_SMALL)->render('inline');
201
        $icons['expand'] = $iconFactory->getIcon('actions-expand', Icon::SIZE_SMALL)->render('inline');
202
        $icons['desktop'] = $iconFactory->getIcon('actions-device-desktop', Icon::SIZE_SMALL)->render('inline');
203
        $icons['tablet'] = $iconFactory->getIcon('actions-device-tablet', Icon::SIZE_SMALL)->render('inline');
204
        $icons['mobile'] = $iconFactory->getIcon('actions-device-mobile', Icon::SIZE_SMALL)->render('inline');
205
        $icons['unidentified'] = $iconFactory->getIcon('actions-device-unidentified', Icon::SIZE_SMALL)->render('inline');
206
207
        $current = ($this->getBackendUser()->uc['moduleData']['web_view']['States']['current'] ?: []);
208
        $current['label'] = ($current['label'] ?? $this->getLanguageService()->sL('LLL:EXT:viewpage/Resources/Private/Language/locallang.xlf:custom'));
209
        $current['width'] = (isset($current['width']) && (int)$current['width'] >= 300 ? (int)$current['width'] : 320);
210
        $current['height'] = (isset($current['height']) && (int)$current['height'] >= 300 ? (int)$current['height'] : 480);
211
212
        $custom = ($this->getBackendUser()->uc['moduleData']['web_view']['States']['custom'] ?: []);
213
        $custom['width'] = (isset($current['custom']) && (int)$current['custom'] >= 300 ? (int)$current['custom'] : 320);
214
        $custom['height'] = (isset($current['custom']) && (int)$current['custom'] >= 300 ? (int)$current['custom'] : 480);
215
216
        $this->view->assign('icons', $icons);
217
        $this->view->assign('current', $current);
218
        $this->view->assign('custom', $custom);
219
        $this->view->assign('presetGroups', $this->getPreviewPresets($pageId));
220
        $this->view->assign('url', $targetUrl);
221
222
        $this->moduleTemplate->setContent($this->view->render());
223
        return new HtmlResponse($this->moduleTemplate->renderContent());
224
    }
225
226
    protected function renderFlashMessage(FlashMessage $flashMessage): HtmlResponse
227
    {
228
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
229
        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
230
        $defaultFlashMessageQueue->enqueue($flashMessage);
231
232
        $this->moduleTemplate->setContent($this->view->render());
233
        return new HtmlResponse($this->moduleTemplate->renderContent());
234
    }
235
236
    /**
237
     * With page TS config it is possible to force a specific type id via mod.web_view.type
238
     * for a page id or a page tree.
239
     * The method checks if a type is set for the given id and returns the additional GET string.
240
     *
241
     * @param int $pageId
242
     * @return string
243
     */
244
    protected function getTypeParameterIfSet(int $pageId): string
245
    {
246
        $typeParameter = '';
247
        $typeId = (int)(BackendUtility::getPagesTSconfig($pageId)['mod.']['web_view.']['type'] ?? 0);
248
        if ($typeId > 0) {
249
            $typeParameter = '&type=' . $typeId;
250
        }
251
        return $typeParameter;
252
    }
253
254
    /**
255
     * Get available presets for page id
256
     *
257
     * @param int $pageId
258
     * @return array
259
     */
260
    protected function getPreviewPresets(int $pageId): array
261
    {
262
        $presetGroups = [
263
            'desktop' => [],
264
            'tablet' => [],
265
            'mobile' => [],
266
            'unidentified' => []
267
        ];
268
        $previewFrameWidthConfig = BackendUtility::getPagesTSconfig($pageId)['mod.']['web_view.']['previewFrameWidths.'] ?? [];
269
        foreach ($previewFrameWidthConfig as $item => $conf) {
270
            $data = [
271
                'key' => substr($item, 0, -1),
272
                'label' => $conf['label'] ?? null,
273
                'type' => $conf['type'] ?? 'unknown',
274
                'width' => (isset($conf['width']) && (int)$conf['width'] > 0 && strpos($conf['width'], '%') === false) ? (int)$conf['width'] : null,
275
                'height' => (isset($conf['height']) && (int)$conf['height'] > 0 && strpos($conf['height'], '%') === false) ? (int)$conf['height'] : null,
276
            ];
277
            $width = (int)substr($item, 0, -1);
278
            if (!isset($data['width']) && $width > 0) {
279
                $data['width'] = $width;
280
            }
281
            if (!isset($data['label'])) {
282
                $data['label'] = $data['key'];
283
            } elseif (strpos($data['label'], 'LLL:') === 0) {
284
                $data['label'] = $this->getLanguageService()->sL(trim($data['label']));
285
            }
286
287
            if (array_key_exists($data['type'], $presetGroups)) {
288
                $presetGroups[$data['type']][$data['key']] = $data;
289
            } else {
290
                $presetGroups['unidentified'][$data['key']] = $data;
291
            }
292
        }
293
294
        return $presetGroups;
295
    }
296
297
    /**
298
     * Returns the preview languages
299
     *
300
     * @param int $pageId
301
     * @return array
302
     */
303
    protected function getPreviewLanguages(int $pageId): array
304
    {
305
        $languages = [];
306
        $modSharedTSconfig = BackendUtility::getPagesTSconfig($pageId)['mod.']['SHARED.'] ?? [];
307
        if ($modSharedTSconfig['view.']['disableLanguageSelector'] === '1') {
308
            return $languages;
309
        }
310
311
        try {
312
            $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
313
            $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($pageId);
314
            $siteLanguages = $site->getAvailableLanguages($this->getBackendUser(), false, $pageId);
315
316
            foreach ($siteLanguages as $siteLanguage) {
317
                $languageAspectToTest = LanguageAspectFactory::createFromSiteLanguage($siteLanguage);
318
                $page = $pageRepository->getPageOverlay($pageRepository->getPage($pageId), $siteLanguage->getLanguageId());
319
320
                if ($pageRepository->isPageSuitableForLanguage($page, $languageAspectToTest)) {
321
                    $languages[$siteLanguage->getLanguageId()] = $siteLanguage->getTitle();
322
                }
323
            }
324
        } catch (SiteNotFoundException $e) {
325
            // do nothing
326
        }
327
        return $languages;
328
    }
329
330
    /**
331
     * Returns the current language
332
     *
333
     * @param int $pageId
334
     * @param string $languageParam
335
     * @return int
336
     */
337
    protected function getCurrentLanguage(int $pageId, string $languageParam = null): int
338
    {
339
        $languageId = (int)$languageParam;
340
        if ($languageParam === null) {
341
            $states = $this->getBackendUser()->uc['moduleData']['web_view']['States'];
342
            $languages = $this->getPreviewLanguages($pageId);
343
            if (isset($states['languageSelectorValue']) && isset($languages[$states['languageSelectorValue']])) {
344
                $languageId = (int)$states['languageSelectorValue'];
345
            }
346
        } else {
347
            $this->getBackendUser()->uc['moduleData']['web_view']['States']['languageSelectorValue'] = $languageId;
348
            $this->getBackendUser()->writeUC($this->getBackendUser()->uc);
349
        }
350
        return $languageId;
351
    }
352
353
    /**
354
     * Verifies if doktype of given page is valid
355
     *
356
     * @param int $pageId
357
     * @return bool
358
     */
359
    protected function isValidDoktype(int $pageId = 0): bool
360
    {
361
        if ($pageId === 0) {
362
            return false;
363
        }
364
365
        $page = BackendUtility::getRecord('pages', $pageId);
366
        $pageType = (int)($page['doktype'] ?? 0);
367
368
        return $pageType !== 0
369
            && !in_array($pageType, [
370
                PageRepository::DOKTYPE_SPACER,
371
                PageRepository::DOKTYPE_SYSFOLDER,
372
                PageRepository::DOKTYPE_RECYCLER
373
            ], true);
374
    }
375
376
    /**
377
     * @return BackendUserAuthentication
378
     */
379
    protected function getBackendUser(): BackendUserAuthentication
380
    {
381
        return $GLOBALS['BE_USER'];
382
    }
383
384
    /**
385
     * @return LanguageService
386
     */
387
    protected function getLanguageService(): LanguageService
388
    {
389
        return $GLOBALS['LANG'];
390
    }
391
}
392