• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FluidTYPO3 / flux / 15918415903

20 May 2025 10:36AM UTC coverage: 91.109% (-2.1%) from 93.21%
15918415903

push

github

NamelessCoder
[TASK] Lock phpstan version

6927 of 7603 relevant lines covered (91.11%)

9.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.02
/Classes/Integration/PreviewView.php
1
<?php
2
namespace FluidTYPO3\Flux\Integration;
3

4
/*
5
 * This file is part of the FluidTYPO3/Flux project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10

11
use FluidTYPO3\Flux\Enum\FormOption;
12
use FluidTYPO3\Flux\Enum\PreviewOption;
13
use FluidTYPO3\Flux\Form;
14
use FluidTYPO3\Flux\Hooks\HookHandler;
15
use FluidTYPO3\Flux\Integration\Overrides\PageLayoutView;
16
use FluidTYPO3\Flux\Provider\ProviderInterface;
17
use FluidTYPO3\Flux\Proxy\SiteFinderProxy;
18
use FluidTYPO3\Flux\Service\WorkspacesAwareRecordService;
19
use FluidTYPO3\Flux\Utility\ExtensionNamingUtility;
20
use FluidTYPO3\Flux\Utility\RecursiveArrayUtility;
21
use TYPO3\CMS\Backend\Utility\BackendUtility;
22
use TYPO3\CMS\Backend\View\BackendViewFactory;
23
use TYPO3\CMS\Backend\View\Drawing\DrawingConfiguration;
24
use TYPO3\CMS\Backend\View\PageLayoutContext;
25
use TYPO3\CMS\Backend\View\PageViewMode;
26
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
27
use TYPO3\CMS\Core\Configuration\Features;
28
use TYPO3\CMS\Core\Domain\RecordFactory;
29
use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
30
use TYPO3\CMS\Core\Localization\LanguageService;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
33
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
34
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
35
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
36
use TYPO3Fluid\Fluid\View\TemplateView;
37

38
class PreviewView extends TemplateView
39
{
40
    protected array $templates = [
41
        'gridToggle' => '<div class="grid-visibility-toggle" data-toggle-uid="%s">%s</div>',
42
        'link' => '<a href="%s" title="%s" class="btn btn-default btn-sm">%s %s</a>'
43
    ];
44

45
    protected ConfigurationManagerInterface $configurationManager;
46
    protected WorkspacesAwareRecordService $workspacesAwareRecordService;
47

48
    public function __construct(RenderingContextInterface $context = null)
49
    {
50
        parent::__construct($context);
5✔
51

52
        /** @var ConfigurationManagerInterface $configurationManager */
53
        $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
5✔
54
        $this->configurationManager = $configurationManager;
5✔
55

56
        /** @var WorkspacesAwareRecordService $workspacesAwareRecordService */
57
        $workspacesAwareRecordService = GeneralUtility::makeInstance(WorkspacesAwareRecordService::class);
5✔
58
        $this->workspacesAwareRecordService = $workspacesAwareRecordService;
5✔
59
    }
60

61
    public function getPreview(ProviderInterface $provider, array $row, bool $withoutGrid = false): string
62
    {
63
        $form = $provider->getForm($row);
3✔
64
        $options = $this->getPreviewOptions($form);
3✔
65
        $mode = $this->getOptionMode($options);
3✔
66
        $previewContent = (string) $this->renderPreviewSection($provider, $row, $form);
3✔
67

68
        if ($withoutGrid || PreviewOption::MODE_NONE === $mode || !is_object($form)) {
3✔
69
            return $previewContent;
1✔
70
        }
71

72
        $gridContent = $this->renderGrid($provider, $row, $form);
2✔
73
        $collapsedClass = '';
2✔
74
        if (in_array($row['uid'], (array) json_decode($this->getCookie() ?? ''))) {
2✔
75
            $collapsedClass = ' flux-grid-hidden';
2✔
76
        }
77
        $gridContent = sprintf(
2✔
78
            '<div class="flux-collapse%s" data-grid-uid="%d">%s</div>',
2✔
79
            $collapsedClass,
2✔
80
            $row['uid'],
2✔
81
            $gridContent
2✔
82
        );
2✔
83
        if (PreviewOption::MODE_APPEND === $mode) {
2✔
84
            $previewContent = $previewContent . $gridContent;
1✔
85
        } elseif (PreviewOption::MODE_PREPEND === $mode) {
1✔
86
            $previewContent = $gridContent . $previewContent;
1✔
87
        }
88

89
        $previewContent = trim($previewContent);
2✔
90

91
        return HookHandler::trigger(
2✔
92
            HookHandler::PREVIEW_RENDERED,
2✔
93
            ['form' => $form, 'preview' => $previewContent]
2✔
94
        )['preview'];
2✔
95
    }
