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

FluidTYPO3 / flux / 14774311267

01 May 2025 11:00AM UTC coverage: 93.246% (+0.3%) from 92.9%
14774311267

push

github

NamelessCoder
[TER] 11.0.0

7083 of 7596 relevant lines covered (93.25%)

66.31 hits per line

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

95.93
/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);
27✔
51

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

149
    protected function renderGrid(ProviderInterface $provider, array $row, Form $form): string
150
    {
151
        $content = '';
23✔
152
        $grid = $provider->getGrid($row);
23✔
153
        if ($grid->hasChildren()) {
23✔
154
            $options = $this->getPreviewOptions($form);
16✔
155
            if ($this->getOptionToggle($options)) {
16✔
156
                $content = $this->drawGridToggle($row, $content);
16✔
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'];
16✔
164
            $GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'] = array_merge(
16✔
165
                $GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'],
16✔
166
                $grid->buildExtendedBackendLayoutArray($row['uid'])['__items']
16✔
167
            );
16✔
168

169
            $pageUid = $row['pid'];
16✔
170
            if (($workspaceId = $this->getBackendUser()->workspace) > 0) {
16✔
171
                $workspaceVersion = $this->fetchWorkspaceVersionOfRecord($workspaceId, $row['uid']);
4✔
172
                $pageUid = $workspaceVersion['pid'] ?? $pageUid;
4✔
173
            }
174
            $pageLayoutView = $this->getInitializedPageLayoutView($provider, $row);
16✔
175
            if ($pageLayoutView instanceof BackendLayoutRenderer) {
16✔
176
                if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '12.0', '>=')) {
8✔
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);
8✔
184
                }
185
            } elseif (method_exists($pageLayoutView, 'start') && method_exists($pageLayoutView, 'generateList')) {
8✔
186
                $pageLayoutView->start($pageUid, 'tt_content', 0);
4✔
187
                $pageLayoutView->generateList();
4✔
188
                $content .= $pageLayoutView->HTMLcode;
4✔
189
            } else {
190
                $content .= $pageLayoutView->getTable_tt_content($row['pid']);
4✔
191
            }
192

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

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

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

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

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

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

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

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

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

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

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

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

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

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

276
            return $backendLayoutRenderer;
7✔
277
        }
278

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

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

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

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

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

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

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

327
        return $view;
1✔
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