Updated to Drupal 8.5. Core Media not yet in use.
[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    * List of process plugin IDs used to lookup migrations.
57    *
58    * @var array
59    */
60   protected $migrationLookupPluginIds = [
61     'migration',
62     'migration_lookup',
63   ];
64
65   /**
66    * From the provided source information, instantiate the appropriate migrations
67    * in the active configuration.
68    *
69    * @throws \Exception
70    */
71   public function configure() {
72     $legacy_db_key = drush_get_option('legacy-db-key');
73     if (!empty($legacy_db_key)) {
74       $connection = Database::getConnection('default', drush_get_option('legacy-db-key'));
75       $this->version = $this->getLegacyDrupalVersion($connection);
76       $database_state['key'] = drush_get_option('legacy-db-key');
77       $database_state_key = 'migrate_drupal_' . $this->version;
78       \Drupal::state()->set($database_state_key, $database_state);
79       \Drupal::state()->set('migrate.fallback_state_key', $database_state_key);
80     }
81     else {
82       $db_url = drush_get_option('legacy-db-url');
83       $db_spec = drush_convert_db_from_db_url($db_url);
84       $db_prefix = drush_get_option('legacy-db-prefix');
85       $db_spec['prefix'] = $db_prefix;
86       $connection = $this->getConnection($db_spec);
87       $this->version = $this->getLegacyDrupalVersion($connection);
88       $this->createDatabaseStateSettings($db_spec, $this->version);
89     }
90
91     $this->databaseStateKey = 'migrate_drupal_' . $this->version;
92     $migrations = $this->getMigrations($this->databaseStateKey, $this->version);
93     $this->migrationList = [];
94     foreach ($migrations as $migration) {
95       $this->applyFilePath($migration);
96       $this->expandNodeMigrations($migration);
97       $this->migrationList[$migration->id()] = $migration;
98     }
99   }
100
101   /**
102    * Adds the source base path configuration to relevant migrations.
103    *
104    * @param \Drupal\migrate\Plugin\MigrationInterface $migration
105    *   Migration to alter with the provided path.
106    */
107   protected function applyFilePath(MigrationInterface $migration) {
108     $destination = $migration->getDestinationConfiguration();
109     if ($destination['plugin'] === 'entity:file') {
110       // Make sure we have a single trailing slash.
111       $source_base_path = rtrim(drush_get_option('legacy-root'), '/') . '/';
112       $source = $migration->getSourceConfiguration();
113       $source['constants']['source_base_path'] = $source_base_path;
114       $migration->set('source', $source);
115     }
116   }
117
118   /**
119    * For D6 term_node migrations, make sure the nid reference is expanded.
120    *
121    * @param \Drupal\migrate\Plugin\MigrationInterface $migration
122    *   Migration to alter with the list of node migrations.
123    */
124   protected function expandNodeMigrations(MigrationInterface $migration) {
125     $source = $migration->getSourceConfiguration();
126     // Track the node migrations as we see them.
127     if ($source['plugin'] == 'd6_node') {
128       $this->nodeMigrations[] = $migration->id();
129     }
130     elseif ($source['plugin'] == 'd6_term_node' || $source['plugin'] == 'd6_term_node_revision') {
131       if ($source['plugin'] == 'd6_term_node') {
132         $id_property = 'nid';
133       }
134       else {
135         $id_property = 'vid';
136       }
137       // If the ID mapping is to the underived d6_node migration, replace
138       // it with an expanded list of node migrations.
139       $process = $migration->getProcess();
140       $new_nid_process = [];
141       foreach ($process[$id_property] as $delta => $plugin_configuration) {
142         if (in_array($plugin_configuration['plugin'], $this->migrationLookupPluginIds) &&
143             is_string($plugin_configuration['migration']) &&
144             substr($plugin_configuration['migration'], -7) == 'd6_node') {
145           $plugin_configuration['migration'] = $this->nodeMigrations;
146         }
147         $new_nid_process[$delta] = $plugin_configuration;
148       }
149       $migration->setProcessOfProperty($id_property, $new_nid_process);
150     }
151   }
152
153   /**
154    * Run the configured migrations.
155    */
156   public function import() {
157     static::$messages = new DrushLogMigrateMessage();
158     if (drush_get_option('debug')) {
159       \Drupal::service('event_dispatcher')->addListener(MigrateEvents::IDMAP_MESSAGE,
160         [get_class(), 'onIdMapMessage']);
161     }
162     foreach ($this->migrationList as $migration_id => $migration) {
163       drush_print(dt('Upgrading @migration', ['@migration' => $migration_id]));
164       $executable = new MigrateExecutable($migration, static::$messages);
165       // drush_op() provides --simulate support.
166       drush_op([$executable, 'import']);
167     }
168   }
169
170   /**
171    * Export the configured migration plugins as configuration entities.
172    */
173   public function export() {
174     $db_info = \Drupal::state()->get($this->databaseStateKey);
175
176     // Create a group to hold the database configuration.
177     $group = [
178       'id' => $this->databaseStateKey,
179       'label' => 'Import from Drupal ' . $this->version,
180       'description' => 'Migrations originally generated from drush migrate-upgrade --configure-only',
181       'source_type' => 'Drupal ' . $this->version,
182       'shared_configuration' => [
183         'source' => [
184           'key' => 'drupal_' . $this->version,
185         ]
186       ]
187     ];
188
189     // Only add the database connection info to the configuration entity
190     // if it was passed in as a parameter.
191     if (!empty(drush_get_option('legacy-db-url'))) {
192       $group['shared_configuration']['source']['database'] = $db_info['database'];
193     }
194
195     // Ditto for the key.
196     if (!empty(drush_get_option('legacy-db-key'))) {
197       $group['shared_configuration']['source']['key'] = drush_get_option('legacy-db-key');
198     }
199
200     $group = MigrationGroup::create($group);
201     $group->save();
202     foreach ($this->migrationList as $migration_id => $migration) {
203       drush_print(dt('Exporting @migration as @new_migration',
204         ['@migration' => $migration_id, '@new_migration' => $this->modifyId($migration_id)]));
205       $entity_array['id'] = $migration_id;
206       $entity_array['class'] = $migration->get('class');
207       $entity_array['cck_plugin_method'] = $migration->get('cck_plugin_method');
208       $entity_array['field_plugin_method'] = $migration->get('field_plugin_method');
209       $entity_array['migration_group'] = $this->databaseStateKey;
210       $entity_array['migration_tags'] = $migration->get('migration_tags');
211       $entity_array['label'] = $migration->get('label');
212       $entity_array['source'] = $migration->getSourceConfiguration();
213       $entity_array['destination'] = $migration->getDestinationConfiguration();
214       $entity_array['process'] = $migration->get('process');
215       $entity_array['migration_dependencies'] = $migration->getMigrationDependencies();
216       $migration_entity = Migration::create($this->substituteIds($entity_array));
217       $migration_entity->save();
218     }
219   }
220
221   /**
222    * Rewrite any migration plugin IDs so they won't conflict with the core
223    * IDs.
224    *
225    * @param $entity_array
226    *   A configuration array for a migration.
227    *
228    * @return array
229    *   The migration configuration array modified with new IDs.
230    */
231   protected function substituteIds($entity_array) {
232     $entity_array['id'] = $this->modifyId($entity_array['id']);
233     foreach ($entity_array['migration_dependencies'] as $type => $dependencies) {
234       foreach ($dependencies as $key => $dependency) {
235         $entity_array['migration_dependencies'][$type][$key] = $this->modifyId($dependency);
236       }
237     }
238     $this->substituteMigrationIds($entity_array['process']);
239     return $entity_array;
240   }
241
242   /**
243    * Recursively substitute IDs for migration plugins.
244    *
245    * @param mixed $process
246    */
247   protected function substituteMigrationIds(&$process) {
248     if (is_array($process)) {
249       // We found a migration plugin, change the ID.
250       if (isset($process['plugin']) && in_array($process['plugin'], $this->migrationLookupPluginIds)) {
251         if (is_array($process['migration'])) {
252           $new_migration = [];
253           foreach ($process['migration'] as $migration) {
254             $new_migration[] = $this->modifyId($migration);
255           }
256           $process['migration'] = $new_migration;
257         }
258         else {
259           $process['migration'] = $this->modifyId($process['migration']);
260         }
261         // The source_ids configuration for migrate_lookup is keyed by
262         // migration id.  If it is there, we need to rekey to the new ids.
263         if (isset($process['source_ids']) && is_array($process['source_ids'])) {
264           $new_source_ids = [];
265           foreach ($process['source_ids'] as $migration_id => $source_ids) {
266             $new_migration_id = $this->modifyId($migration_id);
267             $new_source_ids[$new_migration_id] = $source_ids;
268           }
269           $process['source_ids'] = $new_source_ids;
270         }
271       }
272       else {
273         // Recurse on each array member.
274         foreach ($process as &$subprocess) {
275           $this->substituteMigrationIds($subprocess);
276         }
277       }
278     }
279   }
280
281   /**
282    * @param $id
283    *   The original core plugin ID.
284    *
285    * @return string
286    *   The ID modified to serve as a configuration entity ID.
287    */
288   protected function modifyId($id) {
289     return drush_get_option('migration-prefix', 'upgrade_') . str_replace(':', '_', $id);
290   }
291
292   /**
293    * Rolls back the configured migrations.
294    */
295   public function rollback() {
296     static::$messages = new DrushLogMigrateMessage();
297     $database_state_key = \Drupal::state()->get('migrate.fallback_state_key');
298     $database_state = \Drupal::state()->get($database_state_key);
299     $db_spec = $database_state['database'];
300     $connection = $this->getConnection($db_spec);
301     $version = $this->getLegacyDrupalVersion($connection);
302     $migrations = $this->getMigrations('migrate_drupal_' . $version, $version);
303
304     // Roll back in reverse order.
305     $this->migrationList = array_reverse($migrations);
306
307     foreach ($migrations as $migration) {
308       drush_print(dt('Rolling back @migration', ['@migration' => $migration->id()]));
309       $executable = new MigrateExecutable($migration, static::$messages);
310       // drush_op() provides --simulate support.
311       drush_op([$executable, 'rollback']);
312     }
313   }
314
315   /**
316    * Display any messages being logged to the ID map.
317    *
318    * @param \Drupal\migrate\Event\MigrateIdMapMessageEvent $event
319    *   The message event.
320    */
321   public static function onIdMapMessage(MigrateIdMapMessageEvent $event) {
322     if ($event->getLevel() == MigrationInterface::MESSAGE_NOTICE ||
323         $event->getLevel() == MigrationInterface::MESSAGE_INFORMATIONAL) {
324       $type = 'status';
325     }
326     else {
327       $type = 'error';
328     }
329     $source_id_string = implode(',', $event->getSourceIdValues());
330     $message = t('Source ID @source_id: @message',
331       ['@source_id' => $source_id_string, '@message' => $event->getMessage()]);
332     static::$messages->display($message, $type);
333   }
334
335 }