96

97
    protected function getPreviewOptions(Form $form = null): array
98
    {
99
        if (!is_object($form) || !$form->hasOption(PreviewOption::PREVIEW)) {
8✔
100
            return [
5✔
101
                PreviewOption::MODE => $this->getOptionMode(),
5✔
102
                PreviewOption::TOGGLE => $this->getOptionToggle()
5✔
103
            ];
5✔
104
        }
105

106
        return (array) $form->getOption(PreviewOption::PREVIEW);
3✔
107
    }
108

109
    protected function getOptionMode(array $options = []): string
110
    {
111
        return $options[PreviewOption::MODE] ?? PreviewOption::MODE_APPEND;
9✔
112
    }
113

114
    protected function getOptionToggle(array $options = []): bool
115
    {
116
        return (boolean) ($options[PreviewOption::TOGGLE] ?? true);
5✔
117
    }
118

119
    protected function renderPreviewSection(ProviderInterface $provider, array $row, Form $form = null): ?string
120
    {
121
        $templatePathAndFilename = $provider->getTemplatePathAndFilename($row);
2✔
122
        if (!$templatePathAndFilename) {
2✔
123
            return null;
1✔
124
        }
125
        $extensionKey = $provider->getExtensionKey($row);
1✔
126

127
        $templateVariables = $provider->getTemplateVariables($row);
1✔
128
        $flexformVariables = $provider->getFlexFormValues($row);
1✔
129
        $variables = RecursiveArrayUtility::merge($templateVariables, $flexformVariables);
1✔
130
        $variables['row'] = $row;
1✔
131
        $variables['record'] = $row;
1✔
132

133
        if (is_object($form)) {
1✔
134
            $formLabel = $form->getLabel();
1✔
135
            $label = $this->getLanguageService()->sL((string) $formLabel);
1✔
136
            $variables['label'] = $label;
1✔
137
        }
138

139
        $renderingContext = $this->getRenderingContext();
1✔
140
        $renderingContext->setControllerName($provider->getControllerNameFromRecord($row));
1✔
141
        $renderingContext->setControllerAction($provider->getControllerActionFromRecord($row));
1✔
142
        $renderingContext->getTemplatePaths()->fillDefaultsByPackageName(
1✔
143
            ExtensionNamingUtility::getExtensionKey($extensionKey)
1✔
144
        );
1✔
145
        $renderingContext->getTemplatePaths()->setTemplatePathAndFilename($templatePathAndFilename);
1✔
146
        return $this->renderSection('Preview', $variables, true);
1✔
147
    }
148

149
    protected function renderGrid(ProviderInterface $provider, array $row, Form $form): string
