[TASK] Finalize L10nModeUpdater for enhanced l10n_modes
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Updates / RowUpdater / ImageCropUpdater.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Install\Updates\RowUpdater;
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
18 use TYPO3\CMS\Core\Database\ConnectionPool;
19 use TYPO3\CMS\Core\Imaging\ImageManipulation\Area;
20 use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
21 use TYPO3\CMS\Core\Resource\ResourceFactory;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Core\Utility\MathUtility;
24 use TYPO3\CMS\Install\Service\LoadTcaService;
25
26 /**
27 * Migrate values for database records having columns
28 * using "l10n_mode" set to "mergeIfNotBlank".
29 */
30 class ImageCropUpdater implements RowUpdaterInterface
31 {
32 /**
33 * @var array Full, migrated TCA as prepared by upgrade wizard controller
34 */
35 protected $migratedTca;
36
37 /**
38 * @var array Full, but NOT migrated TCA
39 */
40 protected $notMigratedTca;
41
42 /**
43 * List of tables with information about to migrate fields.
44 * Created during hasPotentialUpdateForTable(), used in updateTableRow()
45 *
46 * @var array
47 */
48 protected $payload = [];
49
50 /**
51 * Prepare non-migrated TCA to be used in 'hasPotentialUpdateForTable' step
52 */
53 public function __construct()
54 {
55 $this->migratedTca = $GLOBALS['TCA'];
56 $loadTcaService = GeneralUtility::makeInstance(LoadTcaService::class);
57 $loadTcaService->loadExtensionTablesWithoutMigration();
58 $this->notMigratedTca = $GLOBALS['TCA'];
59 $GLOBALS['TCA'] = $this->migratedTca;
60 }
61
62 /**
63 * Get title
64 *
65 * @return string
66 */
67 public function getTitle(): string
68 {
69 return 'Migrate values in sys_file_reference crop field';
70 }
71
72 /**
73 * Return true if a table needs modifications.
74 *
75 * @param string $tableName Table name to check
76 * @return bool True if this table has fields to migrate
77 */
78 public function hasPotentialUpdateForTable(string $tableName): bool
79 {
80 $GLOBALS['TCA'] = $this->notMigratedTca;
81 $result = false;
82 $payload = $this->getPayloadForTable($tableName);
83 if (count($payload) !== 0) {
84 $this->payload[$tableName] = $payload;
85 $result = true;
86 }
87 $GLOBALS['TCA'] = $this->migratedTca;
88 return $result;
89 }
90
91 /**
92 * Update single row if needed
93 *
94 * @param string $tableName
95 * @param array $inputRow Given row data
96 * @return array Modified row data
97 */
98 public function updateTableRow(string $tableName, array $inputRow): array
99 {
100 $tablePayload = $this->payload[$tableName];
101
102 foreach ($tablePayload['fields'] as $field) {
103 if (strpos($inputRow[$field], '{"x":') === 0) {
104 $file = $this->getFile($inputRow, $tablePayload['fileReferenceField'] ?: 'uid_local');
105 $cropArray = json_decode($inputRow[$field], true);
106 if (is_array($cropArray)) {
107 $cropArea = Area::createFromConfiguration(json_decode($inputRow[$field], true));
108 $cropVariantCollectionConfig = [
109 'default' => [
110 'cropArea' => $cropArea->makeRelativeBasedOnFile($file)->asArray(),
111 ]
112 ];
113 $inputRow[$field] = json_encode($cropVariantCollectionConfig);
114 }
115 }
116 }
117
118 return $inputRow;
119 }
120
121 /**
122 * Retrieves field names grouped per table name having "l10n_mode" set
123 * to a relevant value that shall be migrated in database records.
124 *
125 * Resulting array is structured like this:
126 * + fields: [field a, field b, ...]
127 * + sources
128 * + source uid: [localization uid, localization uid, ...]
129 *
130 * @param string $tableName Table name
131 * @return array Payload information for this table
132 * @throws \RuntimeException
133 */
134 protected function getPayloadForTable(string $tableName): array
135 {
136 if (!is_array($GLOBALS['TCA'][$tableName])) {
137 throw new \RuntimeException(
138 'Globals TCA of given table name must exist',
139 1485386982
140 );
141 }
142 $tableDefinition = $GLOBALS['TCA'][$tableName];
143
144 if (
145 empty($tableDefinition['columns'])
146 || !is_array($tableDefinition['columns'])
147 ) {
148 return [];
149 }
150
151 $fields = [];
152 $fileReferenceField = null;
153 foreach ($tableDefinition['columns'] as $fieldName => $fieldConfiguration) {
154 if (
155 !empty($fieldConfiguration['config']['type'])
156 && $fieldConfiguration['config']['type'] === 'group'
157 && !empty($fieldConfiguration['config']['internal_type'])
158 && $fieldConfiguration['config']['internal_type'] === 'db'
159 && !empty($fieldConfiguration['config']['allowed'])
160 && $fieldConfiguration['config']['allowed'] === 'sys_file'
161 ) {
162 $fileReferenceField = $fieldName;
163 }
164 if (
165 !empty($fieldConfiguration['config']['type'])
166 && $fieldConfiguration['config']['type'] === 'imageManipulation'
167 ) {
168 $fields[] = $fieldName;
169 }
170 }
171
172 if (empty($fields)) {
173 return [];
174 }
175
176 $payload = [];
177 $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
178
179 foreach ($fields as $fieldName) {
180 $queryBuilder = $connectionPool->getQueryBuilderForTable($tableName);
181 $queryBuilder->getRestrictions()->removeAll();
182
183 $query = $queryBuilder
184 ->from($tableName)
185 ->count($fieldName)
186 ->where(
187 $queryBuilder->expr()->like(
188 $fieldName,
189 $queryBuilder->createNamedParameter('{"x":%', \PDO::PARAM_STR)
190 )
191 );
192 if ((int)$query->execute()->fetchColumn(0) > 0) {
193 $payload['fields'][] = $fieldName;
194 if (isset($fileReferenceField)) {
195 $payload['fileReferenceField'] = $fileReferenceField;
196 } else {
197 $payload['fileReferenceField'] = null;
198 }
199 }
200 }
201 return $payload;
202 }
203
204 /**
205 * Get file object
206 *
207 * @param array $row
208 * @param string $fieldName
209 * @return null|\TYPO3\CMS\Core\Resource\File
210 */
211 private function getFile(array $row, $fieldName)
212 {
213 $file = null;
214 $fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : null;
215 if (is_array($fileUid) && isset($fileUid[0]['uid'])) {
216 $fileUid = $fileUid[0]['uid'];
217 }
218 if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
219 try {
220 $file = ResourceFactory::getInstance()->getFileObject($fileUid);
221 } catch (FileDoesNotExistException $e) {
222 } catch (\InvalidArgumentException $e) {
223 }
224 }
225 return $file;
226 }
227 }