TYPO3 CMS  TYPO3_6-2
TceformsUpdateWizard.php
Go to the documentation of this file.
1 <?php
3 
20 
28 
33  const RECORDS_PER_QUERY = 1000;
34 
38  protected $title = 'Migrate all file relations from tt_content.image and pages.media';
39 
43  protected $storage;
44 
48  protected $logger;
49 
53  protected $database;
54 
59  protected $tables = array(
60  'tt_content' => array(
61  'image' => array(
62  'sourcePath' => 'uploads/pics/',
63  // Relative to fileadmin
64  'targetPath' => '_migrated/pics/',
65  'titleTexts' => 'titleText',
66  'captions' => 'imagecaption',
67  'links' => 'image_link',
68  'alternativeTexts' => 'altText'
69  )
70  ),
71  'pages' => array(
72  'media' => array(
73  'sourcePath' => 'uploads/media/',
74  // Relative to fileadmin
75  'targetPath' => '_migrated/media/'
76  )
77  ),
78  'pages_language_overlay' => array(
79  'media' => array(
80  'sourcePath' => 'uploads/media/',
81  // Relative to fileadmin
82  'targetPath' => '_migrated/media/'
83  )
84  )
85  );
86 
90  protected $registry;
91 
95  protected $registryNamespace = 'TceformsUpdateWizard';
96 
100  protected $recordOffset = array();
101 
105  public function __construct() {
107  $logManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Log\\LogManager');
108  $this->logger = $logManager->getLogger(__CLASS__);
109  $this->database = $GLOBALS['TYPO3_DB'];
110  }
111 
115  public function init() {
117  $storageRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\StorageRepository');
118  $storages = $storageRepository->findAll();
119  $this->storage = $storages[0];
120  $this->registry = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
121  $this->recordOffset = $this->registry->get($this->registryNamespace, 'recordOffset', array());
122  }
123 
130  public function checkForUpdate(&$description) {
131  $description = 'This update wizard goes through all files that are referenced in the tt_content.image and '
132  . 'pages.media / pages_language_overlay.media field and adds the files to the new File Index.<br />'
133  . 'It also moves the files from uploads/ to the fileadmin/_migrated/ path.<br /><br />'
134  . 'This update wizard can be called multiple times in case it didn\'t finish after running once.';
135 
136  if ($this->versionNumber < 6000000) {
137  // Nothing to do
138  return FALSE;
139  }
140 
141  $finishedFields = $this->getFinishedFields();
142  if (count($finishedFields) === 0) {
143  // Nothing done yet, so there's plenty of work left
144  return TRUE;
145  }
146 
147  $numberOfFieldsToMigrate = 0;
148  foreach ($this->tables as $table => $tableConfiguration) {
149  // find all additional fields we should get from the database
150  foreach (array_keys($tableConfiguration) as $fieldToMigrate) {
151  $fieldKey = $table . ':' . $fieldToMigrate;
152  if (!in_array($fieldKey, $finishedFields)) {
153  $numberOfFieldsToMigrate++;
154  }
155  }
156  }
157  return $numberOfFieldsToMigrate > 0;
158  }
159 
167  public function performUpdate(array &$dbQueries, &$customMessages) {
168  if ($this->versionNumber < 6000000) {
169  // Nothing to do
170  return TRUE;
171  }
172  try {
173  $this->init();
174  $finishedFields = $this->getFinishedFields();
175  foreach ($this->tables as $table => $tableConfiguration) {
176  // find all additional fields we should get from the database
177  foreach ($tableConfiguration as $fieldToMigrate => $fieldConfiguration) {
178  $fieldKey = $table . ':' . $fieldToMigrate;
179  if (in_array($fieldKey, $finishedFields)) {
180  // this field was already migrated
181  continue;
182  }
183  $fieldsToGet = array($fieldToMigrate);
184  if (isset($fieldConfiguration['titleTexts'])) {
185  $fieldsToGet[] = $fieldConfiguration['titleTexts'];
186  }
187  if (isset($fieldConfiguration['alternativeTexts'])) {
188  $fieldsToGet[] = $fieldConfiguration['alternativeTexts'];
189  }
190  if (isset($fieldConfiguration['captions'])) {
191  $fieldsToGet[] = $fieldConfiguration['captions'];
192  }
193  if (isset($fieldConfiguration['links'])) {
194  $fieldsToGet[] = $fieldConfiguration['links'];
195  }
196 
197  if (!isset($this->recordOffset[$table])) {
198  $this->recordOffset[$table] = 0;
199  }
200 
201  do {
202  $limit = $this->recordOffset[$table] . ',' . self::RECORDS_PER_QUERY;
203  $records = $this->getRecordsFromTable($table, $fieldToMigrate, $fieldsToGet, $limit);
204  foreach ($records as $record) {
205  $this->migrateField($table, $record, $fieldToMigrate, $fieldConfiguration, $customMessages);
206  }
207  $this->registry->set($this->registryNamespace, 'recordOffset', $this->recordOffset);
208  } while (count($records) === self::RECORDS_PER_QUERY);
209 
210  // add the field to the "finished fields" if things didn't fail above
211  if (is_array($records)) {
212  $finishedFields[] = $fieldKey;
213  }
214  }
215  }
216  $this->markWizardAsDone(implode(',', $finishedFields));
217  $this->registry->remove($this->registryNamespace, 'recordOffset');
218  } catch (\Exception $e) {
219  $customMessages .= PHP_EOL . $e->getMessage();
220  }
221  return empty($customMessages);
222  }
223 
230  protected function getFinishedFields() {
231  $className = 'TYPO3\\CMS\\Install\\Updates\\TceformsUpdateWizard';
232  return isset($GLOBALS['TYPO3_CONF_VARS']['INSTALL']['wizardDone'][$className])
233  ? explode(',', $GLOBALS['TYPO3_CONF_VARS']['INSTALL']['wizardDone'][$className])
234  : array();
235  }
236 
248  protected function getRecordsFromTable($table, $fieldToMigrate, $relationFields, $limit) {
249  $fields = implode(',', array_merge($relationFields, array('uid', 'pid')));
250  $deletedCheck = isset($GLOBALS['TCA'][$table]['ctrl']['delete'])
251  ? ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['delete'] . '=0'
252  : '';
253  $where = $fieldToMigrate . ' IS NOT NULL'
254  . ' AND ' . $fieldToMigrate . ' != \'\''
255  . ' AND CAST(CAST(' . $fieldToMigrate . ' AS DECIMAL) AS CHAR) <> CAST(' . $fieldToMigrate . ' AS CHAR)'
256  . $deletedCheck;
257  $result = $this->database->exec_SELECTgetRows($fields, $table, $where, '', 'uid', $limit);
258  if ($result === NULL) {
259  throw new \RuntimeException('Database query failed. Error was: ' . $this->database->sql_error());
260  }
261  return $result;
262  }
263 
275  protected function migrateField($table, $row, $fieldname, $fieldConfiguration, &$customMessages) {
276  $titleTextContents = array();
277  $alternativeTextContents = array();
278  $captionContents = array();
279  $linkContents = array();
280 
281  $fieldItems = GeneralUtility::trimExplode(',', $row[$fieldname], TRUE);
282  if (empty($fieldItems) || is_numeric($row[$fieldname])) {
283  return array();
284  }
285  if (isset($fieldConfiguration['titleTexts'])) {
286  $titleTextField = $fieldConfiguration['titleTexts'];
287  $titleTextContents = explode(LF, $row[$titleTextField]);
288  }
289 
290  if (isset($fieldConfiguration['alternativeTexts'])) {
291  $alternativeTextField = $fieldConfiguration['alternativeTexts'];
292  $alternativeTextContents = explode(LF, $row[$alternativeTextField]);
293  }
294  if (isset($fieldConfiguration['captions'])) {
295  $captionField = $fieldConfiguration['captions'];
296  $captionContents = explode(LF, $row[$captionField]);
297  }
298  if (isset($fieldConfiguration['links'])) {
299  $linkField = $fieldConfiguration['links'];
300  $linkContents = explode(LF, $row[$linkField]);
301  }
302  $fileadminDirectory = rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') . '/';
303  $queries = array();
304  $i = 0;
305 
306  if (!PATH_site) {
307  throw new \Exception('PATH_site was undefined.');
308  }
309 
310  $storageUid = (int)$this->storage->getUid();
311 
312  foreach ($fieldItems as $item) {
313  $fileUid = NULL;
314  $sourcePath = PATH_site . $fieldConfiguration['sourcePath'] . $item;
315  $targetDirectory = PATH_site . $fileadminDirectory . $fieldConfiguration['targetPath'];
316  $targetPath = $targetDirectory . basename($item);
317 
318  // maybe the file was already moved, so check if the original file still exists
319  if (file_exists($sourcePath)) {
320  if (!is_dir($targetDirectory)) {
321  GeneralUtility::mkdir_deep($targetDirectory);
322  }
323 
324  // see if the file already exists in the storage
325  $fileSha1 = sha1_file($sourcePath);
326 
327  $existingFileRecord = $this->database->exec_SELECTgetSingleRow(
328  'uid',
329  'sys_file',
330  'sha1=' . $this->database->fullQuoteStr($fileSha1, 'sys_file') . ' AND storage=' . $storageUid
331  );
332  // the file exists, the file does not have to be moved again
333  if (is_array($existingFileRecord)) {
334  $fileUid = $existingFileRecord['uid'];
335  } else {
336  // just move the file (no duplicate)
337  rename($sourcePath, $targetPath);
338  }
339  }
340 
341  if ($fileUid === NULL) {
342  // get the File object if it hasn't been fetched before
343  try {
344  // if the source file does not exist, we should just continue, but leave a message in the docs;
345  // ideally, the user would be informed after the update as well.
347  $file = $this->storage->getFile($fieldConfiguration['targetPath'] . $item);
348  $fileUid = $file->getUid();
349 
350  } catch (\InvalidArgumentException $e) {
351 
352  // no file found, no reference can be set
353  $this->logger->notice(
354  'File ' . $fieldConfiguration['sourcePath'] . $item . ' does not exist. Reference was not migrated.',
355  array('table' => $table, 'record' => $row, 'field' => $fieldname)
356  );
357 
358  $format = 'File \'%s\' does not exist. Referencing field: %s.%d.%s. The reference was not migrated.';
359  $message = sprintf($format, $fieldConfiguration['sourcePath'] . $item, $table, $row['uid'], $fieldname);
360  $customMessages .= PHP_EOL . $message;
361 
362  continue;
363  }
364  }
365 
366  if ($fileUid > 0) {
367  $fields = array(
368  // TODO add sorting/sorting_foreign
369  'fieldname' => $fieldname,
370  'table_local' => 'sys_file',
371  // the sys_file_reference record should always placed on the same page
372  // as the record to link to, see issue #46497
373  'pid' => ($table === 'pages' ? $row['uid'] : $row['pid']),
374  'uid_foreign' => $row['uid'],
375  'uid_local' => $fileUid,
376  'tablenames' => $table,
377  'crdate' => time(),
378  'tstamp' => time(),
379  'sorting' => ($i + 256),
380  'sorting_foreign' => $i,
381  );
382  if (isset($titleTextField)) {
383  $fields['title'] = trim($titleTextContents[$i]);
384  }
385  if (isset($alternativeTextField)) {
386  $fields['alternative'] = trim($alternativeTextContents[$i]);
387  }
388  if (isset($captionField)) {
389  $fields['description'] = trim($captionContents[$i]);
390  }
391  if (isset($linkField)) {
392  $fields['link'] = trim($linkContents[$i]);
393  }
394  $this->database->exec_INSERTquery('sys_file_reference', $fields);
395  $queries[] = str_replace(LF, ' ', $this->database->debug_lastBuiltQuery);
396  ++$i;
397  }
398  }
399 
400  // Update referencing table's original field to now contain the count of references,
401  // but only if all new references could be set
402  if ($i === count($fieldItems)) {
403  $this->database->exec_UPDATEquery($table, 'uid=' . $row['uid'], array($fieldname => $i));
404  $queries[] = str_replace(LF, ' ', $this->database->debug_lastBuiltQuery);
405  } else {
406  $this->recordOffset[$table]++;
407  }
408  return $queries;
409  }
410 }
static mkdir_deep($directory, $deepDirectory='')
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
getRecordsFromTable($table, $fieldToMigrate, $relationFields, $limit)
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
performUpdate(array &$dbQueries, &$customMessages)