‪TYPO3CMS  10.4
ContentFetcher.php
Go to the documentation of this file.
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 
19 
35 
48 {
52  protected ‪$context;
53 
57  protected ‪$fetchedContentRecords = [];
58 
59  public function ‪__construct(‪PageLayoutContext $pageLayoutContext)
60  {
61  $this->context = $pageLayoutContext;
62  $this->fetchedContentRecords = $this->‪getRuntimeCache()->get('ContentFetcher_fetchedContentRecords') ?: [];
63  }
64 
73  public function ‪getContentRecordsPerColumn(?int $columnNumber = null, ?int $languageId = null): array
74  {
75  $languageId = $languageId ?? $this->context->getSiteLanguage()->getLanguageId();
76 
77  if (empty($this->fetchedContentRecords)) {
78  $isLanguageMode = $this->context->getDrawingConfiguration()->getLanguageMode();
79  $queryBuilder = $this->‪getQueryBuilder();
80  $result = $queryBuilder->execute();
81  $records = $this->‪getResult($result);
82  foreach ($records as $record) {
83  $recordLanguage = (int)$record['sys_language_uid'];
84  $recordColumnNumber = (int)$record['colPos'];
85  if ($recordLanguage === -1) {
86  // Record is set to "all languages", place it according to view mode.
87  if ($isLanguageMode) {
88  // Force the record to only be shown in default language in "Languages" view mode.
89  $recordLanguage = 0;
90  } else {
91  // Force the record to be shown in the currently active language in "Columns" view mode.
92  $recordLanguage = $languageId;
93  }
94  }
95  $this->fetchedContentRecords[$recordLanguage][$recordColumnNumber][] = $record;
96  }
97  $this->‪getRuntimeCache()->set('ContentFetcher_fetchedContentRecords', $this->fetchedContentRecords);
98  }
99 
100  $contentByLanguage = &$this->fetchedContentRecords[$languageId];
101 
102  if ($columnNumber === null) {
103  return $contentByLanguage ?? [];
104  }
105 
106  return $contentByLanguage[$columnNumber] ?? [];
107  }
108 
109  public function ‪getFlatContentRecords(int $languageId): iterable
110  {
111  $contentRecords = $this->‪getContentRecordsPerColumn(null, $languageId);
112  return empty($contentRecords) ? [] : array_merge(...$contentRecords);
113  }
114 
120  public function ‪getUnusedRecords(): iterable
121  {
122  $unrendered = [];
123  $hookArray = ‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['record_is_used'] ?? [];
124  $pageLayoutView = ‪PageLayoutView::createFromPageLayoutContext($this->context);
125 
126  $knownColumnPositionNumbers = $this->context->getBackendLayout()->getColumnPositionNumbers();
127  $rememberer = GeneralUtility::makeInstance(RecordRememberer::class);
128  $languageId = $this->context->getDrawingConfiguration()->getSelectedLanguageId();
129  foreach ($this->‪getContentRecordsPerColumn(null, $languageId) as $contentRecordsInColumn) {
130  foreach ($contentRecordsInColumn as $contentRecord) {
131  $used = $rememberer->isRemembered((int)$contentRecord['uid']);
132  // A hook mentioned that this record is used somewhere, so this is in fact "rendered" already
133  foreach ($hookArray as $hookFunction) {
134  $_params = ['columns' => $knownColumnPositionNumbers, 'record' => $contentRecord, 'used' => $used];
135  $used = GeneralUtility::callUserFunction($hookFunction, $_params, $pageLayoutView);
136  }
137  if (!$used) {
138  $unrendered[] = $contentRecord;
139  }
140  }
141  }
142  return $unrendered;
143  }
144 
145  public function ‪getTranslationData(iterable $contentElements, int $language): array
146  {
147  if ($language === 0) {
148  return [];
149  }
150 
151  $languageTranslationInfo = $this->‪getRuntimeCache()->get('ContentFetcher_TranslationInfo_' . $language) ?: [];
152  if (empty($languageTranslationInfo)) {
153  $contentRecordsInDefaultLanguage = $this->‪getContentRecordsPerColumn(null, 0);
154  if (!empty($contentRecordsInDefaultLanguage)) {
155  $contentRecordsInDefaultLanguage = array_merge(...$contentRecordsInDefaultLanguage);
156  }
157  $untranslatedRecordUids = array_flip(
158  array_column(
159  // Eliminate records with "-1" as sys_language_uid since they can not be translated
160  array_filter($contentRecordsInDefaultLanguage, static function (array $record): bool {
161  return (int)($record['sys_language_uid'] ?? 0) !== -1;
162  }),
163  'uid'
164  )
165  );
166 
167  foreach ($contentElements as $contentElement) {
168  if ((int)$contentElement['sys_language_uid'] === -1) {
169  continue;
170  }
171  if ((int)$contentElement['l18n_parent'] === 0) {
172  $languageTranslationInfo['hasStandAloneContent'] = true;
173  $languageTranslationInfo['mode'] = 'free';
174  }
175  if ((int)$contentElement['l18n_parent'] > 0) {
176  $languageTranslationInfo['hasTranslations'] = true;
177  $languageTranslationInfo['mode'] = 'connected';
178  }
179  if ((int)$contentElement['l10n_source'] > 0) {
180  unset($untranslatedRecordUids[(int)$contentElement['l10n_source']]);
181  }
182  }
183  if (!isset($languageTranslationInfo['hasTranslations'])) {
184  $languageTranslationInfo['hasTranslations'] = false;
185  }
186  $languageTranslationInfo['untranslatedRecordUids'] = array_keys($untranslatedRecordUids);
187 
188  // Check for inconsistent translations, force "mixed" mode and dispatch a FlashMessage to user if such a case is encountered.
189  if (isset($languageTranslationInfo['hasStandAloneContent'])
190  && $languageTranslationInfo['hasTranslations']
191  ) {
192  $languageTranslationInfo['mode'] = 'mixed';
193  $siteLanguage = $this->context->getSiteLanguage($language);
194 
195  $message = GeneralUtility::makeInstance(
196  FlashMessage::class,
197  $this->‪getLanguageService()->getLL('staleTranslationWarning'),
198  sprintf($this->‪getLanguageService()->getLL('staleTranslationWarningTitle'), $siteLanguage->getTitle()),
200  );
201  $service = GeneralUtility::makeInstance(FlashMessageService::class);
202  $queue = $service->getMessageQueueByIdentifier();
203  $queue->addMessage($message);
204  }
205 
206  $this->‪getRuntimeCache()->set('ContentFetcher_TranslationInfo_' . $language, $languageTranslationInfo);
207  }
208  return $languageTranslationInfo;
209  }
210 
211  protected function ‪getQueryBuilder(): ‪QueryBuilder
212  {
213  ‪$fields = ['*'];
214  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
215  ->getQueryBuilderForTable('tt_content');
216  $queryBuilder->getRestrictions()
217  ->removeAll()
218  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
219  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)‪$GLOBALS['BE_USER']->workspace));
220  $queryBuilder
221  ->select(...‪$fields)
222  ->from('tt_content');
223 
224  $queryBuilder->andWhere(
225  $queryBuilder->expr()->eq(
226  'tt_content.pid',
227  $queryBuilder->createNamedParameter($this->context->getPageId(), \PDO::PARAM_INT)
228  )
229  );
230 
231  $additionalConstraints = [];
232  $parameters = [
233  'table' => 'tt_content',
234  'fields' => ‪$fields,
235  'groupBy' => null,
236  'orderBy' => null
237  ];
238 
239  $sortBy = (string)(‪$GLOBALS['TCA']['tt_content']['ctrl']['sortby'] ?: ‪$GLOBALS['TCA']['tt_content']['ctrl']['default_sortby']);
240  foreach (‪QueryHelper::parseOrderBy($sortBy) as $orderBy) {
241  $queryBuilder->addOrderBy($orderBy[0], $orderBy[1]);
242  }
243 
244  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][PageLayoutView::class]['modifyQuery'] ?? [] as $className) {
245  $hookObject = GeneralUtility::makeInstance($className);
246  if (method_exists($hookObject, 'modifyQuery')) {
247  $hookObject->modifyQuery(
248  $parameters,
249  'tt_content',
250  $this->context->getPageId(),
251  $additionalConstraints,
252  ‪$fields,
253  $queryBuilder
254  );
255  }
256  }
257 
258  return $queryBuilder;
259  }
260 
261  protected function ‪getResult($result): array
262  {
263  ‪$output = [];
264  while ($row = $result->fetch()) {
265  ‪BackendUtility::workspaceOL('tt_content', $row, -99, true);
266  if ($row && !‪VersionState::cast($row['t3ver_state'] ?? 0)->equals(‪VersionState::DELETE_PLACEHOLDER)) {
267  ‪$output[] = $row;
268  }
269  }
270  return ‪$output;
271  }
272 
273  protected function ‪getLanguageService(): ‪LanguageService
274  {
275  return ‪$GLOBALS['LANG'];
276  }
277 
278  protected function ‪getRuntimeCache(): ‪VariableFrontend
279  {
280  return GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime');
281  }
282 }
‪TYPO3\CMS\Core\Database\Query\QueryHelper\parseOrderBy
‪static array array[] parseOrderBy(string $input)
Definition: QueryHelper.php:44
‪TYPO3\CMS\Backend\View\PageLayoutView\createFromPageLayoutContext
‪static PageLayoutView createFromPageLayoutContext(PageLayoutContext $context)
Definition: PageLayoutView.php:196
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getLanguageService
‪getLanguageService()
Definition: ContentFetcher.php:271
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getRuntimeCache
‪getRuntimeCache()
Definition: ContentFetcher.php:276
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\$context
‪PageLayoutContext $context
Definition: ContentFetcher.php:51
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\__construct
‪__construct(PageLayoutContext $pageLayoutContext)
Definition: ContentFetcher.php:57
‪TYPO3\CMS\Core\Versioning\VersionState\DELETE_PLACEHOLDER
‪const DELETE_PLACEHOLDER
Definition: VersionState.php:55
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher
Definition: ContentFetcher.php:48
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Core\Database\Query\QueryBuilder
Definition: QueryBuilder.php:52
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getQueryBuilder
‪getQueryBuilder()
Definition: ContentFetcher.php:209
‪TYPO3\CMS\Core\Type\Enumeration\cast
‪static static cast($value)
Definition: Enumeration.php:186
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\$fetchedContentRecords
‪array $fetchedContentRecords
Definition: ContentFetcher.php:55
‪TYPO3\CMS\Core\Messaging\AbstractMessage\WARNING
‪const WARNING
Definition: AbstractMessage.php:30
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
Definition: VariableFrontend.php:25
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:35
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:75
‪TYPO3\CMS\Core\Versioning\VersionState
Definition: VersionState.php:24
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Backend\View\BackendLayout
Definition: BackendLayout.php:16
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:24
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Backend\Utility\BackendUtility\workspaceOL
‪static workspaceOL($table, &$row, $wsid=-99, $unsetMovePointers=false)
Definition: BackendUtility.php:3586
‪TYPO3\CMS\Backend\View\PageLayoutContext
Definition: PageLayoutContext.php:43
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Backend\View\PageLayoutView
Definition: PageLayoutView.php:61
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getTranslationData
‪getTranslationData(iterable $contentElements, int $language)
Definition: ContentFetcher.php:143
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getFlatContentRecords
‪getFlatContentRecords(int $languageId)
Definition: ContentFetcher.php:107
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getResult
‪getResult($result)
Definition: ContentFetcher.php:259
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getUnusedRecords
‪iterable getUnusedRecords()
Definition: ContentFetcher.php:118
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getContentRecordsPerColumn
‪array getContentRecordsPerColumn(?int $columnNumber=null, ?int $languageId=null)
Definition: ContentFetcher.php:71
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39