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