‪TYPO3CMS  ‪main
RecordsContentObject.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Psr\Log\LogLevel;
23 
28 {
34  protected ‪$itemArray = [];
35 
41  protected ‪$data = [];
42 
43  public function ‪__construct(
44  private readonly ‪TimeTracker $timeTracker,
45  ) {}
46 
53  public function ‪render($conf = [])
54  {
55  // Reset items and data
56  $this->itemArray = [];
57  $this->data = [];
58  $frontendController = $this->‪getTypoScriptFrontendController();
59 
60  $theValue = '';
61  $originalRec = $frontendController->currentRecord;
62  // If the currentRecord is set, we register that this record has invoked this function.
63  // It should not be allowed to do this again then.
64  if ($originalRec) {
65  if (!($frontendController->recordRegister[$originalRec] ?? false)) {
66  $frontendController->recordRegister[$originalRec] = 0;
67  }
68  ++$frontendController->recordRegister[$originalRec];
69  }
70 
71  $tables = (string)$this->cObj->stdWrapValue('tables', $conf ?? []);
72  if ($tables !== '') {
73  $tablesArray = array_unique(‪GeneralUtility::trimExplode(',', $tables, true));
74  // Add tables which have a configuration (note that this may create duplicate entries)
75  if (is_array($conf['conf.'] ?? false)) {
76  foreach ($conf['conf.'] as $key => $value) {
77  if (!str_ends_with($key, '.') && !in_array($key, $tablesArray)) {
78  $tablesArray[] = $key;
79  }
80  }
81  }
82 
83  // Get the data, depending on collection method.
84  // Property "source" is considered more precise and thus takes precedence over "categories"
85  $source = (string)$this->cObj->stdWrapValue('source', $conf ?? []);
86  $categories = (string)$this->cObj->stdWrapValue('categories', $conf ?? []);
87  if ($source !== '') {
88  $this->‪collectRecordsFromSource($source, $tablesArray);
89  } elseif ($categories !== '') {
90  $relationField = (string)$this->cObj->stdWrapValue('relation', $conf['categories.'] ?? []);
91  $this->‪collectRecordsFromCategories($categories, $tablesArray, $relationField);
92  }
93  if (!empty($this->itemArray)) {
94  ‪$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class, $frontendController);
95  ‪$cObj->‪setParent($this->cObj->data, $this->cObj->currentRecord);
96  $this->cObj->currentRecordNumber = 0;
97  $pageRepository = $this->‪getPageRepository();
98  foreach ($this->itemArray as $val) {
99  $row = $this->data[$val['table']][$val['id']] ?? null;
100  if ($row === null) {
101  continue;
102  }
103  // Perform overlays if necessary (records coming from category collections are already overlaid)
104  if ($source) {
105  // Versioning preview
106  $pageRepository->versionOL($val['table'], $row);
107  // Language overlay
108  if (is_array($row)) {
109  $row = $pageRepository->getLanguageOverlay($val['table'], $row);
110  }
111  }
112  // Might be unset during the overlay process
113  if (is_array($row)
114  && $this->‪isRecordsPageAccessible($val['table'], $row, $conf ?? [])
115  && !($frontendController->recordRegister[$val['table'] . ':' . $val['id']] ?? false)
116  ) {
117  $renderObjName = ($conf['conf.'][$val['table']] ?? false) ? $conf['conf.'][$val['table']] : '<' . $val['table'];
118  $renderObjKey = ($conf['conf.'][$val['table']] ?? false) ? 'conf.' . $val['table'] : '';
119  $renderObjConf = ($conf['conf.'][$val['table'] . '.'] ?? false) ? $conf['conf.'][$val['table'] . '.'] : [];
120  $this->cObj->currentRecordNumber++;
121  ‪$cObj->parentRecordNumber = $this->cObj->currentRecordNumber;
122  $frontendController->currentRecord = $val['table'] . ':' . $val['id'];
123  $this->cObj->‪lastChanged($row['tstamp'] ?? 0);
124  ‪$cObj->‪setRequest($this->request);
125  ‪$cObj->‪start($row, $val['table']);
126  $tmpValue = ‪$cObj->‪cObjGetSingle($renderObjName, $renderObjConf, $renderObjKey);
127  $theValue .= $tmpValue;
128  }
129  }
130  }
131  }
132  $wrap = $this->cObj->stdWrapValue('wrap', $conf);
133  if ($wrap) {
134  $theValue = $this->cObj->wrap($theValue, $wrap);
135  }
136  if (isset($conf['stdWrap.'])) {
137  $theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
138  }
139  // Restore
140  $frontendController->currentRecord = $originalRec;
141  if ($originalRec) {
142  --$frontendController->recordRegister[$originalRec];
143  }
144  return $theValue;
145  }
146 
150  protected function ‪isRecordsPageAccessible(string $table, array $row, array $conf): bool
151  {
152  $pageId = (int)($table === 'pages' ? $row['uid'] : $row['pid']);
153  if ($pageId === $this->request->getAttribute('frontend.page.information')->getId()) {
154  // Access to current page has already been checked before rendering this content object.
155  return true;
156  }
157  if ($this->cObj->stdWrapValue('dontCheckPid', $conf)) {
158  return true;
159  }
160  $validPageId = $this->‪getPageRepository()->filterAccessiblePageIds([$pageId]);
161  return $validPageId !== [];
162  }
163 
170  protected function ‪collectRecordsFromSource($source, array $tables)
171  {
172  $loadDB = GeneralUtility::makeInstance(RelationHandler::class);
173  $loadDB->start($source, implode(',', $tables));
174  foreach ($loadDB->tableArray as $table => $v) {
175  if (isset(‪$GLOBALS['TCA'][$table])) {
176  $constraints = $this->‪getPageRepository()->getDefaultConstraints($table);
177  if ($constraints !== []) {
178  $loadDB->additionalWhere[$table] = implode(' AND ', $constraints);
179  }
180  }
181  }
182  $this->data = $loadDB->getFromDB();
183  reset($loadDB->itemArray);
184  $this->itemArray = $loadDB->itemArray;
185  }
186 
194  protected function ‪collectRecordsFromCategories($selectedCategories, array $tables, $relationField)
195  {
196  $selectedCategories = array_unique(‪GeneralUtility::intExplode(',', $selectedCategories, true));
197 
198  // Loop on all selected tables
199  foreach ($tables as $table) {
200  // Get the records for each selected category
201  $tableRecords = [];
202  $categoriesPerRecord = [];
203  foreach ($selectedCategories as $aCategory) {
204  try {
205  $collection = ‪CategoryCollection::load(
206  $aCategory,
207  true,
208  $table,
209  $relationField
210  );
211  if ($collection->count() > 0) {
212  // Add items to the collection of records for the current table
213  foreach ($collection as $item) {
214  $tableRecords[$item['uid']] = $item;
215  // Keep track of all categories a given item belongs to
216  if (!isset($categoriesPerRecord[$item['uid']])) {
217  $categoriesPerRecord[$item['uid']] = [];
218  }
219  $categoriesPerRecord[$item['uid']][] = $aCategory;
220  }
221  }
222  } catch (\Exception $e) {
223  $message = sprintf(
224  'Could not get records for category id %d. Error: %s (%d)',
225  $aCategory,
226  $e->getMessage(),
227  $e->getCode()
228  );
229  $this->timeTracker->setTSlogMessage($message, LogLevel::WARNING);
230  }
231  }
232  // Store the resulting records into the itemArray and data results array
233  if (!empty($tableRecords)) {
234  $this->data[$table] = [];
235  foreach ($tableRecords as ‪$record) {
236  $this->itemArray[] = [
237  'id' => ‪$record['uid'],
238  'table' => $table,
239  ];
240  // Add to the record the categories it belongs to
241  ‪$record['_categories'] = implode(',', $categoriesPerRecord[‪$record['uid']]);
242  $this->data[$table][‪$record['uid']] = ‪$record;
243  }
244  }
245  }
246  }
247 }
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\start
‪start($data, $table='')
Definition: ContentObjectRenderer.php:436
‪TYPO3\CMS\Frontend\ContentObject\RecordsContentObject\collectRecordsFromCategories
‪collectRecordsFromCategories($selectedCategories, array $tables, $relationField)
Definition: RecordsContentObject.php:192
‪TYPO3\CMS\Core\Database\RelationHandler
Definition: RelationHandler.php:36
‪TYPO3\CMS\Frontend\ContentObject
Definition: AbstractContentObject.php:18
‪TYPO3\CMS\Frontend\ContentObject\RecordsContentObject\collectRecordsFromSource
‪collectRecordsFromSource($source, array $tables)
Definition: RecordsContentObject.php:168
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\setParent
‪setParent($data, $currentRecord)
Definition: ContentObjectRenderer.php:468
‪TYPO3\CMS\Frontend\Category\Collection\CategoryCollection\load
‪static CategoryCollection load($id, $fillItems=false, $tableName='', $fieldName='')
Definition: CategoryCollection.php:72
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject\getPageRepository
‪getPageRepository()
Definition: AbstractContentObject.php:88
‪TYPO3\CMS\Frontend\ContentObject\RecordsContentObject\isRecordsPageAccessible
‪isRecordsPageAccessible(string $table, array $row, array $conf)
Definition: RecordsContentObject.php:148
‪TYPO3\CMS\Frontend\ContentObject\RecordsContentObject\__construct
‪__construct(private readonly TimeTracker $timeTracker,)
Definition: RecordsContentObject.php:41
‪TYPO3\CMS\Frontend\ContentObject\RecordsContentObject\$itemArray
‪array $itemArray
Definition: RecordsContentObject.php:33
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\cObjGetSingle
‪string cObjGetSingle(string $name, $conf, $TSkey='__')
Definition: ContentObjectRenderer.php:554
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Frontend\ContentObject\RecordsContentObject\render
‪string render($conf=[])
Definition: RecordsContentObject.php:51
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject
Definition: AbstractContentObject.php:31
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject\$cObj
‪ContentObjectRenderer $cObj
Definition: AbstractContentObject.php:39
‪TYPO3\CMS\Frontend\ContentObject\RecordsContentObject\$data
‪array $data
Definition: RecordsContentObject.php:39
‪TYPO3\CMS\Core\Resource\Exception
Definition: Exception.php:21
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject\getTypoScriptFrontendController
‪getTypoScriptFrontendController()
Definition: AbstractContentObject.php:79
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\lastChanged
‪lastChanged($tstamp)
Definition: ContentObjectRenderer.php:990
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\setRequest
‪setRequest(ServerRequestInterface $request)
Definition: ContentObjectRenderer.php:373
‪TYPO3\CMS\Frontend\ContentObject\RecordsContentObject
Definition: RecordsContentObject.php:28
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:34
‪TYPO3\CMS\Frontend\Category\Collection\CategoryCollection
Definition: CategoryCollection.php:33
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822