90c60ad1d4ab953fe9c5bbceb4d3cb32b8eb3874
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Tree / TableConfiguration / DatabaseTreeDataProvider.php
1 <?php
2 namespace TYPO3\CMS\Core\Tree\TableConfiguration;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * TCA tree data provider
21 *
22 * @author Steffen Ritter <info@steffen-ritter.net>
23 */
24 class DatabaseTreeDataProvider extends AbstractTableConfigurationTreeDataProvider {
25
26 const SIGNAL_PostProcessTreeData = 'PostProcessTreeData';
27 const MODE_CHILDREN = 1;
28 const MODE_PARENT = 2;
29
30 /**
31 * @var string
32 */
33 protected $tableName = '';
34
35 /**
36 * @var string
37 */
38 protected $treeId = '';
39
40 /**
41 * @var string
42 */
43 protected $labelField = '';
44
45 /**
46 * @var string
47 */
48 protected $tableWhere = '';
49
50 /**
51 * @var int
52 */
53 protected $lookupMode = self::MODE_CHILDREN;
54
55 /**
56 * @var string
57 */
58 protected $lookupField = '';
59
60 /**
61 * @var int
62 */
63 protected $rootUid = 0;
64
65 /**
66 * @var array
67 */
68 protected $idCache = array();
69
70 /**
71 * Stores TCA-Configuration of the LookUpField in tableName
72 *
73 * @var array
74 */
75 protected $columnConfiguration;
76
77 /**
78 * node sort values (the orderings from foreign_Table_where evaluation)
79 *
80 * @var array
81 */
82 protected $nodeSortValues = array();
83
84 /**
85 * @var array TCEforms compiled TSConfig array
86 */
87 protected $generatedTSConfig = array();
88
89 /**
90 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
91 */
92 protected $signalSlotDispatcher;
93
94 /**
95 * Sets the label field
96 *
97 * @param string $labelField
98 * @return void
99 */
100 public function setLabelField($labelField) {
101 $this->labelField = $labelField;
102 }
103
104 /**
105 * Gets the label field
106 *
107 * @return string
108 */
109 public function getLabelField() {
110 return $this->labelField;
111 }
112
113 /**
114 * Sets the table name
115 *
116 * @param string $tableName
117 * @return void
118 */
119 public function setTableName($tableName) {
120 $this->tableName = $tableName;
121 }
122
123 /**
124 * Gets the table name
125 *
126 * @return string
127 */
128 public function getTableName() {
129 return $this->tableName;
130 }
131
132 /**
133 * Sets the lookup field
134 *
135 * @param string $lookupField
136 * @return void
137 */
138 public function setLookupField($lookupField) {
139 $this->lookupField = $lookupField;
140 }
141
142 /**
143 * Gets the lookup field
144 *
145 * @return string
146 */
147 public function getLookupField() {
148 return $this->lookupField;
149 }
150
151 /**
152 * Sets the lookup mode
153 *
154 * @param int $lookupMode
155 * @return void
156 */
157 public function setLookupMode($lookupMode) {
158 $this->lookupMode = $lookupMode;
159 }
160
161 /**
162 * Gets the lookup mode
163 *
164 * @return int
165 */
166 public function getLookupMode() {
167 return $this->lookupMode;
168 }
169
170 /**
171 * Gets the nodes
172 *
173 * @param \TYPO3\CMS\Backend\Tree\TreeNode $node
174 * @return \TYPO3\CMS\Backend\Tree\TreeNodeCollection
175 */
176 public function getNodes(\TYPO3\CMS\Backend\Tree\TreeNode $node) {
177
178 }
179
180 /**
181 * Gets the root node
182 *
183 * @return \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode
184 */
185 public function getRoot() {
186 return $this->buildRepresentationForNode($this->treeData);
187 }
188
189 /**
190 * Sets the root uid
191 *
192 * @param int $rootUid
193 * @return void
194 */
195 public function setRootUid($rootUid) {
196 $this->rootUid = $rootUid;
197 }
198
199 /**
200 * Gets the root uid
201 *
202 * @return int
203 */
204 public function getRootUid() {
205 return $this->rootUid;
206 }
207
208 /**
209 * Sets the tableWhere clause
210 *
211 * @param string $tableWhere
212 * @return void
213 */
214 public function setTableWhere($tableWhere) {
215 $this->tableWhere = $tableWhere;
216 }
217
218 /**
219 * Gets the tableWhere clause
220 *
221 * @return string
222 */
223 public function getTableWhere() {
224 return $this->tableWhere;
225 }
226
227 /**
228 * Builds a complete node including childs
229 *
230 * @param \TYPO3\CMS\Backend\Tree\TreeNode $basicNode
231 * @param NULL|\TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode $parent
232 * @param int $level
233 * @return \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode Node object
234 */
235 protected function buildRepresentationForNode(\TYPO3\CMS\Backend\Tree\TreeNode $basicNode, DatabaseTreeNode $parent = NULL, $level = 0) {
236 /** @var $node \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode */
237 $node = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeNode::class);
238 $row = array();
239 if ($basicNode->getId() == 0) {
240 $node->setSelected(FALSE);
241 $node->setExpanded(TRUE);
242 $node->setLabel($GLOBALS['LANG']->sL($GLOBALS['TCA'][$this->tableName]['ctrl']['title']));
243 } else {
244 $row = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordWSOL($this->tableName, $basicNode->getId(), '*', '', FALSE);
245 if ($this->getLabelField() !== '') {
246 $node->setLabel($row[$this->getLabelField()]);
247 } else {
248 $node->setLabel($basicNode->getId());
249 }
250 $node->setSelected(GeneralUtility::inList($this->getSelectedList(), $basicNode->getId()));
251 $node->setExpanded($this->isExpanded($basicNode));
252 }
253 $node->setId($basicNode->getId());
254 $node->setSelectable(!GeneralUtility::inList($this->getNonSelectableLevelList(), $level) && !in_array($basicNode->getId(), $this->getItemUnselectableList()));
255 $node->setSortValue($this->nodeSortValues[$basicNode->getId()]);
256 $node->setIcon(\TYPO3\CMS\Backend\Utility\IconUtility::mapRecordTypeToSpriteIconClass($this->tableName, $row));
257 $node->setParentNode($parent);
258 if ($basicNode->hasChildNodes()) {
259 $node->setHasChildren(TRUE);
260 /** @var $childNodes \TYPO3\CMS\Backend\Tree\SortedTreeNodeCollection */
261 $childNodes = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\SortedTreeNodeCollection::class);
262 foreach ($basicNode->getChildNodes() as $child) {
263 $childNodes->append($this->buildRepresentationForNode($child, $node, $level + 1));
264 }
265 $node->setChildNodes($childNodes);
266 }
267 return $node;
268 }
269
270 /**
271 * Init the tree data
272 *
273 * @return void
274 */
275 public function initializeTreeData() {
276 parent::initializeTreeData();
277 $this->nodeSortValues = array_flip($this->itemWhiteList);
278 $this->columnConfiguration = $GLOBALS['TCA'][$this->getTableName()]['columns'][$this->getLookupField()]['config'];
279 if (isset($this->columnConfiguration['foreign_table']) && $this->columnConfiguration['foreign_table'] != $this->getTableName()) {
280 throw new \InvalidArgumentException('TCA Tree configuration is invalid: tree for different node-Tables is not implemented yet', 1290944650);
281 }
282 $this->treeData = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\TreeNode::class);
283 $this->treeData->setId($this->getRootUid());
284 $this->treeData->setParentNode(NULL);
285 $childNodes = $this->getChildrenOf($this->treeData, 0);
286 if ($childNodes !== NULL) {
287 $this->treeData->setChildNodes($childNodes);
288 }
289
290 $this->emitPostProcessTreeDataSignal();
291 }
292
293 /**
294 * Gets node children
295 *
296 * @param \TYPO3\CMS\Backend\Tree\TreeNode $node
297 * @param int $level
298 * @return NULL|\TYPO3\CMS\Backend\Tree\TreeNodeCollection
299 */
300 protected function getChildrenOf(\TYPO3\CMS\Backend\Tree\TreeNode $node, $level) {
301 $nodeData = NULL;
302 if ($node->getId() !== 0) {
303 $nodeData = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', $this->tableName, 'uid=' . $node->getId());
304 }
305 if ($nodeData == NULL) {
306 $nodeData = array(
307 'uid' => 0,
308 $this->getLookupField() => ''
309 );
310 }
311 $storage = NULL;
312 $children = $this->getRelatedRecords($nodeData);
313 if (count($children)) {
314 /** @var $storage \TYPO3\CMS\Backend\Tree\TreeNodeCollection */
315 $storage = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\TreeNodeCollection::class);
316 foreach ($children as $child) {
317 $node = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\TreeNode::class);
318 $node->setId($child);
319 if ($level <= $this->levelMaximum) {
320 $children = $this->getChildrenOf($node, $level + 1);
321 if ($children !== NULL) {
322 $node->setChildNodes($children);
323 }
324 }
325 $storage->append($node);
326 }
327 }
328 return $storage;
329 }
330
331 /**
332 * Gets related records depending on TCA configuration
333 *
334 * @param array $row
335 * @return array
336 */
337 protected function getRelatedRecords(array $row) {
338 if ($this->getLookupMode() == DatabaseTreeDataProvider::MODE_PARENT) {
339 $children = $this->getChildrenUidsFromParentRelation($row);
340 } else {
341 $children = $this->getChildrenUidsFromChildrenRelation($row);
342 }
343 $allowedArray = array();
344 foreach ($children as $child) {
345 if (!in_array($child, $this->idCache) && in_array($child, $this->itemWhiteList)) {
346 $allowedArray[] = $child;
347 }
348 }
349 $this->idCache = array_merge($this->idCache, $allowedArray);
350 return $allowedArray;
351 }
352
353 /**
354 * Gets related records depending on TCA configuration
355 *
356 * @param array $row
357 * @return array
358 */
359 protected function getChildrenUidsFromParentRelation(array $row) {
360 $uid = $row['uid'];
361 switch ((string)$this->columnConfiguration['type']) {
362 case 'inline':
363
364 case 'select':
365 if ($this->columnConfiguration['MM']) {
366 /** @var $dbGroup \TYPO3\CMS\Core\Database\RelationHandler */
367 $dbGroup = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
368 // Dummy field for setting "look from other site"
369 $this->columnConfiguration['MM_oppositeField'] = 'children';
370 $dbGroup->start($row[$this->getLookupField()], $this->getTableName(), $this->columnConfiguration['MM'], $uid, $this->getTableName(), $this->columnConfiguration);
371 $relatedUids = $dbGroup->tableArray[$this->getTableName()];
372 } elseif ($this->columnConfiguration['foreign_field']) {
373 $relatedUids = $this->listFieldQuery($this->columnConfiguration['foreign_field'], $uid);
374 } else {
375 $relatedUids = $this->listFieldQuery($this->getLookupField(), $uid);
376 }
377 break;
378 default:
379 $relatedUids = $this->listFieldQuery($this->getLookupField(), $uid);
380 }
381 return $relatedUids;
382 }
383
384 /**
385 * Gets related children records depending on TCA configuration
386 *
387 * @param array $row
388 * @return array
389 */
390 protected function getChildrenUidsFromChildrenRelation(array $row) {
391 $relatedUids = array();
392 $uid = $row['uid'];
393 $value = $row[$this->getLookupField()];
394 switch ((string)$this->columnConfiguration['type']) {
395 case 'inline':
396
397 case 'select':
398 if ($this->columnConfiguration['MM']) {
399 /** @var $dbGroup \TYPO3\CMS\Core\Database\RelationHandler */
400 $dbGroup = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
401 $dbGroup->start($value, $this->getTableName(), $this->columnConfiguration['MM'], $uid, $this->getTableName(), $this->columnConfiguration);
402 $relatedUids = $dbGroup->tableArray[$this->getTableName()];
403 } elseif ($this->columnConfiguration['foreign_field']) {
404 $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', $this->getTableName(), $this->columnConfiguration['foreign_field'] . '=' . (int)$uid);
405 foreach ($records as $record) {
406 $relatedUids[] = $record['uid'];
407 }
408 } else {
409 $relatedUids = GeneralUtility::intExplode(',', $value, TRUE);
410 }
411 break;
412 default:
413 $relatedUids = GeneralUtility::intExplode(',', $value, TRUE);
414 }
415 return $relatedUids;
416 }
417
418 /**
419 * Queries the table for an field which might contain a list.
420 *
421 * @param string $fieldName the name of the field to be queried
422 * @param int $queryId the uid to search for
423 * @return int[] all uids found
424 */
425 protected function listFieldQuery($fieldName, $queryId) {
426 $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', $this->getTableName(), $GLOBALS['TYPO3_DB']->listQuery($fieldName, (int)$queryId, $this->getTableName()) . ((int)$queryId == 0 ? ' OR ' . $fieldName . ' = \'\'' : ''));
427 $uidArray = array();
428 foreach ($records as $record) {
429 $uidArray[] = $record['uid'];
430 }
431 return $uidArray;
432 }
433
434 /**
435 * Emits the post processing tree data signal.
436 *
437 * @return void
438 */
439 protected function emitPostProcessTreeDataSignal() {
440 $this->getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeDataProvider::class,
441 self::SIGNAL_PostProcessTreeData,
442 array($this, $this->treeData)
443 );
444 $this->emitDeprecatedPostProcessTreeDataSignal();
445 }
446
447 /**
448 * A wrong signal name was introduced with https://review.typo3.org/#/c/34855/
449 * This function handles the old signal name and logs a deprecation warning.
450 *
451 * @return void
452 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
453 */
454 protected function emitDeprecatedPostProcessTreeDataSignal() {
455 $deprecatedSlots = $this->getSignalSlotDispatcher()->getSlots(
456 'TYPO3\\CMS\\Core\\Tree\\TableConfiguration\\TableConfiguration\\DatabaseTreeDataProvider',
457 self::SIGNAL_PostProcessTreeData
458 );
459 if (!empty($deprecatedSlots)) {
460 foreach ($deprecatedSlots as $slotInformation) {
461 $slotClassNameOrObject = $slotInformation['object'] ? get_class($slotInformation['object']) : $slotInformation['class'];
462 GeneralUtility::deprecationLog(
463 'Signal "TYPO3\\CMS\\Core\\Tree\\TableConfiguration\\TableConfiguration\\DatabaseTreeDataProvider" ' .
464 'is deprecated but used by "' . $slotClassNameOrObject . '". ' .
465 'Please update signal name to "' . self::class . '".'
466 );
467 }
468 $this->getSignalSlotDispatcher()->dispatch(
469 'TYPO3\\CMS\\Core\\Tree\\TableConfiguration\\TableConfiguration\\DatabaseTreeDataProvider',
470 self::SIGNAL_PostProcessTreeData,
471 array($this, $this->treeData)
472 );
473 }
474 }
475
476 /**
477 * Get the SignalSlot dispatcher
478 *
479 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
480 */
481 protected function getSignalSlotDispatcher() {
482 if (!isset($this->signalSlotDispatcher)) {
483 $this->signalSlotDispatcher = $this->getObjectManager()->get(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
484 }
485 return $this->signalSlotDispatcher;
486 }
487
488 /**
489 * Get the ObjectManager
490 *
491 * @return \TYPO3\CMS\Extbase\Object\ObjectManager
492 */
493 protected function getObjectManager() {
494 return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
495 }
496
497 }