[CLEANUP] Make use of bool/int in @param DocComments
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / InlineRelatedRecordResolver.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form;
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\Backend\Form\Utility\FormEngineUtility;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
21 use TYPO3\CMS\Core\Utility\MathUtility;
22 use TYPO3\CMS\Core\Versioning\VersionState;
23 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
24
25 /**
26 * Resolve inline relations.
27 *
28 * This class contains various methods to fetch inline child records based on configurations
29 * and to prepare new child records.
30 */
31 class InlineRelatedRecordResolver {
32
33 /**
34 * Get the related records of the embedding item, this could be 1:n, m:n.
35 * Returns an associative array with the keys records and count. 'count' contains only real existing records on the current parent record.
36 *
37 * @param string $table The table name of the record
38 * @param string $field The field name which this element is supposed to edit
39 * @param array $row The record data array where the value(s) for the field can be found
40 * @param array $PA An array with additional configuration options.
41 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
42 * @param int $inlineFirstPid Inline first pid
43 * @return array The records related to the parent item as associative array.
44 */
45 public function getRelatedRecords($table, $field, $row, $PA, $config, $inlineFirstPid) {
46 $language = 0;
47 $elements = $PA['itemFormElValue'];
48 $foreignTable = $config['foreign_table'];
49 $localizationMode = BackendUtility::getInlineLocalizationMode($table, $config);
50 if ($localizationMode !== FALSE) {
51 $language = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
52 $transOrigPointer = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']];
53 $transOrigTable = BackendUtility::getOriginalTranslationTable($table);
54
55 if ($language > 0 && $transOrigPointer) {
56 // Localization in mode 'keep', isn't a real localization, but keeps the children of the original parent record:
57 if ($localizationMode === 'keep') {
58 $transOrigRec = $this->getRecord($transOrigTable, $transOrigPointer);
59 $elements = $transOrigRec[$field];
60 } elseif ($localizationMode === 'select') {
61 $transOrigRec = $this->getRecord($transOrigTable, $transOrigPointer);
62 $fieldValue = $transOrigRec[$field];
63
64 // Checks if it is a flexform field
65 if ($GLOBALS['TCA'][$table]['columns'][$field]['config']['type'] === 'flex') {
66 $flexFormParts = FormEngineUtility::extractFlexFormParts($PA['itemFormElName']);
67 $flexData = GeneralUtility::xml2array($fieldValue);
68 /** @var $flexFormTools FlexFormTools */
69 $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
70 $flexFormFieldValue = $flexFormTools->getArrayValueByPath($flexFormParts, $flexData);
71
72 if ($flexFormFieldValue !== NULL) {
73 $fieldValue = $flexFormFieldValue;
74 }
75 }
76
77 $recordsOriginal = $this->getRelatedRecordsArray($foreignTable, $fieldValue);
78 }
79 }
80 }
81 $records = $this->getRelatedRecordsArray($foreignTable, $elements);
82 $relatedRecords = array('records' => $records, 'count' => count($records));
83 // Merge original language with current localization and show differences:
84 if (!empty($recordsOriginal)) {
85 $options = array(
86 'showPossible' => isset($config['appearance']['showPossibleLocalizationRecords']) && $config['appearance']['showPossibleLocalizationRecords'],
87 'showRemoved' => isset($config['appearance']['showRemovedLocalizationRecords']) && $config['appearance']['showRemovedLocalizationRecords']
88 );
89 // Either show records that possibly can localized or removed
90 if ($options['showPossible'] || $options['showRemoved']) {
91 $relatedRecords['records'] = $this->getLocalizationDifferences($foreignTable, $options, $recordsOriginal, $records);
92 // Otherwise simulate localizeChildrenAtParentLocalization behaviour when creating a new record
93 // (which has language and translation pointer values set)
94 } elseif (!empty($config['behaviour']['localizeChildrenAtParentLocalization']) && !MathUtility::canBeInterpretedAsInteger($row['uid'])) {
95 if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['transOrigPointerField'])) {
96 $foreignLanguageField = $GLOBALS['TCA'][$foreignTable]['ctrl']['languageField'];
97 }
98 if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['transOrigPointerField'])) {
99 $foreignTranslationPointerField = $GLOBALS['TCA'][$foreignTable]['ctrl']['transOrigPointerField'];
100 }
101 // Duplicate child records of default language in form
102 foreach ($recordsOriginal as $record) {
103 if (!empty($foreignLanguageField)) {
104 $record[$foreignLanguageField] = $language;
105 }
106 if (!empty($foreignTranslationPointerField)) {
107 $record[$foreignTranslationPointerField] = $record['uid'];
108 }
109 $newId = uniqid('NEW', TRUE);
110 $record['uid'] = $newId;
111 $record['pid'] = $$inlineFirstPid;
112 $relatedRecords['records'][$newId] = $record;
113 }
114 }
115 }
116 return $relatedRecords;
117 }
118
119 /**
120 * Wrapper. Calls getRecord in case of a new record should be created.
121 *
122 * @param int $pid The pid of the page the record should be stored (only relevant for NEW records)
123 * @param string $table The table to fetch data from (= foreign_table)
124 * @return array A record row from the database post-processed by \TYPO3\CMS\Backend\Form\DataPreprocessor
125 */
126 public function getNewRecord($pid, $table) {
127 $record = $this->getRecord($table, $pid, 'new');
128 $record['uid'] = uniqid('NEW', TRUE);
129
130 $newRecordPid = $pid;
131 $pageTS = BackendUtility::getPagesTSconfig($pid);
132 if (isset($pageTS['TCAdefaults.'][$table . '.']['pid']) && MathUtility::canBeInterpretedAsInteger($pageTS['TCAdefaults.'][$table . '.']['pid'])) {
133 $newRecordPid = $pageTS['TCAdefaults.'][$table . '.']['pid'];
134 }
135
136 $record['pid'] = $newRecordPid;
137
138 return $record;
139 }
140
141 /**
142 * Get a single record row for a TCA table from the database.
143 * \TYPO3\CMS\Backend\Form\DataPreprocessor is used for "upgrading" the
144 * values, especially the relations.
145 * Used in inline context
146 *
147 * @param string $table The table to fetch data from (= foreign_table)
148 * @param string $uid The uid of the record to fetch, or the pid if a new record should be created
149 * @param string $cmd The command to perform, empty or 'new'
150 * @return array A record row from the database post-processed by \TYPO3\CMS\Backend\Form\DataPreprocessor
151 * @internal
152 */
153 public function getRecord($table, $uid, $cmd = '') {
154 $backendUser = $this->getBackendUserAuthentication();
155 // Fetch workspace version of a record (if any):
156 if ($cmd !== 'new' && $backendUser->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($table)) {
157 $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($backendUser->workspace, $table, $uid, 'uid,t3ver_state');
158 if ($workspaceVersion !== FALSE) {
159 $versionState = VersionState::cast($workspaceVersion['t3ver_state']);
160 if ($versionState->equals(VersionState::DELETE_PLACEHOLDER)) {
161 return FALSE;
162 }
163 $uid = $workspaceVersion['uid'];
164 }
165 }
166 /** @var $trData DataPreprocessor */
167 $trData = GeneralUtility::makeInstance(DataPreprocessor::class);
168 $trData->addRawData = TRUE;
169 $trData->lockRecords = 1;
170 // If a new record should be created
171 $trData->fetchRecord($table, $uid, $cmd === 'new' ? 'new' : '');
172 $rec = reset($trData->regTableItems_data);
173 return $rec;
174 }
175
176 /**
177 * Gets the related records of the embedding item, this could be 1:n, m:n.
178 *
179 * @param string $table The table name of the record
180 * @param string $itemList The list of related child records
181 * @return array The records related to the parent item
182 */
183 protected function getRelatedRecordsArray($table, $itemList) {
184 $records = array();
185 $itemArray = FormEngineUtility::getInlineRelatedRecordsUidArray($itemList);
186 // Perform modification of the selected items array:
187 foreach ($itemArray as $uid) {
188 // Get the records for this uid using \TYPO3\CMS\Backend\Form\DataPreprocessor
189 if ($record = $this->getRecord($table, $uid)) {
190 $records[$uid] = $record;
191 }
192 }
193 return $records;
194 }
195
196 /**
197 * Gets the difference between current localized structure and the original language structure.
198 * If there are records which once were localized but don't exist in the original version anymore, the record row is marked with '__remove'.
199 * If there are records which can be localized and exist only in the original version, the record row is marked with '__create' and '__virtual'.
200 *
201 * @param string $table The table name of the parent records
202 * @param array $options Options defining what kind of records to display
203 * @param array $recordsOriginal The uids of the child records of the original language
204 * @param array $recordsLocalization The uids of the child records of the current localization
205 * @return array Merged array of uids of the child records of both versions
206 */
207 protected function getLocalizationDifferences($table, array $options, array $recordsOriginal, array $recordsLocalization) {
208 $records = array();
209 $transOrigPointerField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
210 // Compare original to localized version of the records:
211 foreach ($recordsLocalization as $uid => $row) {
212 // If the record points to a original translation which doesn't exist anymore, it could be removed:
213 if (isset($row[$transOrigPointerField]) && $row[$transOrigPointerField] > 0) {
214 $transOrigPointer = $row[$transOrigPointerField];
215 if (isset($recordsOriginal[$transOrigPointer])) {
216 unset($recordsOriginal[$transOrigPointer]);
217 } elseif ($options['showRemoved']) {
218 $row['__remove'] = TRUE;
219 }
220 }
221 $records[$uid] = $row;
222 }
223 // Process the remaining records in the original unlocalized parent:
224 if ($options['showPossible']) {
225 foreach ($recordsOriginal as $uid => $row) {
226 $row['__create'] = TRUE;
227 $row['__virtual'] = TRUE;
228 $records[$uid] = $row;
229 }
230 }
231 return $records;
232 }
233
234 /**
235 * @return BackendUserAuthentication
236 */
237 protected function getBackendUserAuthentication() {
238 return $GLOBALS['BE_USER'];
239 }
240
241 }