150
    {
151
        $content = '';
5✔
152
        $grid = $provider->getGrid($row);
5✔
153
        if ($grid->hasChildren()) {
5✔
154
            $options = $this->getPreviewOptions($form);
4✔
155
            if ($this->getOptionToggle($options)) {
4✔
156
                $content = $this->drawGridToggle($row, $content);
4✔
157
            }
158

159
            // Live-patching TCA to add items, which will be read by the BackendLayoutView in order to read
160
            // the LLL labels of individual columns. Unfortunately, BackendLayoutView calls functions in a way
161
            // that it is not possible to overrule the colPos values via the BackendLayout without creating an
162
            // XCLASS - so a bit of runtime TCA patching is preferable.
163
            $tcaBackup = $GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'];
4✔
164
            $GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'] = array_merge(
4✔
165
                $GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'],
4✔
166
                $grid->buildExtendedBackendLayoutArray($row['uid'])['__items']
4✔
167
            );
4✔
168

169
            $pageUid = $row['pid'];
4✔
170
            if (($workspaceId = $this->getBackendUser()->workspace) > 0) {
4✔
171
                $workspaceVersion = $this->fetchWorkspaceVersionOfRecord($workspaceId, $row['uid']);
1✔
172
                $pageUid = $workspaceVersion['pid'] ?? $pageUid;
1✔
173
            }
174
            $pageLayoutView = $this->getInitializedPageLayoutView($provider, $row);
4✔
175
            if ($pageLayoutView instanceof BackendLayoutRenderer) {
4✔
176
                if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '12.0', '>=')) {
2✔
177
                    $content .= $pageLayoutView->drawContent(
×
178
                        $GLOBALS['TYPO3_REQUEST'],
×
179
                        $pageLayoutView->getContext(),
×
180
                        $form->getOption(FormOption::RECORD_TABLE) === 'pages' // render unused area only for "pages"
×
181
                    );
×
182
                } else {
183
                    $content .= $pageLayoutView->drawContent(false);
2✔
184
                }
185
            } elseif (method_exists($pageLayoutView, 'start') && method_exists($pageLayoutView, 'generateList')) {
2✔
186
                $pageLayoutView->start($pageUid, 'tt_content', 0);
1✔
187
                $pageLayoutView->generateList();
1✔
188
                $content .= $pageLayoutView->HTMLcode;
1✔
189
            } else {
190
                $content .= $pageLayoutView->getTable_tt_content($row['pid']);
1✔
191
            }
192

193
            $GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'] = $tcaBackup;
4✔
194
        }
195
        return $content;
5✔
196
    }
197

198
    protected function drawGridToggle(array $row, string $content): string
199
    {
200
        return sprintf($this->templates['gridToggle'], $row['uid'], $content);
4✔
201
    }
202

203
    /**
204
     * @return PageLayoutView|BackendLayoutRenderer
205
     */
206
    protected function getInitializedPageLayoutView(ProviderInterface $provider, array $row)
207
    {
208
        $pageId = (int) $row['pid'];
1✔
209
        $pageRecord = $this->workspacesAwareRecordService->getSingle('pages', '*', $pageId);
1✔
210
        $moduleData = $this->getBackendUser()->getModuleData('web_layout', '');
1✔
211
        $showHiddenRecords = (int) ($moduleData['tt_content_showHidden'] ?? 1);
1✔
212

213
        // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't
214
        // disabled the option analog behavior to the PageLayoutController at the end of menuConfig()
215
        if ($this->getActiveWorkspaceId() !== 0 || !$showHiddenRecords) {
1✔
216
            $moduleData['tt_content_showHidden'] = 1;
×
217
        }
218

219
        $parentRecordUid = ($row['l18n_parent'] ?? 0) > 0 ? $row['l18n_parent'] : ($row['t3ver_oid'] ?: $row['uid']);
1✔
220

221
        $backendLayout = $provider->getGrid($row)->buildBackendLayout($parentRecordUid);
1✔
222
        $layoutConfiguration = $backendLayout->getStructure();
1✔
223

224
        /** @var Features $features */
225
        $features = GeneralUtility::makeInstance(Features::class);
1✔
226
        $fluidBasedLayoutFeatureEnabled = $features->isFeatureEnabled('fluidBasedPageModule');
1✔
227

228
        if ($fluidBasedLayoutFeatureEnabled) {
1✔
229
            /** @var SiteFinderProxy $siteFinder */
230
            $siteFinder = GeneralUtility::makeInstance(SiteFinderProxy::class);
1✔
231
            $site = $siteFinder->getSiteByPageId($pageId);
1✔
232
            $language = null;
1✔
233
            if ($row['sys_language_uid'] >= 0) {
1✔
234
                $language = $site->getLanguageById((int) $row['sys_language_uid']);
1✔
235
            }
236

237
            if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '13.4', '>=')) {
1✔
238
                $configuration = DrawingConfiguration::create($backendLayout, [], PageViewMode::LayoutView);
×
239
                $configuration->setSelectedLanguageId($language->getLanguageId());
×
240

241
                /** @var PageLayoutContext $context */
242
                $context = GeneralUtility::makeInstance(
×
243
                    PageLayoutContext::class,
×
244
                    $this->fetchPageRecordWithoutOverlay($pageId),
×
245
                    $backendLayout,
×
246
                    $site,
×
247
                    $configuration,
×
248
                    $GLOBALS['TYPO3_REQUEST']
×
249
                );
×
250
            } else {
251
                /** @var PageLayoutContext $context */
252
                $context = GeneralUtility::makeInstance(
1✔
253
                    PageLayoutContext::class,
1✔
254
                    $this->fetchPageRecordWithoutOverlay($pageId),
1✔
255
                    $backendLayout
1✔
256
                );
1✔
257
                $configuration = $context->getDrawingConfiguration();
1✔
258
            }
