‪TYPO3CMS  ‪main
SuggestWizardDefaultReceiver.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 
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
27 use TYPO3\CMS\Core\Imaging\IconSize;
32 
40 {
46  protected ‪$table = '';
47 
54  protected ‪$mmForeignTable = '';
55 
61  protected ‪$config = [];
62 
68  protected ‪$allowedPages = [];
69 
75  protected ‪$maxItems = 10;
76 
80  protected ‪$params = [];
81 
85  protected ‪$iconFactory;
86 
90  protected ‪$queryBuilder;
91 
98  public function ‪__construct(‪$table, ‪$config)
99  {
100  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
101  $this->queryBuilder = $this->‪getQueryBuilderForTable(‪$table);
102  $this->queryBuilder->getRestrictions()
103  ->removeAll()
104  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
105  // if table is versionized, only get the records from the Live Workspace
106  // the overlay itself of WS-records is done below
107  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, 0));
108  $this->table = ‪$table;
109  $this->config = ‪$config;
110  // get a list of all the pages that should be looked on
111  if (isset(‪$config['pidList'])) {
112  $pageIds = ‪GeneralUtility::intExplode(',', (string)‪$config['pidList'], true);
113  $depth = (int)(‪$config['pidDepth'] ?? 0);
114  $availablePageIds = $this->‪getAvailablePageIds($pageIds, $depth);
115  $this->allowedPages = array_unique(array_merge($this->allowedPages, ...$availablePageIds));
116  }
117  if (isset(‪$config['maxItemsInResultList'])) {
118  $this->maxItems = ‪$config['maxItemsInResultList'];
119  }
120  $backendUser = $this->‪getBackendUser();
121  $backendUser->initializeWebmountsForElementBrowser();
122  if ($this->table === 'pages') {
123  $this->queryBuilder->andWhere(
124  ‪QueryHelper::stripLogicalOperatorPrefix($backendUser->getPagePermsClause(‪Permission::PAGE_SHOW)),
125  $this->queryBuilder->expr()->eq('sys_language_uid', 0)
126  );
127  }
128  if (isset(‪$config['addWhere'])) {
129  $this->queryBuilder->andWhere(
131  );
132  }
133  }
134 
147  public function ‪queryTable(&‪$params, $recursionCounter = 0)
148  {
149  $maxQueryResults = 50;
150  $rows = [];
151  $this->params = &‪$params;
152  $start = $recursionCounter * $maxQueryResults;
153  $this->‪prepareSelectStatement();
154 
155  $selectQueryBuilder = clone ‪$this->queryBuilder;
156  $selectQueryBuilder = $this->‪prepareOrderByStatement($selectQueryBuilder);
157  $result = $selectQueryBuilder->select($this->table . '.*')
158  ->from($this->table)
159  ->setFirstResult($start)
160  ->setMaxResults($maxQueryResults)
161  ->executeQuery();
162 
163  $countQueryBuilder = clone ‪$this->queryBuilder;
164  $allRowsCount = $countQueryBuilder
165  ->count($this->table . '.uid')
166  ->from($this->table)
167  ->executeQuery()
168  ->fetchOne();
169  if ($allRowsCount) {
170  while ($row = $result->fetchAssociative()) {
171  // check if we already have collected the maximum number of records
172  if (count($rows) > $this->maxItems) {
173  break;
174  }
175  $this->‪manipulateRecord($row);
176  $this->‪makeWorkspaceOverlay($row);
177  // check if the user has access to the record
178  if (!$this->‪checkRecordAccess($row, $row['uid'])) {
179  continue;
180  }
181  $icon = $this->iconFactory->getIconForRecord($this->table, $row, IconSize::SMALL);
182  ‪$uid = ($row['t3ver_oid'] ?? 0) > 0 ? $row['t3ver_oid'] : $row['uid'];
183  $path = $this->‪getRecordPath($row, ‪$uid);
184  $label = $this->‪getLabel($row);
185  $entry = [
186  'table' => $this->mmForeignTable ?: ‪$this->table,
187  'label' => strip_tags($label),
188  'path' => $path,
189  'uid' => ‪$uid,
190  'icon' => [
191  'identifier' => $icon->getIdentifier(),
192  'overlay' => $icon->getOverlayIcon()?->getIdentifier(),
193  ],
194  ];
195  $rows[$this->table . '_' . ‪$uid] = $this->‪renderRecord($row, $entry);
196  }
197 
198  // if there are less records than we need, call this function again to get more records
199  if (count($rows) < $this->maxItems && $allRowsCount >= $maxQueryResults && $recursionCounter < $this->maxItems) {
200  $tmp = ‪self::queryTable(‪$params, ++$recursionCounter);
201  $rows = array_merge($tmp, $rows);
202  }
203  }
204  return $rows;
205  }
206 
211  protected function ‪prepareSelectStatement()
212  {
213  $expressionBuilder = $this->queryBuilder->expr();
214  $searchString = $this->params['value'];
215  if ($searchString !== '') {
216  $splitStrings = $this->‪splitSearchString($searchString);
217  $constraints = [];
218  foreach ($splitStrings as $splitString) {
219  $constraints[] = $this->‪buildConstraintBlock($splitString);
220  }
221  foreach ($constraints as $constraint) {
222  $this->queryBuilder->andWhere($expressionBuilder->and($constraint));
223  }
224  }
225  if (!empty($this->allowedPages)) {
226  $pidList = array_map(intval(...), $this->allowedPages);
227  if (!empty($pidList)) {
228  $this->queryBuilder->andWhere(
229  $expressionBuilder->in('pid', $pidList)
230  );
231  }
232  }
233  // add an additional search condition comment
234  if (isset($this->config['searchCondition']) && $this->config['searchCondition'] !== '') {
235  $this->queryBuilder->andWhere(‪QueryHelper::stripLogicalOperatorPrefix($this->config['searchCondition']));
236  }
237  }
238 
244  protected function ‪buildConstraintBlock(string $searchString)
245  {
246  $expressionBuilder = $this->queryBuilder->expr();
247  $selectParts = $expressionBuilder->or();
248  if (‪MathUtility::canBeInterpretedAsInteger($searchString) && (int)$searchString > 0) {
249  $selectParts = $selectParts->with($expressionBuilder->eq('uid', (int)$searchString));
250  }
251  $searchWholePhrase = !isset($this->config['searchWholePhrase']) || $this->config['searchWholePhrase'];
252  $likeCondition = ($searchWholePhrase ? '%' : '') . $this->queryBuilder->escapeLikeWildcards($searchString) . '%';
253  // Search in all fields given by label or label_alt
254  $selectFieldsList = (‪$GLOBALS['TCA'][‪$this->table]['ctrl']['label'] ?? '') . ',' . (‪$GLOBALS['TCA'][$this->table]['ctrl']['label_alt'] ?? '') . ',' . ($this->config['additionalSearchFields'] ?? '');
255  $selectFields = ‪GeneralUtility::trimExplode(',', $selectFieldsList, true);
256  $selectFields = array_unique($selectFields);
257  foreach ($selectFields as $field) {
258  $selectParts = $selectParts->with($expressionBuilder->like($field, $this->queryBuilder->createPositionalParameter($likeCondition)));
259  }
260 
261  return $selectParts;
262  }
263 
269  protected function ‪splitSearchString(string $searchString): array
270  {
271  return str_getcsv($searchString, ' ');
272  }
273 
281  protected function ‪getAvailablePageIds(array $entryPointPageIds, int $depth = 0): array
282  {
283  if ($depth === 0) {
284  return $entryPointPageIds;
285  }
286  $pageIds = [];
287  $repository = GeneralUtility::makeInstance(PageTreeRepository::class);
288  $pages = $repository->getFlattenedPages($entryPointPageIds, $depth);
289  foreach ($pages as $page) {
290  $pageIds[] = (int)$page['uid'];
291  }
292  return $pageIds;
293  }
294 
299  protected function ‪prepareOrderByStatement(QueryBuilder ‪$queryBuilder): QueryBuilder
300  {
301  if (empty($this->config['orderBy'])) {
302  ‪$queryBuilder->addOrderBy(‪$GLOBALS['TCA'][$this->table]['ctrl']['label']);
303  } else {
304  foreach (‪QueryHelper::parseOrderBy($this->config['orderBy']) as $orderPair) {
305  [$fieldName, $order] = $orderPair;
306  ‪$queryBuilder->addOrderBy($fieldName, $order);
307  }
308  }
310  }
311 
317  protected function ‪manipulateRecord(&$row) {}
318 
326  protected function ‪checkRecordAccess($row, ‪$uid)
327  {
328  $backendUser = $this->‪getBackendUser();
329  $retValue = true;
330  ‪$table = $this->mmForeignTable ?: ‪$this->table;
331  if (‪$table === 'pages') {
332  if (!BackendUtility::readPageAccess(‪$uid, $backendUser->getPagePermsClause(‪Permission::PAGE_SHOW))) {
333  $retValue = false;
334  }
335  } elseif (isset(‪$GLOBALS['TCA'][‪$table]['ctrl']['is_static']) && (bool)‪$GLOBALS['TCA'][‪$table]['ctrl']['is_static']) {
336  $retValue = true;
337  } else {
338  if (!is_array(BackendUtility::readPageAccess($row['pid'], $backendUser->getPagePermsClause(‪Permission::PAGE_SHOW)))) {
339  $retValue = false;
340  }
341  }
342  return $retValue;
343  }
344 
350  protected function ‪makeWorkspaceOverlay(&$row)
351  {
352  // Check for workspace-versions
353  if ($this->‪getBackendUser()->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($this->table)) {
354  BackendUtility::workspaceOL($this->mmForeignTable ?: $this->table, $row);
355  }
356  }
357 
368  protected function ‪getRecordPath(&$row, ‪$uid)
369  {
370  $titleLimit = max($this->config['maxPathTitleLength'] ?? 0, 0);
371  if (($this->mmForeignTable ?: $this->table) === 'pages') {
372  $path = BackendUtility::getRecordPath(‪$uid, '', $titleLimit);
373  // For pages we only want the first (n-1) parts of the path,
374  // because the n-th part is the page itself
375  $path = substr($path, 0, (int)strrpos($path, '/', -2)) . '/';
376  } else {
377  $path = BackendUtility::getRecordPath($row['pid'], '', $titleLimit);
378  }
379  return $path;
380  }
381 
388  protected function ‪getLabel($row)
389  {
390  return BackendUtility::getRecordTitle($this->mmForeignTable ?: $this->table, $row, true);
391  }
392 
402  protected function ‪renderRecord($row, $entry)
403  {
404  // Call renderlet if available (normal pages etc. usually don't have one)
405  if (($this->config['renderFunc'] ?? '') != '') {
406  ‪$params = [
407  'table' => ‪$this->table,
408  'uid' => $row['uid'],
409  'row' => $row,
410  'entry' => &$entry,
411  ];
412  GeneralUtility::callUserFunction($this->config['renderFunc'], ‪$params, $this);
413  }
414  return $entry;
415  }
416 
417  protected function ‪getLanguageService(): ‪LanguageService
418  {
419  return ‪$GLOBALS['LANG'];
420  }
421 
423  {
424  return ‪$GLOBALS['BE_USER'];
425  }
426 
431  protected function ‪getQueryBuilderForTable(‪$table)
432  {
433  return GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(‪$table);
434  }
435 }
‪TYPO3\CMS\Core\Database\Query\QueryHelper\parseOrderBy
‪static array array[] parseOrderBy(string $input)
Definition: QueryHelper.php:44
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\$config
‪array $config
Definition: SuggestWizardDefaultReceiver.php:58
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\getRecordPath
‪string getRecordPath(&$row, $uid)
Definition: SuggestWizardDefaultReceiver.php:360
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\getAvailablePageIds
‪array getAvailablePageIds(array $entryPointPageIds, int $depth=0)
Definition: SuggestWizardDefaultReceiver.php:273
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\renderRecord
‪array renderRecord($row, $entry)
Definition: SuggestWizardDefaultReceiver.php:394
‪TYPO3\CMS\Backend\Form\Wizard
Definition: SuggestWizardDefaultReceiver.php:16
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\makeWorkspaceOverlay
‪makeWorkspaceOverlay(&$row)
Definition: SuggestWizardDefaultReceiver.php:342
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\$iconFactory
‪IconFactory $iconFactory
Definition: SuggestWizardDefaultReceiver.php:78
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\queryTable
‪array queryTable(&$params, $recursionCounter=0)
Definition: SuggestWizardDefaultReceiver.php:139
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\getQueryBuilderForTable
‪QueryBuilder getQueryBuilderForTable($table)
Definition: SuggestWizardDefaultReceiver.php:423
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\$mmForeignTable
‪string $mmForeignTable
Definition: SuggestWizardDefaultReceiver.php:52
‪TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression
Definition: CompositeExpression.php:27
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\splitSearchString
‪splitSearchString(string $searchString)
Definition: SuggestWizardDefaultReceiver.php:261
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\$params
‪array $params
Definition: SuggestWizardDefaultReceiver.php:74
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\$queryBuilder
‪QueryBuilder $queryBuilder
Definition: SuggestWizardDefaultReceiver.php:82
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\getLabel
‪string getLabel($row)
Definition: SuggestWizardDefaultReceiver.php:380
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:61
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\checkRecordAccess
‪bool checkRecordAccess($row, $uid)
Definition: SuggestWizardDefaultReceiver.php:318
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\getLanguageService
‪getLanguageService()
Definition: SuggestWizardDefaultReceiver.php:409
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\$allowedPages
‪array $allowedPages
Definition: SuggestWizardDefaultReceiver.php:64
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\$table
‪string $table
Definition: SuggestWizardDefaultReceiver.php:45
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Core\Database\Query\QueryHelper\stripLogicalOperatorPrefix
‪static string stripLogicalOperatorPrefix(string $constraint)
Definition: QueryHelper.php:171
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\getBackendUser
‪getBackendUser()
Definition: SuggestWizardDefaultReceiver.php:414
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Backend\Tree\Repository\PageTreeRepository
Definition: PageTreeRepository.php:40
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\prepareSelectStatement
‪prepareSelectStatement()
Definition: SuggestWizardDefaultReceiver.php:203
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\prepareOrderByStatement
‪prepareOrderByStatement(QueryBuilder $queryBuilder)
Definition: SuggestWizardDefaultReceiver.php:291
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\$maxItems
‪int $maxItems
Definition: SuggestWizardDefaultReceiver.php:70
‪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\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:751
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\manipulateRecord
‪manipulateRecord(&$row)
Definition: SuggestWizardDefaultReceiver.php:309
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver
Definition: SuggestWizardDefaultReceiver.php:40
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\buildConstraintBlock
‪string TYPO3 CMS Core Database Query Expression CompositeExpression buildConstraintBlock(string $searchString)
Definition: SuggestWizardDefaultReceiver.php:236
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:817
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver\__construct
‪__construct($table, $config)
Definition: SuggestWizardDefaultReceiver.php:90
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39