‪TYPO3CMS  9.5
FrontendUserImageUpdateWizard.php
Go to the documentation of this file.
1 <?php
3 
16 use Doctrine\DBAL\DBALException;
17 use Psr\Log\LoggerAwareInterface;
18 use Psr\Log\LoggerAwareTrait;
29 
35 class ‪FrontendUserImageUpdateWizard implements ‪UpgradeWizardInterface, LoggerAwareInterface
36 {
37  use LoggerAwareTrait;
38 
43  const ‪RECORDS_PER_QUERY = 1000;
44 
48  protected ‪$storage;
49 
55  protected ‪$table = 'fe_users';
56 
62  protected ‪$fieldToMigrate = 'image';
63 
69  protected ‪$sourcePath = 'uploads/pics/';
70 
77  protected ‪$targetPath = '_migrated/frontend_users/';
78 
82  protected ‪$registry;
83 
87  protected ‪$registryNamespace = 'FrontendUserImageUpdateWizard';
88 
92  protected ‪$recordOffset = [];
93 
97  public function ‪getIdentifier(): string
98  {
99  return 'frontendUserImageUpdateWizard';
100  }
101 
105  public function ‪getTitle(): string
106  {
107  return 'Migrate all file relations from fe_users.image to sys_file_references';
108  }
109 
113  public function ‪getDescription(): string
114  {
115  return 'This update wizard goes through all files that are referenced in the fe_users.image'
116  . ' field and adds the files to the FAL File Index. It also moves the files from'
117  . ' uploads/ to the fileadmin/_migrated/ path.';
118  }
119 
125  public function ‪updateNecessary(): bool
126  {
127  $this->registry = GeneralUtility::makeInstance(Registry::class);
128  return $this->registry->get($this->registryNamespace, 'recordOffset') === null;
129  }
130 
134  public function ‪getPrerequisites(): array
135  {
136  return [
137  DatabaseUpdatedPrerequisite::class
138  ];
139  }
140 
146  public function ‪executeUpdate(): bool
147  {
148  try {
149  $this->‪init();
150  if (!isset($this->recordOffset[$this->table])) {
151  $this->recordOffset[‪$this->table] = 0;
152  }
153  do {
154  $limit = $this->recordOffset[‪$this->table] . ',' . ‪self::RECORDS_PER_QUERY;
155  $records = $this->‪getRecordsFromTable($limit);
156  foreach ($records as $record) {
157  $this->‪migrateField($record);
158  }
159  $this->registry->set($this->registryNamespace, 'recordOffset', $this->recordOffset);
160  } while (count($records) === self::RECORDS_PER_QUERY);
161 
162  $this->registry->remove($this->registryNamespace, 'recordOffset');
163  } catch (\‪Exception $e) {
164  // Silently catch db errors
165  }
166  return true;
167  }
168 
172  protected function ‪init()
173  {
174  $storages = GeneralUtility::makeInstance(StorageRepository::class)->findAll();
175  $this->storage = $storages[0];
176  $this->registry = GeneralUtility::makeInstance(Registry::class);
177  $this->recordOffset = $this->registry->get($this->registryNamespace, 'recordOffset', []);
178  }
179 
188  protected function ‪getRecordsFromTable($limit)
189  {
190  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
191  $queryBuilder = $connectionPool->getQueryBuilderForTable($this->table);
192  $queryBuilder->getRestrictions()
193  ->removeAll()
194  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
195  try {
196  return $queryBuilder
197  ->select('uid', 'pid', $this->fieldToMigrate)
198  ->from($this->table)
199  ->where(
200  $queryBuilder->expr()->isNotNull($this->fieldToMigrate),
201  $queryBuilder->expr()->neq(
202  $this->fieldToMigrate,
203  $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
204  ),
205  $queryBuilder->expr()->comparison(
206  'CAST(CAST(' . $queryBuilder->quoteIdentifier($this->fieldToMigrate) . ' AS DECIMAL) AS CHAR)',
208  'CAST(' . $queryBuilder->quoteIdentifier($this->fieldToMigrate) . ' AS CHAR)'
209  )
210  )
211  ->orderBy('uid')
212  ->setFirstResult($limit)
213  ->execute()
214  ->fetchAll();
215  } catch (DBALException $e) {
216  throw new \RuntimeException(
217  'Database query failed. Error was: ' . $e->getPrevious()->getMessage(),
218  1476050084
219  );
220  }
221  }
222 
228  protected function ‪migrateField($row)
229  {
230  $fieldItems = GeneralUtility::trimExplode(',', $row[$this->fieldToMigrate], true);
231  if (empty($fieldItems) || is_numeric($row[$this->fieldToMigrate])) {
232  return;
233  }
234  $fileadminDirectory = rtrim(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/';
235  $i = 0;
236 
237  $storageUid = (int)$this->storage->getUid();
238 
239  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
240 
241  foreach ($fieldItems as $item) {
242  $fileUid = null;
243  ‪$sourcePath = ‪Environment::getPublicPath() . '/' . $this->sourcePath . $item;
244  $targetDirectory = ‪Environment::getPublicPath() . '/' . $fileadminDirectory . ‪$this->targetPath;
245  ‪$targetPath = $targetDirectory . ‪PathUtility::basename($item);
246 
247  // maybe the file was already moved, so check if the original file still exists
248  if (file_exists(‪$sourcePath)) {
249  if (!is_dir($targetDirectory)) {
250  GeneralUtility::mkdir_deep($targetDirectory);
251  }
252 
253  // see if the file already exists in the storage
254  $fileSha1 = sha1_file(‪$sourcePath);
255 
256  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file');
257  $queryBuilder->getRestrictions()->removeAll();
258  $existingFileRecord = $queryBuilder->select('uid')->from('sys_file')->where(
259  $queryBuilder->expr()->eq(
260  'sha1',
261  $queryBuilder->createNamedParameter($fileSha1, \PDO::PARAM_STR)
262  ),
263  $queryBuilder->expr()->eq(
264  'storage',
265  $queryBuilder->createNamedParameter($storageUid, \PDO::PARAM_INT)
266  )
267  )->execute()->fetch();
268 
269  // the file exists, the file does not have to be moved again
270  if (is_array($existingFileRecord)) {
271  $fileUid = $existingFileRecord['uid'];
272  } else {
273  // just move the file (no duplicate)
275  }
276  }
277 
278  if ($fileUid === null) {
279  // get the File object if it hasn't been fetched before
280  try {
281  // if the source file does not exist, we should just continue, but leave a message in the docs;
282  // ideally, the user would be informed after the update as well.
284  $file = $this->storage->getFile($this->targetPath . $item);
285  $fileUid = $file->getUid();
286  } catch (\InvalidArgumentException $e) {
287  // no file found, no reference can be set
288  $this->logger->notice(
289  'File ' . $this->sourcePath . $item . ' does not exist. Reference was not migrated.',
290  [
291  'table' => $this->table,
292  'record' => $row,
293  'field' => $this->fieldToMigrate,
294  ]
295  );
296  continue;
297  }
298  }
299 
300  if ($fileUid > 0) {
301  ‪$fields = [
302  'fieldname' => ‪$this->fieldToMigrate,
303  'table_local' => 'sys_file',
304  'pid' => $this->table === 'pages' ? $row['uid'] : $row['pid'],
305  'uid_foreign' => $row['uid'],
306  'uid_local' => $fileUid,
307  'tablenames' => ‪$this->table,
308  'crdate' => time(),
309  'tstamp' => time(),
310  'sorting_foreign' => $i,
311  ];
312 
313  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file_reference');
314  $queryBuilder->insert('sys_file_reference')->values(‪$fields)->execute();
315  ++$i;
316  }
317  }
318 
319  // Update referencing table's original field to now contain the count of references,
320  // but only if all new references could be set
321  if ($i === count($fieldItems)) {
322  $queryBuilder = $connectionPool->getQueryBuilderForTable($this->table);
323  $queryBuilder->update($this->table)->where(
324  $queryBuilder->expr()->eq(
325  'uid',
326  $queryBuilder->createNamedParameter($row['uid'], \PDO::PARAM_INT)
327  )
328  )->set($this->fieldToMigrate, $i)->execute();
329  } else {
330  $this->recordOffset[‪$this->table]++;
331  }
332  }
333 }
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:23
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder
Definition: ExpressionBuilder.php:33
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:153
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\$storage
‪ResourceStorage $storage
Definition: FrontendUserImageUpdateWizard.php:47
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\$targetPath
‪string $targetPath
Definition: FrontendUserImageUpdateWizard.php:72
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:32
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\RECORDS_PER_QUERY
‪const RECORDS_PER_QUERY
Definition: FrontendUserImageUpdateWizard.php:43
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\$table
‪string $table
Definition: FrontendUserImageUpdateWizard.php:53
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\$sourcePath
‪string $sourcePath
Definition: FrontendUserImageUpdateWizard.php:65
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\$registryNamespace
‪string $registryNamespace
Definition: FrontendUserImageUpdateWizard.php:80
‪$fields
‪$fields
Definition: pages.php:4
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:164
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\getRecordsFromTable
‪array getRecordsFromTable($limit)
Definition: FrontendUserImageUpdateWizard.php:180
‪TYPO3\CMS\Install\Updates
Definition: AbstractDownloadExtensionUpdate.php:3
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\getIdentifier
‪string getIdentifier()
Definition: FrontendUserImageUpdateWizard.php:89
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\migrateField
‪migrateField($row)
Definition: FrontendUserImageUpdateWizard.php:220
‪TYPO3\CMS\Install\Exception
Definition: Exception.php:23
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\getTitle
‪string getTitle()
Definition: FrontendUserImageUpdateWizard.php:97
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\$registry
‪Registry $registry
Definition: FrontendUserImageUpdateWizard.php:76
‪TYPO3\CMS\Core\Resource\StorageRepository
Definition: StorageRepository.php:29
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:23
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder\NEQ
‪const NEQ
Definition: ExpressionBuilder.php:35
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\getDescription
‪string getDescription()
Definition: FrontendUserImageUpdateWizard.php:105
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\$fieldToMigrate
‪string $fieldToMigrate
Definition: FrontendUserImageUpdateWizard.php:59
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\executeUpdate
‪bool executeUpdate()
Definition: FrontendUserImageUpdateWizard.php:138
‪TYPO3\CMS\Core\Resource\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\$recordOffset
‪array $recordOffset
Definition: FrontendUserImageUpdateWizard.php:84
‪TYPO3\CMS\Install\Updates\UpgradeWizardInterface
Definition: UpgradeWizardInterface.php:22
‪TYPO3\CMS\Core\Resource\ResourceStorage
Definition: ResourceStorage.php:74
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:26
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:39
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\getPrerequisites
‪string[] getPrerequisites()
Definition: FrontendUserImageUpdateWizard.php:126
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard
Definition: FrontendUserImageUpdateWizard.php:36
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:44
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\init
‪init()
Definition: FrontendUserImageUpdateWizard.php:164
‪TYPO3\CMS\Install\Updates\FrontendUserImageUpdateWizard\updateNecessary
‪bool updateNecessary()
Definition: FrontendUserImageUpdateWizard.php:117