Version 1
[yaffs-website] / web / modules / contrib / migrate_upgrade / src / MigrateUpgradeDrushRunner.php
1 <?php
2
3 namespace Drupal\migrate_upgrade;
4
5 use Drupal\migrate\Plugin\MigrationInterface;
6 use Drupal\migrate\Event\MigrateEvents;
7 use Drupal\migrate\Event\MigrateIdMapMessageEvent;
8 use Drupal\migrate\MigrateExecutable;
9 use Drupal\Core\StringTranslation\StringTranslationTrait;
10 use Drupal\migrate_drupal\MigrationConfigurationTrait;
11 use Drupal\migrate_plus\Entity\Migration;
12 use Drupal\migrate_plus\Entity\MigrationGroup;
13 use Drupal\Core\Database\Database;
14
15 class MigrateUpgradeDrushRunner {
16
17   use MigrationConfigurationTrait;
18   use StringTranslationTrait;
19
20   /**
21    * The list of migrations to run and their configuration.
22    *
23    * @var \Drupal\migrate\Plugin\Migration[]
24    */
25   protected $migrationList;
26
27   /**
28    * MigrateMessage instance to display messages during the migration process.
29    *
30    * @var \Drupal\migrate_upgrade\DrushLogMigrateMessage
31    */
32   protected static $messages;
33
34   /**
35    * The Drupal version being imported.
36    *
37    * @var string
38    */
39   protected $version;
40
41   /**
42    * The state key used to store database configuration.
43    *
44    * @var string
45    */
46   protected $databaseStateKey;
47
48   /**
49    * List of D6 node migration IDs we've seen.
50    *
51    * @var array
52    */
53   protected $nodeMigrations = [];
54
55   /**
56    * From the provided source information, instantiate the appropriate migrations
57    * in the active configuration.
58    *
59    * @throws \Exception
60    */
61   public function configure() {
62     $legacy_db_key = drush_get_option('legacy-db-key');
63     if (!empty($legacy_db_key)) {
64       $connection = Database::getConnection('default', drush_get_option('legacy-db-key'));
65       $this->version = $this->getLegacyDrupalVersion($connection);
66       $database_state['key'] = drush_get_option('legacy-db-key');
67       $database_state_key = 'migrate_drupal_' . $this->version;
68       \Drupal::state()->set($database_state_key, $database_state);
69       \Drupal::state()->set('migrate.fallback_state_key', $database_state_key);
70     }
71     else {
72       $db_url = drush_get_option('legacy-db-url');
73       $db_spec = drush_convert_db_from_db_url($db_url);
74       $db_prefix = drush_get_option('legacy-db-prefix');
75       $db_spec['prefix'] = $db_prefix;
76       $connection = $this->getConnection($db_spec);
77       $this->version = $this->getLegacyDrupalVersion($connection);
78       $this->createDatabaseStateSettings($db_spec, $this->version);
79     }
80
81     $this->databaseStateKey = 'migrate_drupal_' . $this->version;
82     $migrations = $this->getMigrations($this->databaseStateKey, $this->version);
83     $this->migrationList = [];
84     foreach ($migrations as $migration) {
85       $this->applyFilePath($migration);
86       $this->expandNodeMigrations($migration);
87       $this->migrationList[$migration->id()] = $migration;
88     }
89   }
90
91   /**
92    * Adds the source base path configuration to relevant migrations.
93    *
94    * @param \Drupal\migrate\Plugin\MigrationInterface $migration
95    *   Migration to alter with the provided path.
96    */
97   protected function applyFilePath(MigrationInterface $migration) {
98     $destination = $migration->getDestinationConfiguration();
99     if ($destination['plugin'] === 'entity:file') {
100       // Make sure we have a single trailing slash.
101       $source_base_path = rtrim(drush_get_option('legacy-root'), '/') . '/';
102       $source = $migration->getSourceConfiguration();
103       $source['constants']['source_base_path'] = $source_base_path;
104       $migration->set('source', $source);
105     }
106   }
107
108   /**
109    * For D6 term_node migrations, make sure the nid reference is expanded.
110    *
111    * @param \Drupal\migrate\Plugin\MigrationInterface $migration
112    *   Migration to alter with the list of node migrations.
113    */
114   protected function expandNodeMigrations(MigrationInterface $migration) {
115     $source = $migration->getSourceConfiguration();
116     // Track the node migrations as we see them.
117     if ($source['plugin'] == 'd6_node') {
118       $this->nodeMigrations[] = $migration->id();
119     }
120     elseif ($source['plugin'] == 'd6_term_node' || $source['plugin'] == 'd6_term_node_revision') {
121       if ($source['plugin'] == 'd6_term_node') {
122         $id_property = 'nid';
123       }
124       else {
125         $id_property = 'vid';
126       }
127       // If the ID mapping is to the underived d6_node migration, replace
128       // it with an expanded list of node migrations.
129       $process = $migration->getProcess();
130       $new_nid_process = [];
131       foreach ($process[$id_property] as $delta => $plugin_configuration) {
132         if ($plugin_configuration['plugin'] == 'migration' &&
133             is_string($plugin_configuration['migration']) &&
134             substr($plugin_configuration['migration'], -7) == 'd6_node') {
135           $plugin_configuration['migration'] = $this->nodeMigrations;
136         }
137         $new_nid_process[$delta] = $plugin_configuration;
138       }
139       $migration->setProcessOfProperty($id_property, $new_nid_process);
140     }
141   }
142
143   /**
144    * Run the configured migrations.
145    */
146   public function import() {
147     static::$messages = new DrushLogMigrateMessage();
148     if (drush_get_option('debug')) {
149       \Drupal::service('event_dispatcher')->addListener(MigrateEvents::IDMAP_MESSAGE,
150         [get_class(), 'onIdMapMessage']);
151     }
152     foreach ($this->migrationList as $migration_id => $migration) {
153       drush_print(dt('Upgrading @migration', ['@migration' => $migration_id]));
154       $executable = new MigrateExecutable($migration, static::$messages);
155       // drush_op() provides --simulate support.
156       drush_op([$executable, 'import']);
157     }
158   }
159
160   /**
161    * Export the configured migration plugins as configuration entities.
162    */
163   public function export() {
164     $db_info = \Drupal::state()->get($this->databaseStateKey);
165
166     // Create a group to hold the database configuration.
167     $group = [
168       'id' => $this->databaseStateKey,
169       'label' => 'Import from Drupal ' . $this->version,
170       'description' => 'Migrations originally generated from drush migrate-upgrade --configure-only',
171       'source_type' => 'Drupal ' . $this->version,
172       'shared_configuration' => [
173         'source' => [
174           'key' => 'drupal_' . $this->version,
175         ]
176       ]
177     ];
178
179     // Only add the database connection info to the configuration entity
180     // if it was passed in as a parameter.
181     if (!empty(drush_get_option('legacy-db-url'))) {
182       $group['shared_configuration']['source']['database'] = $db_info['database'];
183     }
184
185     $group = MigrationGroup::create($group);
186     $group->save();
187     foreach ($this->migrationList as $migration_id => $migration) {
188       drush_print(dt('Exporting @migration as @new_migration',
189         ['@migration' => $migration_id, '@new_migration' => $this->modifyId($migration_id)]));
190       $entity_array['id'] = $migration_id;
191       $entity_array['migration_group'] = $this->databaseStateKey;
192       $entity_array['migration_tags'] = $migration->get('migration_tags');
193       $entity_array['label'] = $migration->get('label');
194       $entity_array['source'] = $migration->getSourceConfiguration();
195       $entity_array['destination'] = $migration->getDestinationConfiguration();
196       $entity_array['process'] = $migration->get('process');
197       $entity_array['migration_dependencies'] = $migration->getMigrationDependencies();
198       $migration_entity = Migration::create($this->substituteIds($entity_array));
199       $migration_entity->save();
200     }
201   }
202
203   /**
204    * Rewrite any migration plugin IDs so they won't conflict with the core
205    * IDs.
206    *
207    * @param $entity_array
208    *   A configuration array for a migration.
209    *
210    * @return array
211    *   The migration configuration array modified with new IDs.
212    */
213   protected function substituteIds($entity_array) {
214     $entity_array['id'] = $this->modifyId($entity_array['id']);
215     foreach ($entity_array['migration_dependencies'] as $type => $dependencies) {
216       foreach ($dependencies as $key => $dependency) {
217         $entity_array['migration_dependencies'][$type][$key] = $this->modifyId($dependency);
218       }
219     }
220     $this->substituteMigrationIds($entity_array['process']);
221     return $entity_array;
222   }
223
224   /**
225    * Recursively substitute IDs for migration plugins.
226    *
227    * @param mixed $process
228    */
229   protected function substituteMigrationIds(&$process) {
230     if (is_array($process)) {
231       // We found a migration plugin, change the ID.
232       if (isset($process['plugin']) && $process['plugin'] == 'migration') {
233         if (is_array($process['migration'])) {
234           $new_migration = [];
235           foreach ($process['migration'] as $migration) {
236             $new_migration[] = $this->modifyId($migration);
237           }
238           $process['migration'] = $new_migration;
239         }
240         else {
241           $process['migration'] = $this->modifyId($process['migration']);
242         }
243       }
244       else {
245         // Recurse on each array member.
246         foreach ($process as &$subprocess) {
247           $this->substituteMigrationIds($subprocess);
248         }
249       }
250     }
251   }
252
253   /**
254    * @param $id
255    *   The original core plugin ID.
256    *
257    * @return string
258    *   The ID modified to serve as a configuration entity ID.
259    */
260   protected function modifyId($id) {
261     return drush_get_option('migration-prefix', 'upgrade_') . str_replace(':', '_', $id);
262   }
263
264   /**
265    * Rolls back the configured migrations.
266    */
267   public function rollback() {
268     static::$messages = new DrushLogMigrateMessage();
269     $database_state_key = \Drupal::state()->get('migrate.fallback_state_key');
270     $database_state = \Drupal::state()->get($database_state_key);
271     $db_spec = $database_state['database'];
272     $connection = $this->getConnection($db_spec);
273     $version = $this->getLegacyDrupalVersion($connection);
274     $migrations = $this->getMigrations('migrate_drupal_' . $version, $version);
275
276     // Roll back in reverse order.
277     $this->migrationList = array_reverse($migrations);
278
279     foreach ($migrations as $migration) {
280       drush_print(dt('Rolling back @migration', ['@migration' => $migration->id()]));
281       $executable = new MigrateExecutable($migration, static::$messages);
282       // drush_op() provides --simulate support.
283       drush_op([$executable, 'rollback']);
284     }
285   }
286
287   /**
288    * Display any messages being logged to the ID map.
289    *
290    * @param \Drupal\migrate\Event\MigrateIdMapMessageEvent $event
291    *   The message event.
292    */
293   public static function onIdMapMessage(MigrateIdMapMessageEvent $event) {
294     if ($event->getLevel() == MigrationInterface::MESSAGE_NOTICE ||
295         $event->getLevel() == MigrationInterface::MESSAGE_INFORMATIONAL) {
296       $type = 'status';
297     }
298     else {
299       $type = 'error';
300     }
301     $source_id_string = implode(',', $event->getSourceIdValues());
302     $message = t('Source ID @source_id: @message',
303       ['@source_id' => $source_id_string, '@message' => $event->getMessage()]);
304     static::$messages->display($message, $type);
305   }
306
307 }