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