TYPO3 CMS  TYPO3_8-7
ProcessedFileChecksumUpdate.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
22 
27 {
31  protected $title = '[Optional] Update sys_file_processedfile records to match new checksum calculation.';
32 
39  public function checkForUpdate(&$description)
40  {
41  if ($this->isWizardDone()) {
42  return false;
43  }
44 
45  $execute = false;
46 
47  // Check if there is a registry entry from a former run that may have been stopped
48  $registry = GeneralUtility::makeInstance(Registry::class);
49  $registryEntry = $registry->get('core', 'ProcessedFileChecksumUpdate');
50  if ($registryEntry !== null) {
51  $execute = true;
52  }
53 
54  // Enable if there are non empty sys_file_processedfile entries
55  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
56  ->getQueryBuilderForTable('sys_file_processedfile');
57  $incompleteCount = $queryBuilder->count('uid')
58  ->from('sys_file_processedfile')
59  ->orWhere(
60  $queryBuilder->expr()->notIn('identifier', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)),
61  $queryBuilder->expr()->isNull('width'),
62  $queryBuilder->expr()->isNull('height')
63  )->execute()->fetchColumn(0);
64  if ((bool)$incompleteCount) {
65  $execute = true;
66  }
67 
68  if ($execute) {
69  $description = 'The checksum calculation for processed files (image thumbnails) has been changed with'
70  . ' TYPO3 CMS 7.3 and 6.2.13. This means that your processed files need to be updated, if you update'
71  . ' from versions <strong>below TYPO3 CMS 7.3 or 6.2.13</strong>.<br />'
72  . 'This can either happen on demand, when the processed file is first needed, or by executing this'
73  . ' wizard, which updates all processed images at once.<br />'
74  . '<strong>Important:</strong> If you have lots of processed files, you should prefer using this'
75  . ' wizard, otherwise this might cause a lot of work for your server.';
76  }
77 
78  return $execute;
79  }
80 
88  public function performUpdate(array &$databaseQueries, &$customMessage)
89  {
90  $registry = GeneralUtility::makeInstance(Registry::class);
91  $factory = GeneralUtility::makeInstance(ResourceFactory::class);
92  $fileConnection = GeneralUtility::makeInstance(ConnectionPool::class)
93  ->getConnectionForTable('sys_file_processedfile');
94 
95  $firstUid = $registry->get('core', 'ProcessedFileChecksumUpdate');
96 
97  // Remove all invalid records which hold NULL values
98  $queryBuilder = $fileConnection->createQueryBuilder();
99  $queryBuilder->delete('sys_file_processedfile')
100  ->orWhere(
101  $queryBuilder->expr()->isNull('width'),
102  $queryBuilder->expr()->isNull('height')
103  )
104  ->execute();
105 
106  // Get all other rows
107  $queryBuilder = $fileConnection->createQueryBuilder();
108  $queryBuilder = $queryBuilder->select('*')
109  ->from('sys_file_processedfile')
110  ->orderBy('uid');
111  // If there was a start trigger, use it
112  if ($firstUid !== null && (int)$firstUid > 0) {
113  $queryBuilder->where(
114  $queryBuilder->expr()->gt(
115  'uid',
116  $queryBuilder->createNamedParameter($firstUid, \PDO::PARAM_INT)
117  )
118  );
119  }
120  $statement = $queryBuilder->execute();
121  while ($processedFileRow = $statement->fetch()) {
122  try {
123  $storage = $factory->getStorageObject($processedFileRow['storage']);
124  } catch (\InvalidArgumentException $e) {
125  $storage = null;
126  }
127  if (!$storage) {
128  // Invalid storage, delete record, we can't take care of the associated file
129  $fileConnection->delete('sys_file_processedfile', ['uid' => (int)$processedFileRow['uid']]);
130  $registry->set('core', 'ProcessedFileChecksumUpdate', (int)$processedFileRow['uid']);
131  continue;
132  }
133 
134  if ($storage->getDriverType() !== 'Local') {
135  // Non-local storage, we can't treat this, skip the record and mark it done
136  $registry->set('core', 'ProcessedFileChecksumUpdate', (int)$processedFileRow['uid']);
137  continue;
138  }
139 
140  $configuration = $storage->getConfiguration();
141  if ($configuration['pathType'] === 'relative') {
142  $absoluteBasePath = PATH_site . $configuration['basePath'];
143  } else {
144  $absoluteBasePath = $configuration['basePath'];
145  }
146  $filePath = rtrim($absoluteBasePath, '/') . '/' . ltrim($processedFileRow['identifier'], '/');
147 
148  try {
149  $originalFile = $factory->getFileObject($processedFileRow['original']);
150  } catch (\Exception $e) {
151  // No original file there anymore, delete local file
152  @unlink($filePath);
153  $fileConnection->delete('sys_file_processedfile', ['uid' => (int)$processedFileRow['uid']]);
154  $registry->set('core', 'ProcessedFileChecksumUpdate', (int)$processedFileRow['uid']);
155  continue;
156  }
157 
158  $processedFileObject = new ProcessedFile($originalFile, '', [], $processedFileRow);
159 
160  // calculate new checksum and name
161  $newChecksum = $processedFileObject->calculateChecksum();
162 
163  // if the checksum already matches, there is nothing to do
164  if ($newChecksum !== $processedFileRow['checksum']) {
165  $newName = str_replace($processedFileRow['checksum'], $newChecksum, $processedFileRow['name']);
166  $newIdentifier = str_replace(
167  $processedFileRow['checksum'],
168  $newChecksum,
169  $processedFileRow['identifier']
170  );
171  $newFilePath = str_replace($processedFileRow['checksum'], $newChecksum, $filePath);
172 
173  // rename file
174  if (@rename($filePath, $newFilePath)) {
175  // save result back into database
176  $fields = [
177  'tstamp' => time(),
178  'identifier' => $newIdentifier,
179  'name' => $newName,
180  'checksum' => $newChecksum
181  ];
182  $fileConnection->update(
183  'sys_file_processedfile',
184  $fields,
185  ['uid' => (int)$processedFileRow['uid']]
186  );
187  }
188  // if the rename of the file failed, keep the record, but do not bother with it again
189  }
190  $registry->set('core', 'ProcessedFileChecksumUpdate', (int)$processedFileRow['uid']);
191  }
192 
193  $registry->remove('core', 'ProcessedFileChecksumUpdate');
194  $this->markWizardAsDone();
195  return true;
196  }
197 }
static makeInstance($className,... $constructorArguments)
$fields
Definition: pages.php:4