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