259

260
            if (isset($language)) {
1✔
261
                 $context = $context->cloneForLanguage($language);
1✔
262
            }
263

264
            if (method_exists($configuration, 'setActiveColumns')) {
1✔
265
                $configuration->setActiveColumns($backendLayout->getColumnPositionNumbers());
1✔
266
            }
267

268
            if (isset($language) && method_exists($configuration, 'setSelectedLanguageUid')) {
1✔
269
                $configuration->setSelectedLanguageId($language->getLanguageId());
×
270
            }
271

272
            $backendLayoutRenderer = $this->createBackendLayoutRenderer($context);
1✔
273

274
            $backendLayoutRenderer->setContext($context);
1✔
275

276
            return $backendLayoutRenderer;
1✔
277
        }
278

279
        $eventDispatcher = GeneralUtility::makeInstance(EventDispatcher::class);
×
280

281
        /** @var PageLayoutView $view */
282
        $view = GeneralUtility::makeInstance(PageLayoutView::class, $eventDispatcher);
×
283
        $view->setProvider($provider);
×
284
        $view->setRecord($row);
×
285

286
        $contentTypeLabels = [];
×
287
        foreach ($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'] as $val) {
×
288
            $contentTypeLabels[$val[1]] = $this->getLanguageService()->sL($val[0]);
×
289
        }
290
        $itemLabels = [];
×
291
        foreach ($GLOBALS['TCA']['tt_content']['columns'] as $name => $val) {
×
292
            $itemLabels[$name] = ($val['label'] ?? false) ? $this->getLanguageService()->sL($val['label']) : '';
×
293
        }
294

295
        array_push(
×
296
            $GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'],
×
297
            ...($layoutConfiguration['__items'] ?? [])
×
298
        );
×
299

300
        $columnsAsCSV = implode(',', $layoutConfiguration['__colPosList'] ?? []);
×
301

302
        $view->script = 'db_layout.php';
×
303
        $view->showIcon = 1;
×
304
        $view->setLMargin = 0;
×
305
        $view->doEdit = 1;
×
306
        $view->no_noWrap = 1;
×
307
        $view->ext_CALC_PERMS = $this->getBackendUser()->calcPerms($pageRecord);
×
308
        $view->id = $row['pid'];
×
309
        $view->table = 'tt_content';
×
310
        $view->tableList = 'tt_content';
×
311
        $view->currentTable = 'tt_content';
×
312
        $view->tt_contentConfig['showCommands'] = 1;
×
313
        $view->tt_contentConfig['showInfo'] = 1;
×
314
        $view->tt_contentConfig['single'] = 0;
