diff --git a/migrate_tools.routing.yml b/migrate_tools.routing.yml
index b12735a..cecd682 100644
--- a/migrate_tools.routing.yml
+++ b/migrate_tools.routing.yml
@@ -74,6 +74,13 @@ entity.migration.process:
_title: 'Process'
requirements:
_permission: 'administer migrations'
+entity.migration.process.run:
+ path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/process/run'
+ defaults:
+ _controller: '\Drupal\migrate_tools\Controller\MigrationController::run'
+ _title: 'Run'
+ requirements:
+ _permission: 'administer migrations'
entity.migration.destination:
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/destination'
defaults:
@@ -107,3 +114,11 @@ migrate_tools.messages:
_title: 'Messages'
requirements:
_permission: 'administer migrations'
+
+migrate_tools.launch:
+ path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/launch'
+ defaults:
+ _form: '\Drupal\migrate_tools\Form\MigrationLaunchForm'
+ _title: 'Launch migration'
+ requirements:
+ _permission: 'administer migrations'
diff --git a/src/Controller/MigrationController.php b/src/Controller/MigrationController.php
index 09ae154..d7abece 100644
--- a/src/Controller/MigrationController.php
+++ b/src/Controller/MigrationController.php
@@ -9,6 +9,9 @@ use Drupal\Component\Utility\Html;
use Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Url;
+use Drupal\migrate_tools\MigrateExecutable;
+use Drupal\migrate\MigrateMessage;
/**
* Returns responses for migrate_tools migration view routes.
@@ -146,6 +149,38 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
}
/**
+ * Run a migration.
+ *
+ * @param string $migration_group
+ * Machine name of the migration's group.
+ * @param string $migration
+ * Machine name of the migration.
+ *
+ * @return array
+ * A render array as expected by drupal_render().
+ */
+ public function run($migration_group, $migration) {
+ $manager = \Drupal::service('plugin.manager.config_entity_migration');
+ $plugins = $manager->createInstances([]);
+ $migrateMessage = new MigrateMessage();
+
+ $batch = array(
+ 'title' => t('Processing migration...'),
+ 'operations' => array(
+ array('Drupal\migrate_tools\Controller\MigrationController::batchImport', [$plugins[$migration], $migrateMessage, []]),
+ )
+ );
+
+ batch_set($batch);
+ return batch_process("admin/structure/migrate/manage/$migration_group/migrations");
+ }
+
+ public static function batchImport($plugin, $message, $options) {
+ $executable = new MigrateExecutable($plugin, $message, $options);
+ $executable->import();
+ }
+
+ /**
* Display process information of a migration entity.
*
* @param string $migration_group
@@ -205,6 +240,12 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
'#empty' => $this->t('No process defined.'),
];
+ $build['process']['run'] = [
+ '#type' => 'link',
+ '#title' => $this->t('Run'),
+ '#url' => Url::fromRoute('entity.migration.process.run', ['migration_group' => $migration_group, 'migration' => $migration->id()]),
+ ];
+
return $build;
}
diff --git a/src/Controller/MigrationListBuilder.php b/src/Controller/MigrationListBuilder.php
index 16ee84f..8486cd3 100644
--- a/src/Controller/MigrationListBuilder.php
+++ b/src/Controller/MigrationListBuilder.php
@@ -111,6 +111,7 @@ class MigrationListBuilder extends ConfigEntityListBuilder implements EntityHand
$header['unprocessed'] = $this->t('Unprocessed');
$header['messages'] = $this->t('Messages');
$header['last_imported'] = $this->t('Last Imported');
+ $header['operations'] = $this->t('Operations');
return $header; // + parent::buildHeader();
}
@@ -176,6 +177,20 @@ class MigrationListBuilder extends ConfigEntityListBuilder implements EntityHand
else {
$row['last_imported'] = '';
}
+
+ $row['operations']['data'] = array(
+ '#type' => 'dropbutton',
+ '#links' => array(
+ 'simple_form' => array(
+ 'title' => $this->t('Launch'),
+ 'url' => Url::fromRoute('migrate_tools.launch', array(
+ 'migration_group' => $migration_group,
+ 'migration' => $migration->id()
+ )),
+ ),
+ ),
+ );
+
return $row; // + parent::buildRow($migration_entity);
}
diff --git a/src/Form/MigrationLaunchForm.php b/src/Form/MigrationLaunchForm.php
new file mode 100644
index 0000000..95ac16d
--- /dev/null
+++ b/src/Form/MigrationLaunchForm.php
@@ -0,0 +1,347 @@
+migrateMigrateOperations();
+
+ return $form;
+ }
+
+
+ /**
+ * Get Operations.
+ */
+ private function migrateMigrateOperations() {
+ // Build the 'Update options' form.
+ $operations = array(
+ '#type' => 'fieldset',
+ '#title' => t('Operations'),
+ );
+
+ $options = array(
+ '' => t('Please select'),
+ 'import_immediate' => t('Import immediately'),
+ 'rollback_immediate' => t('Rollback immediately'),
+ 'stop' => t('Stop'),
+ 'reset' => t('Reset'),
+ );
+ $operations['operation'] = array(
+ '#type' => 'select',
+ '#title' => t('Operation'),
+ '#title_display' => 'invisible',
+ '#options' => $options,
+ );
+ $operations['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Execute'),
+ );
+ $operations['description'] = array(
+ '#prefix' => '
',
+ '#markup' => t(
+ 'Choose an operation to run on all selections above:
+
+ - Import
Imports all previously unprocessed records from the source, plus
+ any records marked for update, into destination Drupal objects.
+ - Rollback
Deletes all Drupal objects created by the import.
+ - Stop
Cleanly interrupts any import or rollback processes that may
+ currently be running.
+ - Reset
Sometimes a process may fail to stop cleanly, and be
+ left stuck in an Importing or Rolling Back status. Choose Reset to clear
+ the status and permit other operations to proceed.
+ - Remove migration settings
Removes all information about a migration group
+ or task, while preserving any content that has already been imported.
+
'
+ ),
+ '#postfix' => '',
+ );
+
+ $operations['options'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Options'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+ $operations['options']['update'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Update'),
+ '#description' => t('Check this box to update all previously-imported content
+ in addition to importing new content. Leave unchecked to only import
+ new content'),
+ );
+ $operations['options']['force'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Ignore dependencies'),
+ '#description' => t('Check this box to ignore dependencies when running imports
+ - all tasks will run whether or not their dependent tasks have
+ completed.'),
+ );
+ $operations['options']['limit'] = array(
+ '#tree' => TRUE,
+ '#type' => 'fieldset',
+ '#attributes' => array('class' => array('container-inline')),
+ 'value' => array(
+ '#type' => 'textfield',
+ '#title' => t('Limit to:'),
+ '#size' => 10,
+ ),
+ 'unit' => array(
+ '#type' => 'select',
+ '#options' => array(
+ 'items' => t('items'),
+ 'seconds' => t('seconds'),
+ ),
+ '#description' => t('Set a limit of how many items to process for
+ each migration task, or how long each should run.'),
+ ),
+ );
+
+ return $operations;
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ if (empty($form_state->getValue('operation'))) {
+ $form_state->setErrorByName('operation', $this->t('Please select an operation.'));
+ return;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+
+ $operation = $form_state->getValue('operation');
+
+ $limit = $form_state->getValue('limit');
+
+ if ($form_state->getValue('update')) {
+ $update = $form_state->getValue('update');
+ }
+ else {
+ $update = 0;
+ }
+ if ($form_state->getValue('force')) {
+ $force = $form_state->getValue('force');
+ }
+ else {
+ $force = 0;
+ }
+
+ $migration_name = \Drupal::routeMatch()->getParameter('migration');
+ $machine_name = $migration_name;
+
+ $operations = array();
+
+ if ($migration_name) {
+
+ switch ($operation) {
+ case 'import_immediate':
+
+ $migrationPluginManager = \Drupal::service('plugin.manager.config_entity_migration');
+ $migrations = $migrationPluginManager->createInstances([$machine_name]);
+
+ $options = array(
+ 'limit' => $limit,
+ 'update' => $update,
+ 'force' => $force
+ );
+
+ $operations += $this->batchOperations($migrations, $options, 'import');
+
+ break;
+
+ case 'rollback_immediate':
+
+ $migrationPluginManager = \Drupal::service('plugin.manager.config_entity_migration');
+ $migrations = $migrationPluginManager->createInstances([$machine_name]);
+
+ $options = array(
+ 'limit' => $limit,
+ 'update' => $update,
+ 'force' => $force
+ );
+
+ $operations += $this->batchOperations($migrations, $options, 'rollback');
+
+ break;
+
+ case 'stop':
+
+ $migrationPluginManager = \Drupal::service('plugin.manager.config_entity_migration');
+ $migrations = $migrationPluginManager->createInstances([$machine_name]);
+
+ /** @var MigrationInterface $migration */
+ foreach ($migrations as $migration) {
+ $migration->interruptMigration(MigrationInterface::RESULT_STOPPED);
+ }
+
+ break;
+
+ case 'reset':
+
+ $migrationPluginManager = \Drupal::service('plugin.manager.config_entity_migration');
+ $migrations = $migrationPluginManager->createInstances([$machine_name]);
+
+ /** @var MigrationInterface $migration */
+ foreach ($migrations as $migration) {
+ $migration->setStatus(MigrationInterface::STATUS_IDLE);
+ }
+
+ break;
+
+ }
+ }
+
+ // Only immediate rollback and import operations will need to go through Batch API.
+ if (count($operations) > 0) {
+
+ $batch = array(
+ 'operations' => $operations,
+ 'title' => t('Import processing'),
+ 'init_message' => t('Starting import process'),
+ 'progress_message' => t('Processing %migrate', array('%migrate' => $machine_name)),
+ 'error_message' => t('An error occurred. Some or all of the import processing has failed.'),
+ 'finished' => array($this, 'batchFinished'),
+ );
+
+ batch_set($batch);
+ }
+
+ }
+
+ /**
+ * Helper to generate the batch operations for importing migrations.
+ *
+ * @param array $migrations
+ * @param array $options
+ * @param array $operation
+ *
+ * @return array
+ */
+ protected function batchOperations($migrations, $options, $operation) {
+
+ $migrationPluginManager = \Drupal::service('plugin.manager.config_entity_migration');
+
+ $operations = array();
+
+ /**
+ * @var string $id
+ * @var Migration $migration
+ */
+ foreach ($migrations as $id => $migration) {
+
+ if ($options['update']) {
+ $migration->getIdMap()->prepareUpdate();
+ }
+
+ if (!empty($options['force'])) {
+ $migration->set('requirements', []);
+ }
+ else {
+ if ($required_IDS = $migration->get('requirements')) {
+ $required_migrations = $migrationPluginManager->createInstances($required_IDS);
+ $dependency_options = array_merge($options, ['is_dependency' => TRUE]);
+ $operations += $this->batchOperations($required_migrations, $dependency_options, $operation);
+ }
+ }
+
+ $operations[] = array(array($this, 'batchProcess'), array($migration, $options, $operation));
+ }
+
+ return $operations;
+ }
+
+ /**
+ * Batch 'operation' callback
+ *
+ * @param MigrateExecutableInterface $executable
+ * @param MigrationInterface $migration
+ * @param string $operation
+ * @param array $context
+ */
+ public function batchProcess($migration, $options, $operation, &$context) {
+
+ if (empty($context['sandbox'])) {
+ $context['sandbox']['progress'] = 0;
+ $context['sandbox']['offset'] = 0;
+ $total = $source_plugin = $migration->getSourcePlugin()->count();
+ $context['sandbox']['max'] = $total;
+ }
+
+ $executable = new MigrateExecutable($migration, $this, $options);
+ call_user_func(array($executable, $operation));
+
+ $context['sandbox']['results'] = 'OK';
+ $context['sandbox']['progress'] = $context['sandbox']['max'];
+ $context['sandbox']['offset'] = $context['sandbox']['max'];
+ $context['message'] = 'Processed ' . $migration->id();
+
+ if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+ $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+ }
+ }
+
+ /**
+ * Batch 'finished' callback
+ */
+ public function batchFinished($success, $results, $operations) {
+ if ($success) {
+ // TODO maybe do something with results.
+ }
+ else {
+ // An error occurred.
+ // $operations contains the operations that remained unprocessed.
+ $error_operation = reset($operations);
+ $message = t('An error occurred while processing %error_operation with arguments: @arguments', array(
+ '%error_operation' => $error_operation[0],
+ '@arguments' => print_r($error_operation[1], TRUE)
+ ));
+ drupal_set_message($message, 'error');
+ }
+
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function display($message, $type = 'status') {
+ drupal_set_message($message, ($type=='error'?'error':'notice'));
+ }
+
+}