Pathologic was missing because of a .git folder inside.
[yaffs-website] / web / modules / contrib / migrate_tools / migrate_tools.drush.inc
1 <?php
2
3 /**
4  * @file
5  * Command-line tools to aid performing and developing migrations.
6  */
7
8 use Drupal\Component\Utility\Unicode;
9 use Drupal\migrate\Plugin\MigrationInterface;
10 use Drupal\migrate_tools\MigrateExecutable;
11 use Drupal\migrate_tools\DrushLogMigrateMessage;
12 use Drupal\Core\Datetime\DateFormatter;
13 use Drupal\migrate_plus\Entity\MigrationGroup;
14
15 /**
16  * Implements hook_drush_command().
17  */
18 function migrate_tools_drush_command() {
19   $items['migrate-status'] = [
20     'description' => 'List all migrations with current status.',
21     'options' => [
22       'group' => 'A comma-separated list of migration groups to list',
23       'tag' => 'Name of the migration tag to list',
24       'names-only' => 'Only return names, not all the details (faster)',
25     ],
26     'arguments' => [
27       'migration' => 'Restrict to a comma-separated list of migrations. Optional',
28     ],
29     'examples' => [
30       'migrate-status' => 'Retrieve status for all migrations',
31       'migrate-status --group=beer' => 'Retrieve status for all migrations in a given group',
32       'migrate-status --tag=user' => 'Retrieve status for all migrations with a given tag',
33       'migrate-status --group=beer --tag=user' => 'Retrieve status for all migrations in the beer group and with the user tag',
34       'migrate-status beer_term,beer_node' => 'Retrieve status for specific migrations',
35     ],
36     'drupal dependencies' => ['migrate_tools'],
37     'aliases' => ['ms'],
38   ];
39
40   $items['migrate-import'] = [
41     'description' => 'Perform one or more migration processes.',
42     'options' => [
43       'all' => 'Process all migrations.',
44       'group' => 'A comma-separated list of migration groups to import',
45       'tag' => 'Name of the migration tag to import',
46       'limit' => 'Limit on the number of items to process in each migration',
47       'feedback' => 'Frequency of progress messages, in items processed',
48       'idlist' => 'Comma-separated list of IDs to import',
49       'update' => ' In addition to processing unprocessed items from the source, update previously-imported items with the current data',
50       'force' => 'Force an operation to run, even if all dependencies are not satisfied',
51       'execute-dependencies' => 'Execute all dependent migrations first.',
52     ],
53     'arguments' => [
54       'migration' => 'ID of migration(s) to import. Delimit multiple using commas.',
55     ],
56     'examples' => [
57       'migrate-import --all' => 'Perform all migrations',
58       'migrate-import --group=beer' => 'Import all migrations in the beer group',
59       'migrate-import --tag=user' => 'Import all migrations with the user tag',
60       'migrate-import --group=beer --tag=user' => 'Import all migrations in the beer group and with the user tag',
61       'migrate-import beer_term,beer_node' => 'Import new terms and nodes',
62       'migrate-import beer_user --limit=2' => 'Import no more than 2 users',
63       'migrate-import beer_user --idlist=5' => 'Import the user record with source ID 5',
64     ],
65     'drupal dependencies' => ['migrate_tools'],
66     'aliases' => ['mi'],
67   ];
68
69   $items['migrate-rollback'] = array(
70     'description' => 'Rollback one or more migrations.',
71     'options' => array(
72       'all' => 'Process all migrations.',
73       'group' => 'A comma-separated list of migration groups to rollback',
74       'tag' => 'ID of the migration tag to rollback',
75       'feedback' => 'Frequency of progress messages, in items processed',
76     ),
77     'arguments' => array(
78       'migration' => 'Name of migration(s) to rollback. Delimit multiple using commas.',
79     ),
80     'examples' => array(
81       'migrate-rollback --all' => 'Perform all migrations',
82       'migrate-rollback --group=beer' => 'Rollback all migrations in the beer group',
83       'migrate-rollback --tag=user' => 'Rollback all migrations with the user tag',
84       'migrate-rollback --group=beer --tag=user' => 'Rollback all migrations in the beer group and with the user tag',
85       'migrate-rollback beer_term,beer_node' => 'Rollback imported terms and nodes',
86     ),
87     'drupal dependencies' => array('migrate_tools'),
88     'aliases' => array('mr'),
89   );
90
91   $items['migrate-stop'] = [
92     'description' => 'Stop an active migration operation.',
93     'arguments' => [
94       'migration' => 'ID of migration to stop',
95     ],
96     'drupal dependencies' => ['migrate_tools'],
97     'aliases' => ['mst'],
98   ];
99
100   $items['migrate-reset-status'] = [
101     'description' => 'Reset a active migration\'s status to idle.',
102     'arguments' => [
103       'migration' => 'ID of migration to reset',
104     ],
105     'drupal dependencies' => ['migrate_tools'],
106     'aliases' => ['mrs'],
107   ];
108
109   $items['migrate-messages'] = [
110     'description' => 'View any messages associated with a migration.',
111     'arguments' => [
112       'migration' => 'ID of the migration',
113     ],
114     'options' => [
115       'csv' => 'Export messages as a CSV'
116     ],
117     'examples' => [
118       'migrate-messages MyNode' => 'Show all messages for the MyNode migration',
119     ],
120     'drupal dependencies' => ['migrate_tools'],
121     'aliases' => ['mmsg'],
122   ];
123
124   $items['migrate-fields-source'] = [
125     'description' => 'List the fields available for mapping in a source.',
126     'arguments' => [
127       'migration' => 'ID of the migration',
128     ],
129     'examples' => [
130       'migrate-fields-source my_node' => 'List fields for the source in the my_node migration',
131     ],
132     'drupal dependencies' => ['migrate_tools'],
133     'aliases' => ['mfs'],
134   ];
135
136   return $items;
137 }
138
139 /**
140  * @param string $migration_names
141  */
142 function drush_migrate_tools_migrate_status($migration_names = '') {
143   $names_only = drush_get_option('names-only');
144
145   $migrations = drush_migrate_tools_migration_list($migration_names);
146
147   $table = [];
148   // Take it one group at a time, listing the migrations within each group.
149   foreach ($migrations as $group_id => $migration_list) {
150     $group = MigrationGroup::load($group_id);
151     $group_name = !empty($group) ? "{$group->label()} ({$group->id()})" : $group_id;
152     if ($names_only) {
153       $table[] = [
154         dt('Group: @name', array('@name' => $group_name))
155       ];
156     }
157     else {
158       $table[] = [
159         dt('Group: @name', array('@name' => $group_name)),
160         dt('Status'),
161         dt('Total'),
162         dt('Imported'),
163         dt('Unprocessed'),
164         dt('Last imported'),
165       ];
166     }
167     foreach ($migration_list as $migration_id => $migration) {
168       try {
169         $map = $migration->getIdMap();
170         $imported = $map->importedCount();
171         $source_plugin = $migration->getSourcePlugin();
172       }
173       catch (Exception $e) {
174         drush_log(dt('Failure retrieving information on @migration: @message',
175           ['@migration' => $migration_id, '@message' => $e->getMessage()]));
176         continue;
177       }
178       try {
179         $source_rows = $source_plugin->count();
180         // -1 indicates uncountable sources.
181         if ($source_rows == -1) {
182           $source_rows = dt('N/A');
183           $unprocessed = dt('N/A');
184         }
185         else {
186           $unprocessed = $source_rows - $map->processedCount();
187         }
188       }
189       catch (Exception $e) {
190         drush_print($e->getMessage());
191         drush_log(dt('Could not retrieve source count from @migration: @message',
192           ['@migration' => $migration_id, '@message' => $e->getMessage()]));
193         $source_rows = dt('N/A');
194         $unprocessed = dt('N/A');
195       }
196
197       if ($names_only) {
198         $table[] = [$migration_id];
199       }
200       else {
201         $status = $migration->getStatusLabel();
202         $migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
203         $last_imported = $migrate_last_imported_store->get($migration->id(), FALSE);
204         if ($last_imported) {
205           /** @var DateFormatter $date_formatter */
206           $date_formatter = \Drupal::service('date.formatter');
207           $last_imported = $date_formatter->format($last_imported / 1000,
208             'custom', 'Y-m-d H:i:s');
209         }
210         else {
211           $last_imported = '';
212         }
213         $table[] = [$migration_id, $status, $source_rows, $imported, $unprocessed, $last_imported];
214       }
215     }
216   }
217   drush_print_table($table);
218 }
219
220 /**
221  * @param string $migration_names
222  */
223 function drush_migrate_tools_migrate_import($migration_names = '') {
224   $group_names = drush_get_option('group');
225   $tag_names = drush_get_option('tag');
226   $all = drush_get_option('all');
227   $options = [];
228   if (!$all && !$group_names && !$migration_names && !$tag_names) {
229     drush_set_error('MIGRATE_ERROR', dt('You must specify --all, --group, --tag or one or more migration names separated by commas'));
230     return;
231   }
232
233   foreach (['limit', 'feedback', 'idlist', 'update', 'force'] as $option) {
234     if (drush_get_option($option)) {
235       $options[$option] = drush_get_option($option);
236     }
237   }
238
239   $migrations = drush_migrate_tools_migration_list($migration_names);
240   if (empty($migrations)) {
241     drush_log(dt('No migrations found.'), 'error');
242   }
243
244   // Take it one group at a time, importing the migrations within each group.
245   foreach ($migrations as $group_id => $migration_list) {
246     array_walk($migration_list, '_drush_migrate_tools_execute_migration', $options);
247   }
248 }
249
250 /**
251  * Executes a single migration. If the --execute-dependencies option was given,
252  * the migration's dependencies will also be executed first.
253  *
254  * @param \Drupal\migrate\Plugin\MigrationInterface $migration
255  *  The migration to execute.
256  * @param string $migration_id
257  *  The migration ID (not used, just an artifact of array_walk()).
258  * @param array $options
259  *  Additional options for the migration.
260  */
261 function _drush_migrate_tools_execute_migration(MigrationInterface $migration, $migration_id,  array $options = []) {
262   $log = new DrushLogMigrateMessage();
263
264   if (drush_get_option('execute-dependencies')) {
265     if ($required_IDS = $migration->get('requirements')) {
266       $manager = \Drupal::service('plugin.manager.config_entity_migration');
267       $required_migrations = $manager->createInstances($required_IDS);
268       $dependency_options = array_merge($options, ['is_dependency' => TRUE]);
269       array_walk($required_migrations, __FUNCTION__, $dependency_options);
270     }
271   }
272   if (!empty($options['force'])) {
273     $migration->set('requirements', []);
274   }
275   if (!empty($options['update'])) {
276     $migration->getIdMap()->prepareUpdate();
277   }
278   $executable = new MigrateExecutable($migration, $log, $options);
279   // drush_op() provides --simulate support
280   drush_op(array($executable, 'import'));
281 }
282
283 /**
284  * @param string $migration_names
285  */
286 function drush_migrate_tools_migrate_rollback($migration_names = '') {
287   $group_names = drush_get_option('group');
288   $tag_names = drush_get_option('tag');
289   $all = drush_get_option('all');
290   $options = [];
291   if (!$all && !$group_names && !$migration_names && !$tag_names) {
292     drush_set_error('MIGRATE_ERROR', dt('You must specify --all, --group, --tag, or one or more migration names separated by commas'));
293     return;
294   }
295
296   if (drush_get_option('feedback')) {
297     $options['feedback'] = drush_get_option('feedback');
298   }
299
300   $log = new DrushLogMigrateMessage();
301
302   $migrations = drush_migrate_tools_migration_list($migration_names);
303   if (empty($migrations)) {
304     drush_log(dt('No migrations found.'), 'error');
305   }
306
307   // Take it one group at a time, rolling back the migrations within each group.
308   foreach ($migrations as $group_id => $migration_list) {
309     // Roll back in reverse order.
310     $migration_list = array_reverse($migration_list);
311     foreach ($migration_list as $migration_id => $migration) {
312       $executable = new MigrateExecutable($migration, $log, $options);
313       // drush_op() provides --simulate support.
314       drush_op(array($executable, 'rollback'));
315     }
316   }
317 }
318
319 /**
320  * @param string $migration_id
321  */
322 function drush_migrate_tools_migrate_stop($migration_id = '') {
323   /** @var MigrationInterface $migration */
324   $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
325   if ($migration) {
326     $status = $migration->getStatus();
327     switch ($status) {
328       case MigrationInterface::STATUS_IDLE:
329         drush_log(dt('Migration @id is idle', ['@id' => $migration_id]), 'warning');
330         break;
331       case MigrationInterface::STATUS_DISABLED:
332         drush_log(dt('Migration @id is disabled', ['@id' => $migration_id]), 'warning');
333         break;
334       case MigrationInterface::STATUS_STOPPING:
335         drush_log(dt('Migration @id is already stopping', ['@id' => $migration_id]), 'warning');
336         break;
337       default:
338         $migration->interruptMigration(MigrationInterface::RESULT_STOPPED);
339         drush_log(dt('Migration @id requested to stop', ['@id' => $migration_id]), 'success');
340         break;
341     }
342   }
343   else {
344     drush_log(dt('Migration @id does not exist', ['@id' => $migration_id]), 'error');
345   }
346 }
347
348 /**
349  * @param string $migration_id
350  */
351 function drush_migrate_tools_migrate_reset_status($migration_id = '') {
352   /** @var MigrationInterface $migration */
353   $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
354   if ($migration) {
355     $status = $migration->getStatus();
356     if ($status == MigrationInterface::STATUS_IDLE) {
357       drush_log(dt('Migration @id is already Idle', ['@id' => $migration_id]), 'warning');
358     }
359     else {
360       $migration->setStatus(MigrationInterface::STATUS_IDLE);
361       drush_log(dt('Migration @id reset to Idle', ['@id' => $migration_id]), 'status');
362     }
363   }
364   else {
365     drush_log(dt('Migration @id does not exist', ['@id' => $migration_id]), 'error');
366   }
367 }
368
369 /**
370  * @param string $migration_id
371  */
372 function drush_migrate_tools_migrate_messages($migration_id) {
373   /** @var MigrationInterface $migration */
374   $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
375   if ($migration) {
376     $map = $migration->getIdMap();
377     $first = TRUE;
378     $table = [];
379     foreach ($map->getMessageIterator() as $row) {
380       unset($row->msgid);
381       if ($first) {
382         // @todo: Ideally, replace sourceid* with source key names. Or, should
383         // getMessageIterator() do that?
384         foreach ($row as $column => $value) {
385           $table[0][] = $column;
386         }
387         $first = FALSE;
388       }
389       $table[] = (array)$row;
390     }
391     if (empty($table)) {
392       drush_log(dt('No messages for this migration'), 'status');
393     }
394     else {
395       if (drush_get_option('csv')) {
396         foreach ($table as $row) {
397           fputcsv(STDOUT, $row);
398         }
399       }
400       else {
401         $widths = [];
402         foreach ($table[0] as $header) {
403           $widths[] = strlen($header) + 1;
404         }
405         drush_print_table($table, TRUE, $widths);
406       }
407     }
408   }
409   else {
410     drush_log(dt('Migration @id does not exist', ['@id' => $migration_id]), 'error');
411   }
412 }
413
414 /**
415  * @param string $migration_id
416  */
417 function drush_migrate_tools_migrate_fields_source($migration_id) {
418   /** @var MigrationInterface $migration */
419   $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
420   if ($migration) {
421     $source = $migration->getSourcePlugin();
422     $table = [];
423     foreach ($source->fields() as $machine_name => $description) {
424       $table[] = [strip_tags($description), $machine_name];
425     }
426     drush_print_table($table);
427   }
428   else {
429     drush_log(dt('Migration @id does not exist', ['@id' => $migration_id]), 'error');
430   }
431 }
432
433 /**
434  * Retrieve a list of active migrations.
435  *
436  * @param string $migration_ids
437  *  Comma-separated list of migrations - if present, return only these migrations.
438  *
439  * @return MigrationInterface[][]
440  *   An array keyed by migration group, each value containing an array of
441  *   migrations or an empty array if no migrations match the input criteria.
442  */
443 function drush_migrate_tools_migration_list($migration_ids = '') {
444   // Filter keys must match the migration configuration property name.
445   $filter['migration_group'] = drush_get_option('group') ? explode(',', drush_get_option('group')) : [];
446   $filter['migration_tags'] = drush_get_option('tag') ? explode(',', drush_get_option('tag')) : [];
447
448   $manager = \Drupal::service('plugin.manager.config_entity_migration');
449   $plugins = $manager->createInstances([]);
450   $matched_migrations = [];
451
452   // Get the set of migrations that may be filtered.
453   if (empty($migration_ids)) {
454     $matched_migrations  = $plugins;
455   }
456   else {
457     // Get the requested migrations.
458     $migration_ids = explode(',', Unicode::strtolower($migration_ids));
459     foreach ($plugins as $id => $migration) {
460       if (in_array(Unicode::strtolower($id), $migration_ids)) {
461         $matched_migrations [$id] = $migration;
462       }
463     }
464   }
465
466   // Filters the matched migrations if a group or a tag has been input.
467   if (!empty($filter['migration_group']) || !empty($filter['migration_tags'])) {
468     // Get migrations in any of the specified groups and with any of the
469     // specified tags.
470     foreach ($filter as $property => $values) {
471       if (!empty($values)) {
472         $filtered_migrations = [];
473         foreach ($values as $search_value) {
474           foreach ($matched_migrations as $id => $migration) {
475             // Cast to array because migration_tags can be an array.
476             $configured_values = (array) $migration->get($property);
477             $configured_id = (in_array($search_value, $configured_values)) ? $search_value : 'default';
478             if (empty($search_value) || $search_value == $configured_id) {
479               if (empty($migration_ids) || in_array(Unicode::strtolower($id), $migration_ids)) {
480                 $filtered_migrations[$id] = $migration;
481               }
482             }
483           }
484         }
485         $matched_migrations = $filtered_migrations;
486       }
487     }
488   }
489
490   // Sort the matched migrations by group.
491   if (!empty($matched_migrations)) {
492     foreach ($matched_migrations as $id => $migration) {
493       $configured_group_id = empty($migration->get('migration_group')) ? 'default' : $migration->get('migration_group');
494       $migrations[$configured_group_id][$id] = $migration;
495     }
496   }
497   return isset($migrations) ? $migrations : [];
498 }