Completed
Push — master ( f66149...a7372c )
by
unknown
130:24 queued 111:56
created

ContentFetcher::setBackendLayout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
declare(strict_types = 1);
3
4
namespace TYPO3\CMS\Backend\View\BackendLayout;
5
6
/*
7
 * This file is part of the TYPO3 CMS project.
8
 *
9
 * It is free software; you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License, either version 2
11
 * of the License, or any later version.
12
 *
13
 * For the full copyright and license information, please read the
14
 * LICENSE.txt file that was distributed with this source code.
15
 *
16
 * The TYPO3 project - inspiring people to share!
17
 */
18
19
use Doctrine\DBAL\Driver\Statement;
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
21
use TYPO3\CMS\Backend\View\PageLayoutView;
22
use TYPO3\CMS\Core\Database\ConnectionPool;
23
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
24
use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
25
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
26
use TYPO3\CMS\Core\Localization\LanguageService;
27
use TYPO3\CMS\Core\Messaging\FlashMessage;
28
use TYPO3\CMS\Core\Messaging\FlashMessageService;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
31
/**
32
 * Class responsible for fetching the content data related to a BackendLayout
33
 *
34
 * - Reads content records
35
 * - Performs workspace overlay on records
36
 * - Capable of returning all records in active language as flat array
37
 * - Capable of returning records for a given column in a given (optional) language
38
 * - Capable of returning translation data (brief info about translation consistenty)
39
 */
