‪TYPO3CMS  ‪main
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 
20 use Psr\EventDispatcher\EventDispatcherInterface;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
30 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
40 
53 {
55 
56  protected array ‪$fetchedContentRecords = [];
57 
58  protected EventDispatcherInterface ‪$eventDispatcher;
59 
60  public function ‪__construct(‪PageLayoutContext $pageLayoutContext)
61  {
62  $this->context = $pageLayoutContext;
63  $this->fetchedContentRecords = $this->‪getRuntimeCache()->get('ContentFetcher_fetchedContentRecords') ?: [];
64  $this->eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
65  }
66 
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  $isLanguageComparisonMode = $this->context->getDrawingConfiguration()->isLanguageComparisonMode();
79  $queryBuilder = $this->‪getQueryBuilder();
80  $result = $queryBuilder->executeQuery();
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 ($isLanguageComparisonMode) {
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 
118  public function ‪getUnusedRecords(): iterable
119  {
120  $unrendered = [];
121  $rememberer = GeneralUtility::makeInstance(RecordRememberer::class);
122  $languageId = $this->context->getDrawingConfiguration()->getSelectedLanguageId();
123  foreach ($this->‪getContentRecordsPerColumn(null, $languageId) as $contentRecordsInColumn) {
124  foreach ($contentRecordsInColumn as $contentRecord) {
125  $used = $rememberer->isRemembered((int)$contentRecord['uid']);
126  // A hook mentioned that this record is used somewhere, so this is in fact "rendered" already
127  $event = new ‪IsContentUsedOnPageLayoutEvent($contentRecord, $used, $this->context);
128  $event = $this->eventDispatcher->dispatch($event);
129  if (!$event->isRecordUsed()) {
130  $unrendered[] = $contentRecord;
131  }
132  }
133  }
134  return $unrendered;
135  }
136 
137  public function ‪getTranslationData(iterable $contentElements, int $language): array
138  {
139  if ($language === 0) {
140  return [];
141  }
142 
143  $languageTranslationInfo = $this->‪getRuntimeCache()->get('ContentFetcher_TranslationInfo_' . $language) ?: [];
144  if (empty($languageTranslationInfo)) {
145  $contentRecordsInDefaultLanguage = $this->‪getContentRecordsPerColumn(null, 0);
146  if (!empty($contentRecordsInDefaultLanguage)) {
147  $contentRecordsInDefaultLanguage = array_merge(...$contentRecordsInDefaultLanguage);
148  }
149  $untranslatedRecordUids = array_flip(
150  array_column(
151  // Eliminate records with "-1" as sys_language_uid since they can not be translated
152  array_filter($contentRecordsInDefaultLanguage, static function (array ‪$record): bool {
153  return (int)(‪$record['sys_language_uid'] ?? 0) !== -1;
154  }),
155  'uid'
156  )
157  );
158 
159  foreach ($contentElements as $contentElement) {
160  if ((int)$contentElement['sys_language_uid'] === -1) {
161  continue;
162  }
163  if ((int)$contentElement['l18n_parent'] === 0) {
164  $languageTranslationInfo['hasStandAloneContent'] = true;
165  $languageTranslationInfo['mode'] = 'free';
166  }
167  if ((int)$contentElement['l18n_parent'] > 0) {
168  $languageTranslationInfo['hasTranslations'] = true;
169  $languageTranslationInfo['mode'] = 'connected';
170  }
171  if ((int)$contentElement['l10n_source'] > 0) {
172  unset($untranslatedRecordUids[(int)$contentElement['l10n_source']]);
173  }
174  }
175  if (!isset($languageTranslationInfo['hasTranslations'])) {
176  $languageTranslationInfo['hasTranslations'] = false;
177  }
178  $languageTranslationInfo['untranslatedRecordUids'] = array_keys($untranslatedRecordUids);
179 
180  // Check for inconsistent translations, force "mixed" mode and dispatch a FlashMessage to user if such a case is encountered.
181  if (isset($languageTranslationInfo['hasStandAloneContent'])
182  && $languageTranslationInfo['hasTranslations']
183  ) {
184  $languageTranslationInfo['mode'] = 'mixed';
185 
186  // We do not want to show the staleTranslationWarning if allowInconsistentLanguageHandling is enabled
187  if (!$this->context->getDrawingConfiguration()->getAllowInconsistentLanguageHandling()) {
188  $siteLanguage = $this->context->getSiteLanguage($language);
189  $message = GeneralUtility::makeInstance(
190  FlashMessage::class,
191  $this->‪getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:staleTranslationWarning'),
192  sprintf($this->‪getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:staleTranslationWarningTitle'), $siteLanguage->getTitle()),
193  ContextualFeedbackSeverity::WARNING
194  );
195  $service = GeneralUtility::makeInstance(FlashMessageService::class);
196  $queue = $service->getMessageQueueByIdentifier();
197  $queue->addMessage($message);
198  }
199  }
200 
201  $this->‪getRuntimeCache()->set('ContentFetcher_TranslationInfo_' . $language, $languageTranslationInfo);
202  }
203  return $languageTranslationInfo;
204  }
205 
206  protected function ‪getQueryBuilder(): QueryBuilder
207  {
208  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
209  ->getQueryBuilderForTable('tt_content');
210  $queryBuilder->getRestrictions()
211  ->removeAll()
212  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
213  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->‪getBackendUser()->workspace));
214  $queryBuilder
215  ->select('*')
216  ->from('tt_content');
217 
218  $queryBuilder->andWhere(
219  $queryBuilder->expr()->eq(
220  'tt_content.pid',
221  $queryBuilder->createNamedParameter($this->context->getPageId(), ‪Connection::PARAM_INT)
222  )
223  );
224 
225  $sortBy = (string)(‪$GLOBALS['TCA']['tt_content']['ctrl']['sortby'] ?: ‪$GLOBALS['TCA']['tt_content']['ctrl']['default_sortby']);
226  foreach (‪QueryHelper::parseOrderBy($sortBy) as $orderBy) {
227  $queryBuilder->addOrderBy($orderBy[0], $orderBy[1]);
228  }
229 
230  $event = new ‪ModifyDatabaseQueryForContentEvent($queryBuilder, 'tt_content', $this->context->getPageId());
231  $event = $this->eventDispatcher->dispatch($event);
232  return $event->getQueryBuilder();
233  }
234 
235  protected function ‪getResult($result): array
236  {
237  ‪$output = [];
238  while ($row = $result->fetchAssociative()) {
239  BackendUtility::workspaceOL('tt_content', $row, -99, true);
240  if ($row && VersionState::tryFrom($row['t3ver_state'] ?? 0) !== VersionState::DELETE_PLACEHOLDER) {
241  ‪$output[] = $row;
242  }
243  }
244  return ‪$output;
245  }
246 
248  {
249  return ‪$GLOBALS['LANG'];
250  }
251 
253  {
254  return GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime');
255  }
256 
258  {
259  return ‪$GLOBALS['BE_USER'];
260  }
261 }
‪TYPO3\CMS\Core\Database\Query\QueryHelper\parseOrderBy
‪static array array[] parseOrderBy(string $input)
Definition: QueryHelper.php:44
‪TYPO3\CMS\Backend\View\Event\ModifyDatabaseQueryForContentEvent
Definition: ModifyDatabaseQueryForContentEvent.php:26
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getLanguageService
‪getLanguageService()
Definition: ContentFetcher.php:247
‪TYPO3\CMS\Core\Versioning\VersionState
‪VersionState
Definition: VersionState.php:22
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getRuntimeCache
‪getRuntimeCache()
Definition: ContentFetcher.php:252
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\$context
‪PageLayoutContext $context
Definition: ContentFetcher.php:54
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\__construct
‪__construct(PageLayoutContext $pageLayoutContext)
Definition: ContentFetcher.php:60
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getUnusedRecords
‪getUnusedRecords()
Definition: ContentFetcher.php:118
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher
Definition: ContentFetcher.php:53
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getQueryBuilder
‪getQueryBuilder()
Definition: ContentFetcher.php:206
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\$fetchedContentRecords
‪array $fetchedContentRecords
Definition: ContentFetcher.php:56
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Backend\View\Event\IsContentUsedOnPageLayoutEvent
Definition: IsContentUsedOnPageLayoutEvent.php:26
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getBackendUser
‪getBackendUser()
Definition: ContentFetcher.php:257
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Backend\View\BackendLayout
Definition: BackendLayout.php:18
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:27
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\View\PageLayoutContext
Definition: PageLayoutContext.php:42
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getTranslationData
‪getTranslationData(iterable $contentElements, int $language)
Definition: ContentFetcher.php:137
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getFlatContentRecords
‪getFlatContentRecords(int $languageId)
Definition: ContentFetcher.php:109
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getResult
‪getResult($result)
Definition: ContentFetcher.php:235
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: ContentFetcher.php:58
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪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:73
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39