‪TYPO3CMS  10.4
BackendLayoutIconUpdateWizard.php
Go to the documentation of this file.
1 <?php
2 
3 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 
19 
20 use Doctrine\DBAL\DBALException;
21 use Psr\Log\LoggerAwareInterface;
22 use Psr\Log\LoggerAwareTrait;
23 use Symfony\Component\Console\Output\OutputInterface;
32 
39 {
40  use LoggerAwareTrait;
41 
45  protected ‪$output;
46 
50  protected ‪$storage;
51 
57  protected ‪$table = 'backend_layout';
58 
64  protected ‪$fieldToMigrate = 'icon';
65 
71  protected ‪$sourcePath = 'uploads/media/';
72 
79  protected ‪$targetPath = '_migrated/backend_layouts/';
80 
84  public function ‪getIdentifier(): string
85  {
86  return 'backendLayoutIcons';
87  }
88 
92  public function ‪getTitle(): string
93  {
94  return 'Migrate all file relations from backend_layout.icon to sys_file_references';
95  }
96 
100  public function ‪getDescription(): string
101  {
102  return 'This update wizard goes through all files that are referenced in the'
103  . ' backend_layout.icon field and adds the files to the FAL File Index.'
104  . ' It also moves the files from uploads/ to the fileadmin/_migrated/ path.';
105  }
106 
110  public function ‪updateNecessary(): bool
111  {
112  return !empty($this->‪getRecordsFromTable());
113  }
114 
118  public function ‪getPrerequisites(): array
119  {
120  return [
121  DatabaseUpdatedPrerequisite::class
122  ];
123  }
124 
128  public function ‪setOutput(OutputInterface ‪$output): void
129  {
130  $this->output = ‪$output;
131  }
132 
138  public function ‪executeUpdate(): bool
139  {
140  $result = true;
141  try {
142  $storages = GeneralUtility::makeInstance(StorageRepository::class)->findAll();
143  $this->storage = $storages[0];
144  $records = $this->‪getRecordsFromTable();
145  foreach ($records as $record) {
146  $this->‪migrateField($record);
147  }
148  } catch (\‪Exception $e) {
149  // If something goes wrong, migrateField() logs an error
150  $result = false;
151  }
152  return $result;
153  }
154 
162  protected function ‪getRecordsFromTable()
163  {
164  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
165  $queryBuilder = $connectionPool->getQueryBuilderForTable($this->table);
166  $queryBuilder->getRestrictions()->removeAll();
167  try {
168  return $queryBuilder
169  ->select('uid', 'pid', $this->fieldToMigrate)
170  ->from($this->table)
171  ->where(
172  $queryBuilder->expr()->isNotNull($this->fieldToMigrate),
173  $queryBuilder->expr()->neq(
174  $this->fieldToMigrate,
175  $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
176  ),
177  $queryBuilder->expr()->comparison(
178  'CAST(CAST(' . $queryBuilder->quoteIdentifier($this->fieldToMigrate) . ' AS DECIMAL) AS CHAR)',
180  'CAST(' . $queryBuilder->quoteIdentifier($this->fieldToMigrate) . ' AS CHAR)'
181  )
182  )
183  ->orderBy('uid')
184  ->execute()
185  ->fetchAll();
186  } catch (DBALException $e) {
187  throw new \RuntimeException(
188  'Database query failed. Error was: ' . $e->getPrevious()->getMessage(),
189  1511950673
190  );
191  }
192  }
193 
200  protected function ‪migrateField($row)
201  {
202  $fieldItems = ‪GeneralUtility::trimExplode(',', $row[$this->fieldToMigrate], true);
203  if (empty($fieldItems) || is_numeric($row[$this->fieldToMigrate])) {
204  return;
205  }
206  $fileadminDirectory = rtrim(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/';
207  $i = 0;
208 
209  $storageUid = (int)$this->storage->getUid();
210  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
211 
212  foreach ($fieldItems as $item) {
213  $fileUid = null;
214  ‪$sourcePath = ‪Environment::getPublicPath() . '/' . $this->sourcePath . $item;
215  $targetDirectory = ‪Environment::getPublicPath() . '/' . $fileadminDirectory . ‪$this->targetPath;
216  ‪$targetPath = $targetDirectory . ‪PathUtility::basenameDuringBootstrap($item);
217 
218  // maybe the file was already moved, so check if the original file still exists
219  if (file_exists(‪$sourcePath)) {
220  if (!is_dir($targetDirectory)) {
221  ‪GeneralUtility::mkdir_deep($targetDirectory);
222  }
223 
224  // see if the file already exists in the storage
225  $fileSha1 = sha1_file(‪$sourcePath);
226 
227  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file');
228  $queryBuilder->getRestrictions()->removeAll();
229  $existingFileRecord = $queryBuilder->select('uid')->from('sys_file')->where(
230  $queryBuilder->expr()->eq(
231  'sha1',
232  $queryBuilder->createNamedParameter($fileSha1, \PDO::PARAM_STR)
233  ),
234  $queryBuilder->expr()->eq(
235  'storage',
236  $queryBuilder->createNamedParameter($storageUid, \PDO::PARAM_INT)
237  )
238  )->execute()->fetch();
239 
240  // the file exists, the file does not have to be moved again
241  if (is_array($existingFileRecord)) {
242  $fileUid = $existingFileRecord['uid'];
243  } else {
244  // just move the file (no duplicate)
246  }
247  }
248 
249  if ($fileUid === null) {
250  // get the File object if it hasn't been fetched before
251  try {
252  // if the source file does not exist, we should just continue, but leave a message in the docs;
253  // ideally, the user would be informed after the update as well.
255  $file = $this->storage->getFile($this->targetPath . $item);
256  $fileUid = $file->getUid();
257  } catch (\InvalidArgumentException $e) {
258  // no file found, no reference can be set
259  $this->logger->notice(
260  'File ' . $this->sourcePath . $item . ' does not exist. Reference was not migrated.',
261  [
262  'table' => $this->table,
263  'record' => $row,
264  'field' => $this->fieldToMigrate,
265  ]
266  );
267  $format = 'File \'%s\' does not exist. Referencing field: %s.%d.%s. The reference was not migrated.';
268  $this->output->writeln(sprintf(
269  $format,
270  $this->sourcePath . $item,
271  $this->table,
272  $row['uid'],
273  $this->fieldToMigrate
274  ));
275  continue;
276  }
277  }
278 
279  if ($fileUid > 0) {
280  ‪$fields = [
281  'fieldname' => ‪$this->fieldToMigrate,
282  'table_local' => 'sys_file',
283  'pid' => $this->table === 'pages' ? $row['uid'] : $row['pid'],
284  'uid_foreign' => $row['uid'],
285  'uid_local' => $fileUid,
286  'tablenames' => ‪$this->table,
287  'crdate' => time(),
288  'tstamp' => time(),
289  'sorting_foreign' => $i,
290  ];
291 
292  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file_reference');
293  $queryBuilder->insert('sys_file_reference')->values(‪$fields)->execute();
294  ++$i;
295  }
296  }
297 
298  // Update referencing table's original field to now contain the count of references,
299  // but only if all new references could be set
300  if ($i === count($fieldItems)) {
301  $queryBuilder = $connectionPool->getQueryBuilderForTable($this->table);
302  $queryBuilder->update($this->table)->where(
303  $queryBuilder->expr()->eq(
304  'uid',
305  $queryBuilder->createNamedParameter($row['uid'], \PDO::PARAM_INT)
306  )
307  )->set($this->fieldToMigrate, $i)->execute();
308  }
309  }
310 }
‪TYPO3\CMS\Core\Utility\PathUtility\basenameDuringBootstrap
‪static string basenameDuringBootstrap($path)
Definition: PathUtility.php:291
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:24
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder
Definition: ExpressionBuilder.php:35
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:180
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\getTitle
‪string getTitle()
Definition: BackendLayoutIconUpdateWizard.php:86
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\$output
‪OutputInterface $output
Definition: BackendLayoutIconUpdateWizard.php:44
‪TYPO3\CMS\Install\Updates\ChattyInterface
Definition: ChattyInterface.php:26
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\$targetPath
‪string $targetPath
Definition: BackendLayoutIconUpdateWizard.php:73
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard
Definition: BackendLayoutIconUpdateWizard.php:39
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Install\Updates
Definition: AbstractDownloadExtensionUpdate.php:16
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\setOutput
‪setOutput(OutputInterface $output)
Definition: BackendLayoutIconUpdateWizard.php:122
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\migrateField
‪migrateField($row)
Definition: BackendLayoutIconUpdateWizard.php:194
‪TYPO3\CMS\Install\Exception
Definition: Exception.php:24
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\getIdentifier
‪string getIdentifier()
Definition: BackendLayoutIconUpdateWizard.php:78
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\getPrerequisites
‪string[] getPrerequisites()
Definition: BackendLayoutIconUpdateWizard.php:112
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:2022
‪TYPO3\CMS\Core\Resource\StorageRepository
Definition: StorageRepository.php:31
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:24
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder\NEQ
‪const NEQ
Definition: ExpressionBuilder.php:37
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Install\Updates\UpgradeWizardInterface
Definition: UpgradeWizardInterface.php:24
‪TYPO3\CMS\Core\Resource\ResourceStorage
Definition: ResourceStorage.php:122
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:40
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\getRecordsFromTable
‪array getRecordsFromTable()
Definition: BackendLayoutIconUpdateWizard.php:156
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\executeUpdate
‪bool executeUpdate()
Definition: BackendLayoutIconUpdateWizard.php:132
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\getDescription
‪string getDescription()
Definition: BackendLayoutIconUpdateWizard.php:94
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\$fieldToMigrate
‪string $fieldToMigrate
Definition: BackendLayoutIconUpdateWizard.php:60
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\$table
‪string $table
Definition: BackendLayoutIconUpdateWizard.php:54
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\updateNecessary
‪bool updateNecessary()
Definition: BackendLayoutIconUpdateWizard.php:104
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\$storage
‪ResourceStorage $storage
Definition: BackendLayoutIconUpdateWizard.php:48
‪TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard\$sourcePath
‪string $sourcePath
Definition: BackendLayoutIconUpdateWizard.php:66