[!!!][DB][BUGFIX] Update all storages in upgrade wizard
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Updates / FileIdentifierHashUpdate.php
1 <?php
2 namespace TYPO3\CMS\Install\Updates;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2013 Andreas Wolf <andreas.wolf@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 use TYPO3\CMS\Core\Resource\ResourceStorage;
28 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
29
30 /**
31 * Class FileIdentifierHashUpdate adds IdentifierHashes
32 */
33 class FileIdentifierHashUpdate extends AbstractUpdate {
34
35 /**
36 * @var string
37 */
38 protected $title = 'Add the file identifier hash to existing sys_file records and update the settings for local storages';
39
40 /**
41 * @var \TYPO3\CMS\Core\Database\DatabaseConnection
42 */
43 protected $db;
44
45 /**
46 * @var array
47 */
48 protected $sqlQueries = array();
49
50 /**
51 * @var ResourceStorage[]
52 */
53 protected $storages;
54
55 /**
56 * @var \TYPO3\CMS\Core\Resource\StorageRepository
57 */
58 protected $storageRepository;
59
60 /**
61 * Creates this object
62 */
63 public function __construct() {
64 $this->db = $GLOBALS['TYPO3_DB'];
65 }
66
67 /**
68 * Initialize the storage repository.
69 */
70 public function init() {
71 $this->storageRepository = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\StorageRepository');
72 $this->storages = $this->storageRepository->findAll();
73 // Add default storage for core files
74 $this->storages[] = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getStorageObject(0);
75 }
76
77 /**
78 * Checks if an update is needed.
79 *
80 * @param string &$description The description for the update
81 * @return boolean TRUE if an update is needed, FALSE otherwise
82 */
83 public function checkForUpdate(&$description) {
84 $description = 'Add file identifier hash to sys_file records, where it is missing. Additionally upgrade storage configurations.';
85 $unhashedFileCount = $this->db->exec_SELECTcountRows(
86 'uid',
87 'sys_file',
88 'identifier_hash = "" OR folder_hash = ""'
89 );
90
91 $unmigratedStorageCount = $this->db->exec_SELECTcountRows(
92 'uid',
93 'sys_file_storage',
94 'driver = "Local" AND configuration NOT LIKE "%caseSensitive%"'
95 );
96
97 return $unhashedFileCount > 0 || $unmigratedStorageCount > 0;
98 }
99
100 /**
101 * Performs the database update.
102 *
103 * @param array &$dbQueries Queries done in this update
104 * @param mixed &$customMessages Custom messages
105 * @return boolean TRUE on success, FALSE on error
106 */
107 public function performUpdate(array &$dbQueries, &$customMessages) {
108 $this->init();
109 foreach ($this->storages as $storage) {
110 $dbQueries = array_merge($dbQueries, $this->updateIdentifierHashesForStorage($storage));
111 }
112
113 $dbQueries = array_merge($dbQueries, $this->migrateStorages());
114
115 $this->markWizardAsDone();
116 return TRUE;
117 }
118
119 /**
120 * @return array
121 */
122 protected function migrateStorages() {
123 $dbQueries = array();
124 $unmigratedStorages = $this->db->exec_SELECTgetRows(
125 'uid, configuration',
126 'sys_file_storage',
127 'driver = "Local" AND configuration NOT LIKE "%caseSensitive%"'
128 );
129
130 /** @var $flexObj \TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools */
131 $flexObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\FlexForm\\FlexFormTools');
132
133
134 foreach ($unmigratedStorages as $storage) {
135 $flexFormXml = $storage['configuration'];
136 $configurationArray = \TYPO3\CMS\Core\Utility\GeneralUtility::xml2array($flexFormXml);
137
138 $caseSensitive = $this->testCaseSensitivity(
139 $configurationArray['data']['sDEF']['lDEF']['pathType']['vDEF'] == 'relative' ?
140 PATH_site . $configurationArray['data']['sDEF']['lDEF']['basePath']['vDEF'] :
141 $configurationArray['data']['sDEF']['lDEF']['basePath']['vDEF']
142 );
143 $configurationArray['data']['sDEF']['lDEF']['caseSensitive'] = array('vDEF' => $caseSensitive);
144
145 $configuration = $flexObj->flexArray2Xml($configurationArray);
146 $dbQueries[] = $query = $this->db->UPDATEquery(
147 'sys_file_storage',
148 'uid=' . $storage['uid'],
149 array(
150 'configuration' => $configuration
151 )
152 );
153 $this->db->sql_query($query);
154 }
155 return $dbQueries;
156 }
157
158 /**
159 * Creates file identifier hashes for a single storage.
160 *
161 * @param ResourceStorage $storage The storage to update
162 * @return array The executed database queries
163 */
164 protected function updateIdentifierHashesForStorage(ResourceStorage $storage) {
165 $queries = array();
166
167 if (!ExtensionManagementUtility::isLoaded('dbal')) {
168 // if DBAL is not loaded, we're using MySQL and can thus use their
169 // SHA1() function
170 if ($storage->usesCaseSensitiveIdentifiers()) {
171 $updateCall = 'SHA1(identifier)';
172 } else {
173 $updateCall = 'SHA1(LOWER(identifier))';
174 }
175 $queries[] = $query = sprintf(
176 'UPDATE sys_file SET identifier_hash = %s WHERE storage=%d',
177 $updateCall,
178 $storage->getUid()
179 );
180 $this->db->sql_query($query);
181
182 // folder hashes cannot be done with one call: so do it manually
183 $files = $this->db->exec_SELECTgetRows('uid, storage, identifier', 'sys_file',
184 sprintf('storage=%d AND folder_hash=""', $storage->getUid())
185 );
186
187 foreach ($files as $file) {
188 $folderHash = $storage->hashFileIdentifier($storage->getFolderIdentifierFromFileIdentifier($file['identifier']));
189
190 $queries[] = $query = $this->db->UPDATEquery(
191 'sys_file',
192 'uid=' . $file['uid'],
193 array(
194 'folder_hash' => $folderHash
195 )
196 );
197
198 $this->db->sql_query($query);
199 }
200 } else {
201 // manually hash the identifiers when using DBAL
202 $files = $this->db->exec_SELECTgetRows('uid, storage, identifier', 'sys_file',
203 sprintf('storage=%d AND identifier_hash=""', $storage->getUid())
204 );
205
206 foreach ($files as $file) {
207 $hash = $storage->hashFileIdentifier($file['identifier']);
208 $folderHash = $storage->hashFileIdentifier($storage->getFolderIdentifierFromFileIdentifier($file['identifier']));
209
210 $queries[] = $query = $this->db->UPDATEquery(
211 'sys_file',
212 'uid=' . $file['uid'],
213 array(
214 'identifier_hash' => $hash,
215 'folder_hash' => $folderHash
216 )
217 );
218
219 $this->db->sql_query($query);
220 }
221 }
222
223 return $queries;
224 }
225
226
227 /**
228 * Test if the local filesystem is case sensitive
229 *
230 * @param $absolutePath
231 * @return boolean
232 */
233 protected function testCaseSensitivity($absolutePath) {
234 $caseSensitive = TRUE;
235 $path = rtrim($absolutePath, '/') . '/aAbB';
236 $testFileExists = file_exists($path);
237
238 // create test file
239 if (!$testFileExists) {
240 @touch($path);
241 }
242
243 // do the actual sensitivity check
244 if (file_exists(strtoupper($path)) && file_exists(strtolower($path))) {
245 $caseSensitive = FALSE;
246 }
247
248 // clean filesystem
249 if (!$testFileExists) {
250 @unlink($path);
251 }
252
253 return $caseSensitive;
254 }
255 }