40
class ContentFetcher
41
{
42
    /**
43
     * @var BackendLayout
44
     */
45
    protected $backendLayout;
46
47
    /**
48
     * @var array
49
     */
50
    protected $fetchedContentRecords = [];
51
52
    /**
53
     * Stores whether a certain language has translations in it
54
     *
55
     * @var array
56
     */
57
    protected $languageHasTranslationsCache = [];
58
59
    public function __construct(BackendLayout $backendLayout)
60
    {
61
        $this->backendLayout = $backendLayout;
62
    }
63
64
    public function setBackendLayout(BackendLayout $backendLayout): void
65
    {
66
        $this->backendLayout = $backendLayout;
67
    }
68
69
    /**
70
     * Gets content records per column.
71
     * This is required for correct workspace overlays.
72
     *
73
     * @param int|null $columnNumber
74
     * @param int|null $languageId
75
     * @return array Associative array for each column (colPos) or for all columns if $columnNumber is null
76
     */
77
    public function getContentRecordsPerColumn(?int $columnNumber = null, ?int $languageId = null): iterable
78
    {
79
        if (empty($this->fetchedContentRecords)) {
80
            $queryBuilder = $this->getQueryBuilder();
81
            $records = $this->getResult($queryBuilder->execute());
0 ignored issues
show
Bug introduced by
It seems like $queryBuilder->execute() can also be of type integer; however, parameter $result of TYPO3\CMS\Backend\View\B...entFetcher::getResult() does only seem to accept Doctrine\DBAL\Driver\Statement, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

81
            $records = $this->getResult(/** @scrutinizer ignore-type */ $queryBuilder->execute());
Loading history...
82
            foreach ($records as $record) {
83
                $recordLanguage = (int)$record['sys_language_uid'];
84
                $recordColumnNumber = (int)$record['colPos'];
85
                $this->fetchedContentRecords[$recordLanguage][$recordColumnNumber][] = $record;
86
            }
87
        }
88
89
        $languageId = $languageId ?? $this->backendLayout->getDrawingConfiguration()->getLanguageColumnsPointer();
90
91
        $contentByLanguage = &$this->fetchedContentRecords[$languageId];
92
93
        if ($columnNumber === null) {
94
            return $contentByLanguage ?? [];
95
        }
96
97
        return $contentByLanguage[$columnNumber] ?? [];
98
    }
99
100
    public function getFlatContentRecords(): iterable
101
    {
102
        $languageId = $this->backendLayout->getDrawingConfiguration()->getLanguageColumnsPointer();
103
        $contentRecords = $this->getContentRecordsPerColumn(null, $languageId);
104
        return empty($contentRecords) ? [] : array_merge(...$contentRecords);
105
    }
106
107
    public function getUnusedRecords(): iterable
108
    {
109
        $unrendered = [];
110
        $knownColumnPositionNumbers = $this->backendLayout->getColumnPositionNumbers();
111
        $rememberer = $this->backendLayout->getRecordRememberer();
112
        foreach ($this->fetchedContentRecords[$this->backendLayout->getDrawingConfiguration()->getLanguageColumnsPointer()] ?? [] as $contentRecordsInColumn) {
113
            foreach ($contentRecordsInColumn as $contentRecord) {
114
                if (!$rememberer->isRemembered($contentRecord['uid']) && !in_array($contentRecord['colPos'], $knownColumnPositionNumbers)) {
115
                    $unrendered[] = $contentRecord;
116
                }
117
            }
118
        }
119
        return $unrendered;
120
    }
121
122
    public function getTranslationData(iterable $contentElements, int $language): array
123
    {
124
        $configuration = $this->backendLayout->getDrawingConfiguration();
125
126
        if ($language === 0) {
127
            return [];
128
        }
129
130
        if (!isset($this->languageHasTranslationsCache[$language])) {
131
            foreach ($contentElements as $contentElement) {
132
                if ((int)$contentElement['l18n_parent'] === 0) {
133
                    $this->languageHasTranslationsCache[$language]['hasStandAloneContent'] = true;
134
                    $this->languageHasTranslationsCache[$language]['mode'] = 'free';
135
                }
136
                if ((int)$contentElement['l18n_parent'] > 0) {
137
                    $this->languageHasTranslationsCache[$language]['hasTranslations'] = true;
138
                    $this->languageHasTranslationsCache[$language]['mode'] = 'connected';
139
                }
140
            }
141
            if (!isset($this->languageHasTranslationsCache[$language])) {
142
                $this->languageHasTranslationsCache[$language]['hasTranslations'] = false;
143
            }
144
145
            // Check for inconsistent translations, force "mixed" mode and dispatch a FlashMessage to user if such a case is encountered.
146
            if (isset($this->languageHasTranslationsCache[$language]['hasStandAloneContent'])
147
                && $this->languageHasTranslationsCache[$language]['hasTranslations']
148
            ) {
149
                $this->languageHasTranslationsCache[$language]['mode'] = 'mixed';
150
                $siteLanguage = $configuration->getSiteLanguage($language);
151
                $message = GeneralUtility::makeInstance(
152
                    FlashMessage::class,
153
                    sprintf($this->getLanguageService()->getLL('staleTranslationWarning'), $siteLanguage->getTitle()),
0 ignored issues
show
Bug introduced by
sprintf($this->getLangua...teLanguage->getTitle()) of type string is incompatible with the type array|array<mixed,mixed> expected by parameter $constructorArguments of TYPO3\CMS\Core\Utility\G...Utility::makeInstance(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

153
                    /** @scrutinizer ignore-type */ sprintf($this->getLanguageService()->getLL('staleTranslationWarning'), $siteLanguage->getTitle()),
Loading history...
154
                    sprintf($this->getLanguageService()->getLL('staleTranslationWarningTitle'), $siteLanguage->getTitle()),
155
                    FlashMessage::WARNING
0 ignored issues
show
Bug introduced by
TYPO3\CMS\Core\Messaging\FlashMessage::WARNING of type integer is incompatible with the type array|array<mixed,mixed> expected by parameter $constructorArguments of TYPO3\CMS\Core\Utility\G...Utility::makeInstance(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

155
                    /** @scrutinizer ignore-type */ FlashMessage::WARNING
Loading history...
156
                );
157
                $service = GeneralUtility::makeInstance(FlashMessageService::class);
158
                $queue = $service->getMessageQueueByIdentifier();
159
                $queue->addMessage($message);
160
            }
161
        }
162
        return $this->languageHasTranslationsCache[$language];
163
    }
164
165
    protected function getQueryBuilder(): QueryBuilder
166
    {
167
        $fields = ['*'];
168
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
169
            ->getQueryBuilderForTable('tt_content');
170
        $queryBuilder->getRestrictions()
171
            ->removeAll()
172
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
173
            ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
174
        $queryBuilder
175
            ->select(...$fields)
176
            ->from('tt_content');
177
178
        $queryBuilder->andWhere(
179
            $queryBuilder->expr()->eq(
180
                'tt_content.pid',
181
                $queryBuilder->createNamedParameter($this->backendLayout->getDrawingConfiguration()->getPageId(), \PDO::PARAM_INT)
182
            )
183
        );
184
185
        $additionalConstraints = [];
186
        $parameters = [
187
            'table' => 'tt_content',
188
            'fields' => $fields,
189
            'groupBy' => null,
190
            'orderBy' => null
191
        ];
192
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][PageLayoutView::class]['modifyQuery'] ?? [] as $className) {
193
            $hookObject = GeneralUtility::makeInstance($className);
194
            if (method_exists($hookObject, 'modifyQuery')) {
195
                $hookObject->modifyQuery(
196
                    $parameters,
197
                    'tt_content',
198
                    $this->backendLayout->getDrawingConfiguration()->getPageId(),
199
                    $additionalConstraints,
200
                    $fields,
201
                    $queryBuilder
202
                );
203
            }
204
        }
205
206
        return $queryBuilder;
207
    }
208
209
    protected function getResult(Statement $result): array
210
    {
211
        $output = [];
212
        while ($row = $result->fetch()) {
213
            BackendUtility::workspaceOL('tt_content', $row, -99, true);
214
            if ($row) {
215
                $output[] = $row;
216
            }
217
        }
218
        return $output;
219
    }
220
221
    protected function getLanguageService(): LanguageService
222
    {
223
        return $GLOBALS['LANG'];
224
    }
225
}
226