‪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 {
57  protected ‪$context;
58 
62  protected ‪$fetchedContentRecords = [];
63 
64  protected EventDispatcherInterface ‪$eventDispatcher;
65 
66  public function ‪__construct(‪PageLayoutContext $pageLayoutContext)
67  {
68  $this->context = $pageLayoutContext;
69  $this->fetchedContentRecords = $this->‪getRuntimeCache()->get('ContentFetcher_fetchedContentRecords') ?: [];
70  $this->eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
71  }
72 
79  public function ‪getContentRecordsPerColumn(?int $columnNumber = null, ?int $languageId = null): array
80  {
81  $languageId = $languageId ?? $this->context->getSiteLanguage()->getLanguageId();
82 
83  if (empty($this->fetchedContentRecords)) {
84  $isLanguageMode = $this->context->getDrawingConfiguration()->getLanguageMode();
85  $queryBuilder = $this->‪getQueryBuilder();
86  $result = $queryBuilder->executeQuery();
87  $records = $this->‪getResult($result);
88  foreach ($records as ‪$record) {
89  $recordLanguage = (int)‪$record['sys_language_uid'];
90  $recordColumnNumber = (int)‪$record['colPos'];
91  if ($recordLanguage === -1) {
92  // Record is set to "all languages", place it according to view mode.
93  if ($isLanguageMode) {
94  // Force the record to only be shown in default language in "Languages" view mode.
95  $recordLanguage = 0;
96  } else {
97  // Force the record to be shown in the currently active language in "Columns" view mode.
98  $recordLanguage = $languageId;
99  }
100  }
101  $this->fetchedContentRecords[$recordLanguage][$recordColumnNumber][] = ‪$record;
102  }
103  $this->‪getRuntimeCache()->set('ContentFetcher_fetchedContentRecords', $this->fetchedContentRecords);
104  }
105 
106  $contentByLanguage = &$this->fetchedContentRecords[$languageId];
107 
108  if ($columnNumber === null) {
109  return $contentByLanguage ?? [];
110  }
111 
112  return $contentByLanguage[$columnNumber] ?? [];
113  }
114 
115  public function ‪getFlatContentRecords(int $languageId): iterable
116  {
117  $contentRecords = $this->‪getContentRecordsPerColumn(null, $languageId);
118  return empty($contentRecords) ? [] : array_merge(...$contentRecords);
119  }
120 
124  public function ‪getUnusedRecords(): iterable
125  {
126  $unrendered = [];
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  $event = new IsContentUsedOnPageLayoutEvent($contentRecord, $used, $this->context);
134  $event = $this->eventDispatcher->dispatch($event);
135  if (!$event->isRecordUsed()) {
136  $unrendered[] = $contentRecord;
137  }
138  }
139  }
140  return $unrendered;
141  }
142 
143  public function ‪getTranslationData(iterable $contentElements, int $language): array
144  {
145  if ($language === 0) {
146  return [];
147  }
148 
149  $languageTranslationInfo = $this->‪getRuntimeCache()->get('ContentFetcher_TranslationInfo_' . $language) ?: [];
150  if (empty($languageTranslationInfo)) {
151  $contentRecordsInDefaultLanguage = $this->‪getContentRecordsPerColumn(null, 0);
152  if (!empty($contentRecordsInDefaultLanguage)) {
153  $contentRecordsInDefaultLanguage = array_merge(...$contentRecordsInDefaultLanguage);
154  }
155  $untranslatedRecordUids = array_flip(
156  array_column(
157  // Eliminate records with "-1" as sys_language_uid since they can not be translated
158  array_filter($contentRecordsInDefaultLanguage, static function (array ‪$record): bool {
159  return (int)(‪$record['sys_language_uid'] ?? 0) !== -1;
160  }),
161  'uid'
162  )
163  );
164 
165  foreach ($contentElements as $contentElement) {
166  if ((int)$contentElement['sys_language_uid'] === -1) {
167  continue;
168  }
169  if ((int)$contentElement['l18n_parent'] === 0) {
170  $languageTranslationInfo['hasStandAloneContent'] = true;
171  $languageTranslationInfo['mode'] = 'free';
172  }
173  if ((int)$contentElement['l18n_parent'] > 0) {
174  $languageTranslationInfo['hasTranslations'] = true;
175  $languageTranslationInfo['mode'] = 'connected';
176  }
177  if ((int)$contentElement['l10n_source'] > 0) {
178  unset($untranslatedRecordUids[(int)$contentElement['l10n_source']]);
179  }
180  }
181  if (!isset($languageTranslationInfo['hasTranslations'])) {
182  $languageTranslationInfo['hasTranslations'] = false;
183  }
184  $languageTranslationInfo['untranslatedRecordUids'] = array_keys($untranslatedRecordUids);
185 
186  // Check for inconsistent translations, force "mixed" mode and dispatch a FlashMessage to user if such a case is encountered.
187  if (isset($languageTranslationInfo['hasStandAloneContent'])
188  && $languageTranslationInfo['hasTranslations']
189  ) {
190  $languageTranslationInfo['mode'] = 'mixed';
191 
192  // We do not want to show the staleTranslationWarning if allowInconsistentLanguageHandling is enabled
193  $allowInconsistentLanguageHandling = (bool)(BackendUtility::getPagesTSconfig($this->context->getPageId())['mod.']['web_layout.']['allowInconsistentLanguageHandling'] ?? false);
194  if (!$this->context->getDrawingConfiguration()->getAllowInconsistentLanguageHandling()) {
195  $siteLanguage = $this->context->getSiteLanguage($language);
196  $message = GeneralUtility::makeInstance(
197  FlashMessage::class,
198  $this->‪getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:staleTranslationWarning'),
199  sprintf($this->‪getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:staleTranslationWarningTitle'), $siteLanguage->getTitle()),
200  ContextualFeedbackSeverity::WARNING
201  );
202  $service = GeneralUtility::makeInstance(FlashMessageService::class);
203  $queue = $service->getMessageQueueByIdentifier();
204  $queue->addMessage($message);
205  }
206  }
207 
208  $this->‪getRuntimeCache()->set('ContentFetcher_TranslationInfo_' . $language, $languageTranslationInfo);
209  }
210  return $languageTranslationInfo;
211  }
212 
213  protected function ‪getQueryBuilder(): QueryBuilder
214  {
215  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
216  ->getQueryBuilderForTable('tt_content');
217  $queryBuilder->getRestrictions()
218  ->removeAll()
219  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
220  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->‪getBackendUser()->workspace));
221  $queryBuilder
222  ->select('*')
223  ->from('tt_content');
224 
225  $queryBuilder->andWhere(
226  $queryBuilder->expr()->eq(
227  'tt_content.pid',
228  $queryBuilder->createNamedParameter($this->context->getPageId(), ‪Connection::PARAM_INT)
229  )
230  );
231 
232  $sortBy = (string)(‪$GLOBALS['TCA']['tt_content']['ctrl']['sortby'] ?: ‪$GLOBALS['TCA']['tt_content']['ctrl']['default_sortby']);
233  foreach (‪QueryHelper::parseOrderBy($sortBy) as $orderBy) {
234  $queryBuilder->addOrderBy($orderBy[0], $orderBy[1]);
235  }
236 
237  $event = new ModifyDatabaseQueryForContentEvent($queryBuilder, 'tt_content', $this->context->getPageId());
238  $event = $this->eventDispatcher->dispatch($event);
239  return $event->getQueryBuilder();
240  }
241 
242  protected function ‪getResult($result): array
243  {
244  ‪$output = [];
245  while ($row = $result->fetchAssociative()) {
246  BackendUtility::workspaceOL('tt_content', $row, -99, true);
247  if ($row && VersionState::tryFrom($row['t3ver_state'] ?? 0) !== VersionState::DELETE_PLACEHOLDER) {
248  ‪$output[] = $row;
249  }
250  }
251  return ‪$output;
252  }
253 
254  protected function ‪getLanguageService(): ‪LanguageService
255  {
256  return ‪$GLOBALS['LANG'];
257  }
258 
259  protected function ‪getRuntimeCache(): ‪VariableFrontend
260  {
261  return GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime');
262  }
263 
265  {
266  return ‪$GLOBALS['BE_USER'];
267  }
268 }
‪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:50
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getLanguageService
‪getLanguageService()
Definition: ContentFetcher.php:252
‪TYPO3\CMS\Core\Versioning\VersionState
‪VersionState
Definition: VersionState.php:22
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getRuntimeCache
‪getRuntimeCache()
Definition: ContentFetcher.php:257
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\$context
‪PageLayoutContext $context
Definition: ContentFetcher.php:56
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\__construct
‪__construct(PageLayoutContext $pageLayoutContext)
Definition: ContentFetcher.php:64
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getUnusedRecords
‪getUnusedRecords()
Definition: ContentFetcher.php:122
‪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:211
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\$fetchedContentRecords
‪array $fetchedContentRecords
Definition: ContentFetcher.php:60
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Backend\View\Event\IsContentUsedOnPageLayoutEvent
Definition: IsContentUsedOnPageLayoutEvent.php:26
‪TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
Definition: VariableFrontend.php:25
‪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:262
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:60
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Backend\View\BackendLayout
Definition: BackendLayout.php:16
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:39
‪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:43
‪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:141
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getFlatContentRecords
‪getFlatContentRecords(int $languageId)
Definition: ContentFetcher.php:113
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\getResult
‪getResult($result)
Definition: ContentFetcher.php:240
‪TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: ContentFetcher.php:62
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:48
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪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:77
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39