diff -u b/core/modules/migrate_drupal/config/optional/migrate.migration.d7_file.yml b/core/modules/migrate_drupal/config/optional/migrate.migration.d7_file.yml --- b/core/modules/migrate_drupal/config/optional/migrate.migration.d7_file.yml +++ b/core/modules/migrate_drupal/config/optional/migrate.migration.d7_file.yml @@ -1,5 +1,5 @@ -# Every migration that saves into {file_managed} must have the d7_file -# migration as an optional dependency to ensure d7_file runs first. +# Every migration that references a file by fid should specify this migration +# as an optional dependency. id: d7_file label: Drupal 7 files migration_tags: @@ -11,8 +11,8 @@ filename: filename uri: uri filemime: filemime - # filesize is dynamically computed when files are saved, so there is no point - # in migrating this value. + # filesize is dynamically computed when file entities are saved, so there is + # no point in migrating it. # filesize: filesize status: status created: timestamp @@ -20,7 +20,7 @@ uid: uid destination: plugin: entity:file - source_path_property: uri + source_path_property: filepath dependencies: module: - file diff -u b/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/File.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/File.php --- b/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/File.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/File.php @@ -7,6 +7,7 @@ namespace Drupal\migrate_drupal\Plugin\migrate\source\d7; +use Drupal\migrate\Row; use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; /** @@ -30,6 +31,27 @@ /** * {@inheritdoc} */ + public function prepareRow(Row $row) { + // Compute the filepath property, which is a physical representation of + // the URI relative to the Drupal root. + $search = array('public:/', 'private:/', 'temporary:/'); + $replace = array( + $this->variableGet('file_public_path', 'sites/default/files'), + $this->variableGet('file_private_path', NULL), + $this->variableGet('file_temporary_path', '/tmp'), + ); + $path = str_replace($search, $replace, $row->getSourceProperty('uri')); + // At this point, $path could be an absolute path or a relative path, + // depending on how the scheme's variable was set. So we need to shear out + // the source_base_path in order to make them all relative. + $path = str_replace($this->migration->get('destination.source_base_path'), NULL, $path); + + $row->setSourceProperty('filepath', $path); + } + + /** + * {@inheritdoc} + */ public function fields() { return array( 'fid' => $this->t('File ID'), @@ -41,6 +63,7 @@ 'timestamp' => $this->t('The time that the file was added.'), ); } + /** * {@inheritdoc} */ diff -u b/core/modules/migrate_drupal/src/Tests/d7/MigrateFileTest.php b/core/modules/migrate_drupal/src/Tests/d7/MigrateFileTest.php --- b/core/modules/migrate_drupal/src/Tests/d7/MigrateFileTest.php +++ b/core/modules/migrate_drupal/src/Tests/d7/MigrateFileTest.php @@ -16,6 +16,7 @@ * * @group migrate_drupal 7.x * @dumpFile d7/FileManaged.php + * @dumpFile d7/Variable.php * @migration d7_file */ class MigrateFileTest extends MigrateDrupal7TestBase { @@ -30,13 +31,32 @@ $this->loadDumps([ $this->getDumpDirectory() . '/FileManaged.php', + $this->getDumpDirectory() . '/Variable.php', ]); $this->installEntitySchema('file'); - touch('public://cube.jpeg'); - file_put_contents('public://cube.jpeg', str_repeat('*', 3620)); + /** @var \Drupal\Core\File\FileSystemInterface $fs */ + $fs = \Drupal::service('file_system'); + // /tmp will serve as the root directory for the fictional D7 site we're + // migrating. Silence any "file exists" warnings thrown by mkdir(). + // We don't care. + @$fs->mkdir('/tmp/sites/default/files'); + touch('/tmp/sites/default/files/cube.jpeg'); + file_put_contents('/tmp/sites/default/files/cube.jpeg', str_repeat('*', 3620)); + /** @var \Drupal\migrate\Entity\MigrationInterface $migration */ $migration = entity_load('migration', 'd7_file'); + // Set the destination plugin's source_base_path configuration value, which + // would normally be set by the user running the migration. + $migration->set('destination', [ + 'plugin' => 'entity:file', + // Note that source_base_path must include a trailing slash because it's + // prepended directly to the value of the source path property. + 'source_base_path' => '/tmp/', + // This is set in the migration's YAML file, but we need to repeat it + // here because all the destination configuration must be set at once. + 'source_path_property' => 'filepath', + ]); $executable = new MigrateExecutable($migration, $this); $executable->import(); } @@ -47,14 +67,20 @@ $this->assertTrue($file instanceof FileInterface); $this->assertIdentical($name, $file->getFilename()); $this->assertIdentical($uri, $file->getFileUri()); + $this->assertTrue(file_exists($uri)); $this->assertIdentical($mime, $file->getMimeType()); $this->assertIdentical($size, $file->getSize()); - // @TODO Check status, since that's important for files. + // isPermanent(), isTemporary(), etc. are determined by the status column. + $this->assertTrue($file->isPermanent()); $this->assertIdentical($created, $file->getCreatedTime()); $this->assertIdentical($changed, $file->getChangedTime()); $this->assertIdentical($uid, $file->getOwnerId()); } + public function tearDown() { + unlink('/tmp/sites/default/files/cube.jpeg'); + } + /** * Tests that all expected files are migrated. */