TYPO3 CMS  TYPO3_8-7
L10nModeUpdater.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
25 
31 {
38  protected $payload = [];
39 
45  public function getTitle(): string
46  {
47  return 'Migrate values in database records having "l10n_mode"'
48  . ' either set to "exclude" or "mergeIfNotBlank"';
49  }
50 
57  public function hasPotentialUpdateForTable(string $tableName): bool
58  {
59  $this->payload[$tableName] = $this->getL10nModePayloadForTable($tableName);
60  return !empty($this->payload[$tableName]['localizations']);
61  }
62 
70  public function updateTableRow(string $tableName, array $inputRow): array
71  {
72  $currentId = (int)$inputRow['uid'];
73 
74  if (empty($this->payload[$tableName]['localizations'][$currentId])) {
75  return $inputRow;
76  }
77 
78  // disable DataHandler hooks for processing this update
79  if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php'])) {
80  $dataHandlerHooks = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php'];
81  unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']);
82  }
83 
84  if (empty($GLOBALS['LANG'])) {
85  $GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageService::class);
86  }
87  if (!empty($GLOBALS['BE_USER'])) {
88  $adminUser = $GLOBALS['BE_USER'];
89  }
90  // the admin user is required to defined workspace state when working with DataHandler
91  $fakeAdminUser = GeneralUtility::makeInstance(BackendUserAuthentication::class);
92  $fakeAdminUser->user = ['uid' => 0, 'username' => '_migration_', 'admin' => 1];
93  $fakeAdminUser->workspace = (int)($inputRow['t3ver_wsid'] ?? 0);
94  $GLOBALS['BE_USER'] = $fakeAdminUser;
95 
96  $tablePayload = $this->payload[$tableName];
97 
98  $liveId = $currentId;
99  if (!empty($inputRow['t3ver_wsid'])
100  && !empty($inputRow['t3ver_oid'])
101  && !VersionState::cast($inputRow['t3ver_state'])
103  $liveId = (int)$inputRow['t3ver_oid'];
104  }
105 
106  $dataMap = [];
107 
108  // define localization states and thus trigger updates later
109  if (State::isApplicable($tableName)) {
110  $stateUpdates = [];
111  foreach ($tablePayload['fieldModes'] as $fieldName => $fieldMode) {
112  if ($fieldMode !== 'mergeIfNotBlank') {
113  continue;
114  }
115  if (!empty($inputRow[$fieldName])) {
116  $stateUpdates[$fieldName] = State::STATE_CUSTOM;
117  } else {
118  $stateUpdates[$fieldName] = State::STATE_PARENT;
119  }
120  }
121 
122  // fetch the language state upfront, so that calling DataMapProcessor below
123  // will handle mergeIfNotBlank fields properly
124  $languageState = State::create($tableName);
125  $languageState->update($stateUpdates);
126  $dataMap = [
127  $tableName => [
128  $liveId => [
129  'l10n_state' => $languageState->toArray()
130  ]
131  ]
132  ];
133  }
134 
135  // simulate modifying a parent record to trigger dependent updates
136  if (in_array('exclude', $tablePayload['fieldModes'], true)) {
137  if ($liveId !== $currentId) {
138  $record = $this->getRow($tableName, $liveId);
139  } else {
140  $record = $inputRow;
141  }
142  foreach ($tablePayload['fieldModes'] as $fieldName => $fieldMode) {
143  if ($fieldMode !== 'exclude') {
144  continue;
145  }
146  $dataMap[$tableName][$liveId][$fieldName] = $record[$fieldName];
147  }
148  }
149 
150  // in case $dataMap is empty, nothing has to be updated
151  if (!empty($dataMap)) {
152  // let DataHandler process all updates, $inputRow won't change
153  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
154  $dataHandler->enableLogging = false;
155  $dataHandler->start($dataMap, [], $fakeAdminUser);
156  $dataHandler->process_datamap();
157  }
158 
159  if (!empty($dataHandlerHooks)) {
160  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php'] = $dataHandlerHooks;
161  }
162  if (!empty($adminUser)) {
163  $GLOBALS['BE_USER'] = $adminUser;
164  }
165 
166  // the unchanged(!) state as submitted
167  return $inputRow;
168  }
169 
183  protected function getL10nModePayloadForTable(string $tableName): array
184  {
185  if (!is_array($GLOBALS['TCA'][$tableName])) {
186  throw new \RuntimeException(
187  'Globals TCA of given table name must exist',
188  1484176136
189  );
190  }
191 
192  $tableDefinition = $GLOBALS['TCA'][$tableName];
193  $languageFieldName = ($tableDefinition['ctrl']['languageField'] ?? null);
194  $parentFieldName = ($tableDefinition['ctrl']['transOrigPointerField'] ?? null);
195 
196  if (
197  empty($tableDefinition['columns'])
198  || !is_array($tableDefinition['columns'])
199  || empty($languageFieldName)
200  || empty($parentFieldName)
201  ) {
202  return [];
203  }
204 
205  $fieldModes = [];
206  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfiguration) {
207  $l10nMode = ($fieldConfiguration['l10n_mode'] ?? null);
208  $allowLanguageSynchronization = ($fieldConfiguration['config']['behaviour']['allowLanguageSynchronization'] ?? null);
209 
210  if ($l10nMode === 'exclude') {
211  $fieldModes[$fieldName] = $l10nMode;
212  } elseif ($allowLanguageSynchronization) {
213  $fieldModes[$fieldName] = 'mergeIfNotBlank';
214  }
215  }
216 
217  if (empty($fieldModes)) {
218  return [];
219  }
220 
221  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
222  $queryBuilder = $connectionPool->getQueryBuilderForTable($tableName);
223  $queryBuilder->getRestrictions()->removeAll();
224  $queryBuilder->from($tableName);
225 
226  $parentFieldName = $tableDefinition['ctrl']['transOrigPointerField'];
227  $selectFieldNames = ['uid', $parentFieldName];
228 
229  $predicates = [
230  $queryBuilder->expr()->gt(
231  $tableDefinition['ctrl']['languageField'],
232  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
233  ),
234  $queryBuilder->expr()->gt(
235  $parentFieldName,
236  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
237  )
238  ];
239 
240  if (!empty($tableDefinition['ctrl']['versioningWS'])) {
241  $selectFieldNames = array_merge(
242  $selectFieldNames,
243  ['t3ver_wsid', 't3ver_oid', 't3ver_state']
244  );
245  $predicates[] = $queryBuilder->expr()->orX(
246  $queryBuilder->expr()->eq(
247  't3ver_state',
248  $queryBuilder->createNamedParameter(
250  \PDO::PARAM_INT
251  )
252  ),
253  $queryBuilder->expr()->eq(
254  't3ver_state',
255  $queryBuilder->createNamedParameter(
257  \PDO::PARAM_INT
258  )
259  ),
260  $queryBuilder->expr()->eq(
261  't3ver_state',
262  $queryBuilder->createNamedParameter(
264  \PDO::PARAM_INT
265  )
266  )
267  );
268  }
269 
270  $statement = $queryBuilder
271  ->select(...$selectFieldNames)
272  ->andWhere(...$predicates)
273  ->execute();
274 
275  $payload = [];
276 
277  foreach ($statement as $row) {
278  $translationId = $row['uid'];
279  $parentId = (int)$row[$parentFieldName];
280  $payload['localizations'][$translationId] = $parentId;
281  }
282  if (!empty($payload['localizations'])) {
283  $payload['fieldModes'] = $fieldModes;
284  }
285 
286  return $payload;
287  }
288 
294  protected function getRow(string $tableName, int $id)
295  {
296  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
297  ->getQueryBuilderForTable($tableName);
298  $queryBuilder->getRestrictions()->removeAll();
299 
300  $statement = $queryBuilder
301  ->select('*')
302  ->from($tableName)
303  ->where(
304  $queryBuilder->expr()->eq(
305  'uid',
306  $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)
307  )
308  )
309  ->execute();
310 
311  return $statement->fetch();
312  }
313 }
static create(string $tableName)
Definition: State.php:32
static makeInstance($className,... $constructorArguments)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
updateTableRow(string $tableName, array $inputRow)
static isApplicable(string $tableName)
Definition: State.php:67