×
315
        $view->nextThree = 1;
×
316
        $view->tt_contentConfig['sys_language_uid'] = (int) $row['sys_language_uid'];
×
317
        $view->tt_contentConfig['showHidden'] = $showHiddenRecords;
×
318
        $view->tt_contentConfig['activeCols'] = $columnsAsCSV;
×
319
        $view->tt_contentConfig['cols'] = $columnsAsCSV;
×
320
        $view->CType_labels = $contentTypeLabels;
×
321
        $view->itemLabels = $itemLabels;
×
322

323
        if (($pageInfo = $this->checkAccessToPage($pageId))) {
×
324
            $view->setPageinfo($pageInfo);
×
325
        }
326

327
        return $view;
×
328
    }
329

330
    /**
331
     * @codeCoverageIgnore
332
     */
333
    protected function createBackendLayoutRenderer(PageLayoutContext $context): BackendLayoutRenderer
334
    {
335
        if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '13.4', '>=')) {
336
            /** @var BackendViewFactory $backendViewFactory */
337
            $backendViewFactory = GeneralUtility::getContainer()->get(BackendViewFactory::class);
338
            /** @var RecordFactory $recordFactory */
339
            $recordFactory = GeneralUtility::makeInstance(RecordFactory::class);
340
            /** @var BackendLayoutRenderer $backendLayoutRenderer */
341
            $backendLayoutRenderer = GeneralUtility::makeInstance(
342
                BackendLayoutRenderer::class,
343
                $backendViewFactory,
344
                $recordFactory
345
            );
346
        } elseif (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '12.0', '>=')) {
347
            /** @var BackendViewFactory $backendViewFactory */
348
            $backendViewFactory = GeneralUtility::getContainer()->get(BackendViewFactory::class);
349
            /** @var BackendLayoutRenderer $backendLayoutRenderer */
350
            $backendLayoutRenderer = GeneralUtility::makeInstance(
351
                BackendLayoutRenderer::class,
352
                $backendViewFactory
353
            );
354
        } else {
355
            /** @var BackendLayoutRenderer $backendLayoutRenderer */
356
            $backendLayoutRenderer = GeneralUtility::makeInstance(BackendLayoutRenderer::class, $context);
357
        }
358
        return $backendLayoutRenderer;
359
    }
360

361
    /**
362
     * @codeCoverageIgnore
363
     */
364
    protected function fetchWorkspaceVersionOfRecord(int $workspaceId, int $recordUid): ?array
365
    {
366
        /** @var array|false $workspaceVersion */
367
        $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($workspaceId, 'tt_content', $recordUid);
368
        return $workspaceVersion ?: null;
369
    }
370

371
    /**
372
     * @codeCoverageIgnore
373
     * @return array|false
374
     */
375
    protected function checkAccessToPage(int $pageId)
376
    {
377
        return BackendUtility::readPageAccess($pageId, '');
378
    }
379

380
    /**
381
     * @codeCoverageIgnore
382
     */
383
    protected function fetchPageRecordWithoutOverlay(int $pageId): ?array
384
    {
385
        return BackendUtility::getRecord('pages', $pageId);
386
    }
387

388
    /**
389
     * @codeCoverageIgnore
390
     */
391
    protected function getBackendUser(): BackendUserAuthentication
392
    {
393
        return $GLOBALS['BE_USER'];
394
    }
395

396
    /**
397
     * @codeCoverageIgnore
398
     */
399
    protected function getLanguageService(): LanguageService
400
    {
401
        return $GLOBALS['LANG'];
402
    }
403

404
    /**
405
     * @codeCoverageIgnore
406
     */
407
    protected function getCookie(): ?string
408
    {
409
        return $_COOKIE['fluxCollapseStates'] ?? null;
410
    }
411

412
    /**
413
     * @codeCoverageIgnore
414
     */
415
    protected function getActiveWorkspaceId(): int
416
    {
417
        return (integer) ($GLOBALS['BE_USER']->workspace ?? 0);
418
    }
419
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc