‪TYPO3CMS  10.4
SuggestWizardController.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\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
27 
33 {
41  public function ‪searchAction(ServerRequestInterface $request): ResponseInterface
42  {
43  $parsedBody = $request->getParsedBody();
44 
45  $search = $parsedBody['value'] ?? null;
46  $tableName = $parsedBody['tableName'] ?? null;
47  $fieldName = $parsedBody['fieldName'] ?? null;
48  $uid = $parsedBody['uid'] ?? null;
49  $pid = isset($parsedBody['pid']) ? (int)$parsedBody['pid'] : 0;
50  $dataStructureIdentifier = '' ?? null;
51  if (!empty($parsedBody['dataStructureIdentifier'])) {
52  $dataStructureIdentifier = $parsedBody['dataStructureIdentifier'];
53  }
54  $flexFormSheetName = $parsedBody['flexFormSheetName'] ?? null;
55  $flexFormFieldName = $parsedBody['flexFormFieldName'] ?? null;
56  $flexFormContainerName = $parsedBody['flexFormContainerName'] ?? null;
57  $flexFormContainerFieldName = $parsedBody['flexFormContainerFieldName'] ?? null;
58 
59  // Determine TCA config of field
60  if (empty($dataStructureIdentifier)) {
61  // Normal columns field
62  $fieldConfig = ‪$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
63  $fieldNameInPageTsConfig = $fieldName;
64 
65  // With possible columnsOverrides
66  $row = ‪BackendUtility::getRecord($tableName, $uid) ?? [];
67  $recordType = ‪BackendUtility::getTCAtypeValue($tableName, $row);
68  $columnsOverridesConfigOfField = ‪$GLOBALS['TCA'][$tableName]['types'][$recordType]['columnsOverrides'][$fieldName]['config'] ?? null;
69  if ($columnsOverridesConfigOfField) {
70  ‪ArrayUtility::mergeRecursiveWithOverrule($fieldConfig, $columnsOverridesConfigOfField);
71  }
72  } else {
73  // A flex flex form field
74  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
75  $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
76  if (empty($flexFormContainerFieldName)) {
77  // @todo: See if a path in pageTsConfig like "TCEForm.tableName.theContainerFieldName =" is useful and works with other pageTs, too.
78  $fieldNameInPageTsConfig = $flexFormFieldName;
79  if (!isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
80  ['el'][$flexFormFieldName]['TCEforms']['config'])
81  ) {
82  throw new \RuntimeException(
83  'Specified path ' . $flexFormFieldName . ' not found in flex form data structure',
84  1480609491
85  );
86  }
87  $fieldConfig = $dataStructure['sheets'][$flexFormSheetName]['ROOT']
88  ['el'][$flexFormFieldName]['TCEforms']['config'];
89  } else {
90  $fieldNameInPageTsConfig = $flexFormContainerFieldName;
91  if (!isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
92  ['el'][$flexFormFieldName]
93  ['el'][$flexFormContainerName]
94  ['el'][$flexFormContainerFieldName]['TCEforms']['config'])
95  ) {
96  throw new \RuntimeException(
97  'Specified path ' . $flexFormContainerName . ' not found in flex form section container data structure',
98  1480611208
99  );
100  }
101  $fieldConfig = $dataStructure['sheets'][$flexFormSheetName]['ROOT']
102  ['el'][$flexFormFieldName]
103  ['el'][$flexFormContainerName]
104  ['el'][$flexFormContainerFieldName]['TCEforms']['config'];
105  }
106  }
107 
108  $pageTsConfig = ‪BackendUtility::getPagesTSconfig($pid);
109 
110  $wizardConfig = $fieldConfig['suggestOptions'] ?? [];
111 
112  $queryTables = $this->‪getTablesToQueryFromFieldConfiguration($fieldConfig);
113  $whereClause = $this->‪getWhereClause($fieldConfig);
114 
115  $resultRows = [];
116 
117  // fetch the records for each query table. A query table is a table from which records are allowed to
118  // be added to the TCEForm selector, originally fetched from the "allowed" config option in the TCA
119  foreach ($queryTables as $queryTable) {
120  // if the table does not exist, skip it
121  if (!is_array(‪$GLOBALS['TCA'][$queryTable]) || empty(‪$GLOBALS['TCA'][$queryTable])) {
122  continue;
123  }
124 
125  $config = $this->‪getConfigurationForTable($queryTable, $wizardConfig, $pageTsConfig, $tableName, $fieldNameInPageTsConfig);
126 
127  // process addWhere
128  if (!isset($config['addWhere']) && $whereClause) {
129  $config['addWhere'] = $whereClause;
130  }
131  if (isset($config['addWhere'])) {
132  $replacement = [
133  '###THIS_UID###' => (int)$uid,
134  '###CURRENT_PID###' => (int)$pid
135  ];
136  if (isset($pageTsConfig['TCEFORM.'][$tableName . '.'][$fieldNameInPageTsConfig . '.'])) {
137  $fieldTSconfig = $pageTsConfig['TCEFORM.'][$tableName . '.'][$fieldNameInPageTsConfig . '.'];
138  if (isset($fieldTSconfig['PAGE_TSCONFIG_ID'])) {
139  $replacement['###PAGE_TSCONFIG_ID###'] = (int)$fieldTSconfig['PAGE_TSCONFIG_ID'];
140  }
141  if (isset($fieldTSconfig['PAGE_TSCONFIG_IDLIST'])) {
142  $replacement['###PAGE_TSCONFIG_IDLIST###'] = implode(',', ‪GeneralUtility::intExplode(',', $fieldTSconfig['PAGE_TSCONFIG_IDLIST']));
143  }
144  if (isset($fieldTSconfig['PAGE_TSCONFIG_STR'])) {
145  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($fieldConfig['foreign_table']);
146  // nasty hack, but it's currently not possible to just quote anything "inside" the value but not escaping
147  // the whole field as it is not known where it is used in the WHERE clause
148  $replacement['###PAGE_TSCONFIG_STR###'] = trim($connection->quote($fieldTSconfig['PAGE_TSCONFIG_STR']), '\'');
149  }
150  }
151  $config['addWhere'] = strtr(' ' . $config['addWhere'], $replacement);
152  }
153 
154  // instantiate the class that should fetch the records for this $queryTable
155  $receiverClassName = $config['receiverClass'];
156  if (!class_exists($receiverClassName)) {
157  $receiverClassName = SuggestWizardDefaultReceiver::class;
158  }
159  $receiverObj = GeneralUtility::makeInstance($receiverClassName, $queryTable, $config);
160  $params = [
161  'value' => $search,
162  'uid' => $uid,
163  ];
164  $rows = $receiverObj->queryTable($params);
165  if (empty($rows)) {
166  continue;
167  }
168  $resultRows = $rows + $resultRows;
169  unset($rows);
170  }
171 
172  // Limit the number of items in the result list
173  $maxItems = $config['maxItemsInResultList'] ?? 10;
174  $maxItems = min(count($resultRows), $maxItems);
175 
176  array_splice($resultRows, $maxItems);
177  return (new ‪JsonResponse())->setPayload(array_values($resultRows));
178  }
179 
186  protected function ‪isTableHidden(array $tableConfig)
187  {
188  return (bool)$tableConfig['ctrl']['hideTable'];
189  }
190 
198  protected function ‪currentBackendUserMayAccessTable(array $tableConfig)
199  {
200  if ($this->‪getBackendUser()->isAdmin()) {
201  return true;
202  }
203 
204  // If the user is no admin, they may not access admin-only tables
205  if ($tableConfig['ctrl']['adminOnly']) {
206  return false;
207  }
208 
209  // allow access to root level pages if security restrictions should be bypassed
210  return !$tableConfig['ctrl']['rootLevel'] || $tableConfig['ctrl']['security']['ignoreRootLevelRestriction'];
211  }
212 
224  protected function ‪getConfigurationForTable($queryTable, array $wizardConfig, array $TSconfig, $table, $field)
225  {
226  $config = (array)$wizardConfig['default'];
227 
228  if (is_array($wizardConfig[$queryTable])) {
229  ‪ArrayUtility::mergeRecursiveWithOverrule($config, $wizardConfig[$queryTable]);
230  }
231  $globalSuggestTsConfig = $TSconfig['TCEFORM.']['suggest.'];
232  $currentFieldSuggestTsConfig = $TSconfig['TCEFORM.'][$table . '.'][$field . '.']['suggest.'];
233 
234  // merge the configurations of different "levels" to get the working configuration for this table and
235  // field (i.e., go from the most general to the most special configuration)
236  if (is_array($globalSuggestTsConfig['default.'])) {
237  ‪ArrayUtility::mergeRecursiveWithOverrule($config, $globalSuggestTsConfig['default.']);
238  }
239 
240  if (is_array($globalSuggestTsConfig[$queryTable . '.'])) {
241  ‪ArrayUtility::mergeRecursiveWithOverrule($config, $globalSuggestTsConfig[$queryTable . '.']);
242  }
243 
244  // use $table instead of $queryTable here because we overlay a config
245  // for the input-field here, not for the queried table
246  if (is_array($currentFieldSuggestTsConfig['default.'])) {
247  ‪ArrayUtility::mergeRecursiveWithOverrule($config, $currentFieldSuggestTsConfig['default.']);
248  }
249 
250  if (is_array($currentFieldSuggestTsConfig[$queryTable . '.'])) {
251  ‪ArrayUtility::mergeRecursiveWithOverrule($config, $currentFieldSuggestTsConfig[$queryTable . '.']);
252  }
253 
254  return $config;
255  }
256 
264  protected function ‪getTablesToQueryFromFieldConfiguration(array $fieldConfig)
265  {
266  $queryTables = [];
267 
268  if (isset($fieldConfig['allowed'])) {
269  if ($fieldConfig['allowed'] !== '*') {
270  // list of allowed tables
271  $queryTables = ‪GeneralUtility::trimExplode(',', $fieldConfig['allowed']);
272  } else {
273  // all tables are allowed, if the user can access them
274  foreach (‪$GLOBALS['TCA'] as $tableName => $tableConfig) {
275  if (!$this->‪isTableHidden($tableConfig) && $this->‪currentBackendUserMayAccessTable($tableConfig)) {
276  $queryTables[] = $tableName;
277  }
278  }
279  unset($tableName, $tableConfig);
280  }
281  } elseif (isset($fieldConfig['foreign_table'])) {
282  // use the foreign table
283  $queryTables = [$fieldConfig['foreign_table']];
284  }
285 
286  return $queryTables;
287  }
288 
297  protected function ‪getWhereClause(array $fieldConfig)
298  {
299  if (!isset($fieldConfig['foreign_table'])) {
300  return '';
301  }
302 
303  // strip ORDER BY clause
304  return trim(preg_replace('/ORDER[[:space:]]+BY.*/i', '', $fieldConfig['foreign_table_where']));
305  }
306 
310  protected function ‪getBackendUser()
311  {
312  return ‪$GLOBALS['BE_USER'];
313  }
314 }
‪TYPO3\CMS\Backend\Controller\Wizard\SuggestWizardController\getWhereClause
‪string getWhereClause(array $fieldConfig)
Definition: SuggestWizardController.php:297
‪TYPO3\CMS\Backend\Controller\Wizard\SuggestWizardController\getBackendUser
‪TYPO3 CMS Core Authentication BackendUserAuthentication getBackendUser()
Definition: SuggestWizardController.php:310
‪TYPO3\CMS\Core\Utility\ArrayUtility\mergeRecursiveWithOverrule
‪static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
Definition: ArrayUtility.php:654
‪TYPO3\CMS\Backend\Controller\Wizard\SuggestWizardController\currentBackendUserMayAccessTable
‪bool currentBackendUserMayAccessTable(array $tableConfig)
Definition: SuggestWizardController.php:198
‪TYPO3\CMS\Backend\Controller\Wizard\SuggestWizardController\getTablesToQueryFromFieldConfiguration
‪array getTablesToQueryFromFieldConfiguration(array $fieldConfig)
Definition: SuggestWizardController.php:264
‪TYPO3\CMS\Backend\Controller\Wizard
Definition: AbstractWizardController.php:16
‪TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools
Definition: FlexFormTools.php:38
‪TYPO3\CMS\Backend\Utility\BackendUtility\getPagesTSconfig
‪static array getPagesTSconfig($id)
Definition: BackendUtility.php:698
‪TYPO3\CMS\Backend\Controller\Wizard\SuggestWizardController\searchAction
‪ResponseInterface searchAction(ServerRequestInterface $request)
Definition: SuggestWizardController.php:41
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:75
‪TYPO3\CMS\Backend\Controller\Wizard\SuggestWizardController\isTableHidden
‪bool isTableHidden(array $tableConfig)
Definition: SuggestWizardController.php:186
‪TYPO3\CMS\Backend\Utility\BackendUtility\getRecord
‪static array null getRecord($table, $uid, $fields=' *', $where='', $useDeleteClause=true)
Definition: BackendUtility.php:95
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Backend\Controller\Wizard\SuggestWizardController
Definition: SuggestWizardController.php:33
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪TYPO3\CMS\Core\Http\JsonResponse
Definition: JsonResponse.php:26
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:988
‪TYPO3\CMS\Backend\Controller\Wizard\SuggestWizardController\getConfigurationForTable
‪array getConfigurationForTable($queryTable, array $wizardConfig, array $TSconfig, $table, $field)
Definition: SuggestWizardController.php:224
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver
Definition: SuggestWizardDefaultReceiver.php:39
‪TYPO3\CMS\Backend\Utility\BackendUtility\getTCAtypeValue
‪static string getTCAtypeValue($table, $row)
Definition: BackendUtility.php:636