contrib modules updates

This commit is contained in:
2019-02-27 10:39:59 +01:00
parent 04a4b8895d
commit e3cf889820
579 changed files with 18343 additions and 4076 deletions
@@ -8,13 +8,14 @@
"irc": "irc://irc.freenode.org/drupal-migrate",
"source": "http://cgit.drupalcode.org/migrate_tools"
},
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"drupal/migrate_plus": "^4"
},
"require-dev": {
"drupal/coder": "^8",
"drupal/migrate_source_csv": "^2.2"
"drupal/migrate_plus": "4.x-dev",
"drupal/migrate_source_csv": "^2.2",
"drush/drush": "^9"
},
"minimum-stability": "dev",
"extra": {
@@ -0,0 +1,25 @@
# Learn to make one for your own drupal.org project:
# https://www.drupal.org/drupalorg/docs/drupal-ci/customizing-drupalci-testing
build:
assessment:
validate_codebase:
phplint:
container_composer:
phpcs:
# phpcs will use core's specified version of Coder.
sniff-all-files: true
halt-on-fail: true
testing:
# run_tests task is executed several times in order of performance speeds.
# halt-on-fail can be set on the run_tests tasks in order to fail fast.
# suppress-deprecations is false in order to be alerted to usages of
# deprecated code.
run_tests.standard:
types: 'Simpletest,PHPUnit-Unit,PHPUnit-Kernel,PHPUnit-Functional'
testgroups: '--all'
suppress-deprecations: false
run_tests.js:
types: 'PHPUnit-FunctionalJavascript'
testgroups: '--all'
suppress-deprecations: false
nightwatchjs: { }
@@ -5,13 +5,13 @@
* Command-line tools to aid performing and developing migrations.
*/
use Drupal\Component\Utility\Unicode;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\RequirementsInterface;
use Drupal\migrate_plus\Entity\MigrationGroup;
use Drupal\migrate_tools\DrushLogMigrateMessage;
use Drupal\migrate_tools\MigrateExecutable;
use Drupal\migrate_tools\MigrateTools;
/**
* Implements hook_drush_command().
@@ -47,6 +47,7 @@ function migrate_tools_drush_command() {
'limit' => 'Limit on the number of items to process in each migration',
'feedback' => 'Frequency of progress messages, in items processed',
'idlist' => 'Comma-separated list of IDs to import',
'idlist-delimiter' => 'The delimiter for records, defaults to \':\'',
'update' => ' In addition to processing unprocessed items from the source, update previously-imported items with the current data',
'force' => 'Force an operation to run, even if all dependencies are not satisfied',
'execute-dependencies' => 'Execute all dependent migrations first.',
@@ -62,6 +63,7 @@ function migrate_tools_drush_command() {
'migrate-import beer_term,beer_node' => 'Import new terms and nodes',
'migrate-import beer_user --limit=2' => 'Import no more than 2 users',
'migrate-import beer_user --idlist=5' => 'Import the user record with source ID 5',
'migrate-import beer_node_revision --idlist=1:2,2:3,3:5' => "Import the node revision record with source IDs [1,2], [2,3], and [3,5]",
],
'drupal dependencies' => ['migrate_tools'],
'aliases' => ['mi', 'mim'],
@@ -74,9 +76,11 @@ function migrate_tools_drush_command() {
'group' => 'A comma-separated list of migration groups to rollback',
'tag' => 'ID of the migration tag to rollback',
'feedback' => 'Frequency of progress messages, in items processed',
'idlist' => 'Comma-separated list of IDs to import',
],
'arguments' => [
'migration' => 'Name of migration(s) to rollback. Delimit multiple using commas.',
'idlist' => 'Comma-separated list of IDs to import',
],
'examples' => [
'migrate-rollback --all' => 'Perform all migrations',
@@ -84,6 +88,7 @@ function migrate_tools_drush_command() {
'migrate-rollback --tag=user' => 'Rollback all migrations with the user tag',
'migrate-rollback --group=beer --tag=user' => 'Rollback all migrations in the beer group and with the user tag',
'migrate-rollback beer_term,beer_node' => 'Rollback imported terms and nodes',
'migrate-rollback beer_user --idlist=5' => 'Rollback imported user record with source ID 5',
],
'drupal dependencies' => ['migrate_tools'],
'aliases' => ['mr'],
@@ -296,7 +301,16 @@ function _drush_migrate_tools_execute_migration(MigrationInterface $migration, $
$migration->set('requirements', []);
}
if (!empty($options['update'])) {
$migration->getIdMap()->prepareUpdate();
if (empty($options['idlist'])) {
$migration->getIdMap()->prepareUpdate();
}
else {
$source_id_values_list = MigrateTools::buildIdList($options);
$keys = array_keys($migration->getSourcePlugin()->getIds());
foreach ($source_id_values_list as $source_id_values) {
$migration->getIdMap()->setUpdate(array_combine($keys, $source_id_values));
}
}
}
$executable = new MigrateExecutable($migration, $log, $options);
// Function drush_op() provides --simulate support.
@@ -326,8 +340,10 @@ function drush_migrate_tools_migrate_rollback($migration_names = '') {
return;
}
if (drush_get_option('feedback')) {
$options['feedback'] = drush_get_option('feedback');
foreach (['feedback', 'idlist'] as $option) {
if (drush_get_option($option)) {
$options[$option] = drush_get_option($option);
}
}
$log = new DrushLogMigrateMessage();
@@ -365,12 +381,15 @@ function drush_migrate_tools_migrate_stop($migration_id = '') {
case MigrationInterface::STATUS_IDLE:
drush_log(dt('Migration @id is idle', ['@id' => $migration_id]), 'warning');
break;
case MigrationInterface::STATUS_DISABLED:
drush_log(dt('Migration @id is disabled', ['@id' => $migration_id]), 'warning');
break;
case MigrationInterface::STATUS_STOPPING:
drush_log(dt('Migration @id is already stopping', ['@id' => $migration_id]), 'warning');
break;
default:
$migration->interruptMigration(MigrationInterface::RESULT_STOPPED);
drush_log(dt('Migration @id requested to stop', ['@id' => $migration_id]), 'success');
@@ -505,9 +524,9 @@ function drush_migrate_tools_migration_list($migration_ids = '') {
}
else {
// Get the requested migrations.
$migration_ids = explode(',', Unicode::strtolower($migration_ids));
$migration_ids = explode(',', mb_strtolower($migration_ids));
foreach ($plugins as $id => $migration) {
if (in_array(Unicode::strtolower($id), $migration_ids)) {
if (in_array(mb_strtolower($id), $migration_ids)) {
$matched_migrations[$id] = $migration;
}
}
@@ -539,7 +558,7 @@ function drush_migrate_tools_migration_list($migration_ids = '') {
$configured_values = (array) $migration->get($property);
$configured_id = (in_array($search_value, $configured_values)) ? $search_value : 'default';
if (empty($search_value) || $search_value == $configured_id) {
if (empty($migration_ids) || in_array(Unicode::strtolower($id), $migration_ids)) {
if (empty($migration_ids) || in_array(mb_strtolower($id), $migration_ids)) {
$filtered_migrations[$id] = $migration;
}
}
@@ -553,7 +572,7 @@ function drush_migrate_tools_migration_list($migration_ids = '') {
// Sort the matched migrations by group.
if (!empty($matched_migrations)) {
foreach ($matched_migrations as $id => $migration) {
$configured_group_id = empty($migration->get('migration_group')) ? 'default' : $migration->get('migration_group');
$configured_group_id = empty($migration->migration_group) ? 'default' : $migration->migration_group;
$migrations[$configured_group_id][$id] = $migration;
}
}
@@ -3,14 +3,15 @@ name: Migrate Tools
description: 'Tools to assist in developing and running migrations.'
package: Migration
# core: 8.x
configure: entity.migration_group.list
dependencies:
- drupal:migrate (>=8.3)
- migrate_plus:migrate_plus
test_dependencies:
- migrate_source_csv:migrate_source_csv (>=8.x-2.2)
# Information added by Drupal.org packaging script on 2018-08-27
version: '8.x-4.0'
# Information added by Drupal.org packaging script on 2019-01-07
version: '8.x-4.1'
core: '8.x'
project: 'migrate_tools'
datestamp: 1535380087
datestamp: 1546879109
@@ -157,7 +157,7 @@ migrate_tools.messages:
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/messages'
defaults:
_controller: '\Drupal\migrate_tools\Controller\MessageController::overview'
_title: 'Messages'
_title_callback: '\Drupal\migrate_tools\Controller\MessageController::title'
_migrate_group: true
requirements:
_permission: 'administer migrations'
@@ -1,207 +0,0 @@
<?xml version="1.0"?>
<ruleset name="Drupal coding standards">
<description>Drupal 8 coding standards</description>
<file>.</file>
<arg name="extensions" value="inc,install,module,php,profile,test,theme"/>
<!--Exclude third party code.-->
<exclude-pattern>./vendor/*</exclude-pattern>
<!--Run Drupal standards.-->
<rule ref="Drupal.Array"/>
<rule ref="Drupal.Classes"/>
<rule ref="Drupal.Commenting">
<!-- TagsNotGrouped and ParamGroup have false-positives.
@see https://www.drupal.org/node/2060925 -->
<exclude name="Drupal.Commenting.DocComment.TagsNotGrouped"/>
<exclude name="Drupal.Commenting.DocComment.ParamGroup"/>
</rule>
<rule ref="Drupal.ControlStructures"/>
<rule ref="Drupal.CSS"/>
<rule ref="Drupal.Files"/>
<rule ref="Drupal.Formatting"/>
<rule ref="Drupal.Functions"/>
<rule ref="Drupal.InfoFiles"/>
<rule ref="Drupal.Methods"/>
<rule ref="Drupal.NamingConventions"/>
<rule ref="Drupal.Scope"/>
<rule ref="Drupal.Semantics"/>
<rule ref="Drupal.Strings"/>
<rule ref="Drupal.WhiteSpace"/>
<!-- Drupal Practice sniffs -->
<rule ref="DrupalPractice.Commenting"/>
<!-- Generic sniffs -->
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
<rule ref="Generic.Files.ByteOrderMark"/>
<rule ref="Generic.Files.LineEndings"/>
<rule ref="Generic.Formatting.SpaceAfterCast"/>
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
<rule ref="Generic.Functions.OpeningFunctionBraceKernighanRitchie">
<properties>
<property name="checkClosures" value="true"/>
</properties>
</rule>
<rule ref="Generic.NamingConventions.ConstructorName"/>
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
<rule ref="Generic.PHP.DeprecatedFunctions"/>
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
<rule ref="Generic.PHP.LowerCaseKeyword"/>
<rule ref="Generic.PHP.UpperCaseConstant"/>
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
<!-- MySource sniffs -->
<rule ref="MySource.Debug.DebugCode"/>
<!-- PEAR sniffs -->
<rule ref="PEAR.Files.IncludingFile"/>
<!-- Disable some error messages that we do not want. -->
<rule ref="PEAR.Files.IncludingFile.UseIncludeOnce">
<severity>0</severity>
</rule>
<rule ref="PEAR.Files.IncludingFile.UseInclude">
<severity>0</severity>
</rule>
<rule ref="PEAR.Files.IncludingFile.UseRequireOnce">
<severity>0</severity>
</rule>
<rule ref="PEAR.Files.IncludingFile.UseRequire">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.ValidDefaultValue"/>
<!-- PEAR sniffs -->
<rule ref="PEAR.Functions.FunctionCallSignature"/>
<!-- The sniffs inside PEAR.Functions.FunctionCallSignature silenced below are
also silenced in Drupal CS' ruleset.xml. The code below is a 1-on-1 copy
from that file. -->
<!-- Disable some error messages that we already cover. -->
<rule ref="PEAR.Functions.FunctionCallSignature.SpaceAfterOpenBracket">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket">
<severity>0</severity>
</rule>
<!-- Disable some error messages that we do not want. -->
<rule ref="PEAR.Functions.FunctionCallSignature.Indent">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature.CloseBracketLine">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature.EmptyLine">
<severity>0</severity>
</rule>
<!-- PSR-2 sniffs -->
<rule ref="PSR2.Classes.PropertyDeclaration">
<exclude name="PSR2.Classes.PropertyDeclaration.Underscore"/>
</rule>
<rule ref="PSR2.Namespaces.NamespaceDeclaration"/>
<rule ref="PSR2.Namespaces.UseDeclaration">
<exclude name="PSR2.Namespaces.UseDeclaration.UseAfterNamespace"/>
</rule>
<!-- Squiz sniffs -->
<rule ref="Squiz.Arrays.ArrayBracketSpacing"/>
<rule ref="Squiz.Arrays.ArrayDeclaration">
<exclude name="Squiz.Arrays.ArrayDeclaration.NoKeySpecified"/>
<exclude name="Squiz.Arrays.ArrayDeclaration.KeySpecified"/>
</rule>
<!-- Disable some error messages that we do not want. -->
<rule ref="Squiz.Arrays.ArrayDeclaration.CloseBraceNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.DoubleArrowNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.FirstValueNoNewline">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.KeyNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.MultiLineNotAllowed">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.NoComma">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.NoCommaAfterLast">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.NotLowerCase">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.SingleLineNotAllowed">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.ValueNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.ValueNoNewline">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/>
<!-- Disable some error messages that we already cover. -->
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.AsNotLower">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.SpaceAfterOpen">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.SpaceBeforeClose">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForLoopDeclaration"/>
<!-- Disable some error messages that we already cover. -->
<rule ref="Squiz.ControlStructures.ForLoopDeclaration.SpacingAfterOpen">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForLoopDeclaration.SpacingBeforeClose">
<severity>0</severity>
</rule>
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration"/>
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine">
<severity>0</severity>
</rule>
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.ContentAfterBrace">
<severity>0</severity>
</rule>
<!-- Standard yet to be finalized on this (https://www.drupal.org/node/1539712). -->
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.FirstParamSpacing">
<severity>0</severity>
</rule>
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.Indent">
<severity>0</severity>
</rule>
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.CloseBracketLine">
<severity>0</severity>
</rule>
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
<properties>
<property name="equalsSpacing" value="1"/>
</properties>
</rule>
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.NoSpaceBeforeArg">
<severity>0</severity>
</rule>
<rule ref="Squiz.PHP.LowercasePHPFunctions"/>
<rule ref="Squiz.Strings.ConcatenationSpacing">
<properties>
<property name="spacing" value="1"/>
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing" />
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
<!-- Zend sniffs -->
<rule ref="Zend.Files.ClosingTag"/>
</ruleset>
@@ -3,7 +3,6 @@
namespace Drupal\migrate_tools\Commands;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
@@ -12,7 +11,9 @@ use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\MigrationPluginManager;
use Drupal\migrate\Plugin\RequirementsInterface;
use Drupal\migrate_tools\Drush9LogMigrateMessage;
use Drupal\migrate_tools\IdMapFilter;
use Drupal\migrate_tools\MigrateExecutable;
use Drupal\migrate_tools\MigrateTools;
use Drush\Commands\DrushCommands;
/**
@@ -89,6 +90,8 @@ class MigrateToolsCommands extends DrushCommands {
* @option tag Name of the migration tag to list
* @option names-only Only return names, not all the details (faster)
*
* @default $options []
*
* @usage migrate:status
* Retrieve status for all migrations
* @usage migrate:status --group=beer
@@ -118,7 +121,12 @@ class MigrateToolsCommands extends DrushCommands {
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
* Migrations status formatted as table.
*/
public function status($migration_names = '', array $options = ['group' => NULL, 'tag' => NULL, 'names-only' => NULL]) {
public function status($migration_names = '', array $options = []) {
$options += [
'group' => NULL,
'tag' => NULL,
'names-only' => NULL,
];
$names_only = $options['names-only'];
$migrations = $this->migrationsList($migration_names, $options);
@@ -231,12 +239,15 @@ class MigrateToolsCommands extends DrushCommands {
* @option limit Limit on the number of items to process in each migration
* @option feedback Frequency of progress messages, in items processed
* @option idlist Comma-separated list of IDs to import
* @option idlist-delimiter The delimiter for records, defaults to ':'
* @option update In addition to processing unprocessed items from the
* source, update previously-imported items with the current data
* @option force Force an operation to run, even if all dependencies are not
* satisfied
* @option execute-dependencies Execute all dependent migrations first.
*
* @default $options []
*
* @usage migrate:import --all
* Perform all migrations
* @usage migrate:import --group=beer
@@ -251,6 +262,8 @@ class MigrateToolsCommands extends DrushCommands {
* Import no more than 2 users
* @usage migrate:import beer_user --idlist=5
* Import the user record with source ID 5
* @usage migrate:import beer_node_revision --idlist=1:2,2:3,3:5
* Import the node revision record with source IDs [1,2], [2,3], and [3,5]
*
* @validate-module-enabled migrate_tools
*
@@ -259,7 +272,19 @@ class MigrateToolsCommands extends DrushCommands {
* @throws \Exception
* If there are not enough parameters to the command.
*/
public function import($migration_names = '', array $options = ['all' => NULL, 'group' => NULL, 'tag' => NULL, 'limit' => NULL, 'feedback' => NULL, 'idlist' => NULL, 'update' => NULL, 'force' => NULL, 'execute-dependencies' => NULL]) {
public function import($migration_names = '', array $options = []) {
$options += [
'all' => NULL,
'group' => NULL,
'tag' => NULL,
'limit' => NULL,
'feedback' => NULL,
'idlist' => NULL,
'idlist-delimiter' => ':',
'update' => NULL,
'force' => NULL,
'execute-dependencies' => NULL,
];
$group_names = $options['group'];
$tag_names = $options['tag'];
$all = $options['all'];
@@ -268,7 +293,16 @@ class MigrateToolsCommands extends DrushCommands {
throw new \Exception(dt('You must specify --all, --group, --tag or one or more migration names separated by commas'));
}
foreach (['limit', 'feedback', 'idlist', 'update', 'force', 'execute-dependencies'] as $option) {
$possible_options = [
'limit',
'feedback',
'idlist',
'idlist-delimiter',
'update',
'force',
'execute-dependencies',
];
foreach ($possible_options as $option) {
if ($options[$option]) {
$additional_options[$option] = $options[$option];
}
@@ -303,6 +337,11 @@ class MigrateToolsCommands extends DrushCommands {
* @option group A comma-separated list of migration groups to rollback
* @option tag ID of the migration tag to rollback
* @option feedback Frequency of progress messages, in items processed
* @option feedback Frequency of progress messages, in items processed
* @option idlist Comma-separated list of IDs to rollback
* @option idlist-delimiter The delimiter for records, defaults to ':'
*
* @default $options []
*
* @usage migrate:rollback --all
* Perform all migrations
@@ -314,6 +353,8 @@ class MigrateToolsCommands extends DrushCommands {
* Rollback all migrations in the beer group and with the user tag
* @usage migrate:rollback beer_term,beer_node
* Rollback imported terms and nodes
* @usage migrate:rollback beer_user --idlist=5
* Rollback imported user record with source ID 5
* @validate-module-enabled migrate_tools
*
* @aliases mr, migrate-rollback
@@ -321,7 +362,15 @@ class MigrateToolsCommands extends DrushCommands {
* @throws \Exception
* If there are not enough parameters to the command.
*/
public function rollback($migration_names = '', array $options = ['all' => NULL, 'group' => NULL, 'tag' => NULL, 'feedback' => NULL]) {
public function rollback($migration_names = '', array $options = []) {
$options += [
'all' => NULL,
'group' => NULL,
'tag' => NULL,
'feedback' => NULL,
'idlist' => NULL,
'idlist-delimiter' => ':',
];
$group_names = $options['group'];
$tag_names = $options['tag'];
$all = $options['all'];
@@ -330,8 +379,10 @@ class MigrateToolsCommands extends DrushCommands {
throw new \Exception(dt('You must specify --all, --group, --tag, or one or more migration names separated by commas'));
}
if ($options['feedback']) {
$additional_options['feedback'] = $options['feedback'];
foreach (['feedback', 'idlist', 'idlist-delimiter'] as $option) {
if ($options[$option]) {
$additional_options[$option] = $options[$option];
}
}
$migrations = $this->migrationsList($migration_names, $options);
@@ -456,6 +507,10 @@ class MigrateToolsCommands extends DrushCommands {
* @command migrate:messages
*
* @option csv Export messages as a CSV
* @option idlist Comma-separated list of IDs to import
* @option idlist-delimiter The delimiter for records, defaults to ':'
*
* @default $options []
*
* @usage migrate:messages MyNode
* Show all messages for the MyNode migration
@@ -473,7 +528,12 @@ class MigrateToolsCommands extends DrushCommands {
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
* Source fields of the given migration formatted as a table.
*/
public function messages($migration_id, array $options = ['csv' => NULL]) {
public function messages($migration_id, array $options = []) {
$options += [
'csv' => NULL,
'idlist' => NULL,
'idlist-delimiter' => ':',
];
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance(
$migration_id
@@ -484,8 +544,8 @@ class MigrateToolsCommands extends DrushCommands {
);
return NULL;
}
$map = $migration->getIdMap();
$id_list = MigrateTools::buildIdList($options);
$map = new IdMapFilter($migration->getIdMap(), $id_list);
$table = [];
foreach ($map->getMessageIterator() as $row) {
unset($row->msgid);
@@ -561,6 +621,8 @@ class MigrateToolsCommands extends DrushCommands {
* @param array $options
* Command options.
*
* @default $options []
*
* @return \Drupal\migrate\Plugin\MigrationInterface[][]
* An array keyed by migration group, each value containing an array of
* migrations or an empty array if no migrations match the input criteria.
@@ -586,9 +648,9 @@ class MigrateToolsCommands extends DrushCommands {
}
else {
// Get the requested migrations.
$migration_ids = explode(',', Unicode::strtolower($migration_ids));
$migration_ids = explode(',', mb_strtolower($migration_ids));
foreach ($plugins as $id => $migration) {
if (in_array(Unicode::strtolower($id), $migration_ids)) {
if (in_array(mb_strtolower($id), $migration_ids)) {
$matched_migrations[$id] = $migration;
}
}
@@ -624,7 +686,7 @@ class MigrateToolsCommands extends DrushCommands {
)) ? $search_value : 'default';
if (empty($search_value) || $search_value == $configured_id) {
if (empty($migration_ids) || in_array(
Unicode::strtolower($id),
mb_strtolower($id),
$migration_ids
)) {
$filtered_migrations[$id] = $migration;
@@ -640,7 +702,7 @@ class MigrateToolsCommands extends DrushCommands {
// Sort the matched migrations by group.
if (!empty($matched_migrations)) {
foreach ($matched_migrations as $id => $migration) {
$configured_group_id = empty($migration->get('migration_group')) ? 'default' : $migration->get('migration_group');
$configured_group_id = empty($migration->migration_group) ? 'default' : $migration->migration_group;
$migrations[$configured_group_id][$id] = $migration;
}
}
@@ -660,6 +722,8 @@ class MigrateToolsCommands extends DrushCommands {
* @param array $options
* Additional options of the command.
*
* @default $options []
*
* @throws \Exception
* If some migrations failed during execution.
*/
@@ -686,7 +750,16 @@ class MigrateToolsCommands extends DrushCommands {
$migration->set('requirements', []);
}
if (!empty($options['update'])) {
$migration->getIdMap()->prepareUpdate();
if (empty($options['idlist'])) {
$migration->getIdMap()->prepareUpdate();
}
else {
$source_id_values_list = MigrateTools::buildIdList($options);
$keys = array_keys($migration->getSourcePlugin()->getIds());
foreach ($source_id_values_list as $source_id_values) {
$migration->getIdMap()->setUpdate(array_combine($keys, $source_id_values));
}
}
}
$executable = new MigrateExecutable($migration, $this->getMigrateMessage(), $options);
// drush_op() provides --simulate support.
@@ -141,4 +141,22 @@ class MessageController extends ControllerBase {
return $build;
}
/**
* Get the title of the page.
*
* @param \Drupal\migrate_plus\Entity\MigrationGroupInterface $migration_group
* The migration group.
* @param \Drupal\migrate_plus\Entity\MigrationInterface $migration
* The $migration.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
* The translated title.
*/
public function title(MigrationGroupInterface $migration_group, MigratePlusMigrationInterface $migration) {
return $this->t(
'Messages of %migration',
['%migration' => $migration->label()]
);
}
}
@@ -211,6 +211,9 @@ class MigrationController extends ControllerBase implements ContainerInjectionIn
$row = [];
$row[] = ['data' => Html::escape($destination_id)];
if (isset($process_line[0]['source'])) {
if (is_array($process_line[0]['source'])) {
$process_line[0]['source'] = implode(', ', $process_line[0]['source']);
}
$row[] = ['data' => Xss::filterAdmin($process_line[0]['source'])];
}
else {
@@ -142,7 +142,7 @@ class MigrationListBuilder extends ConfigEntityListBuilder implements EntityHand
try {
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance($migration_entity->id());
$migration_group = $migration->get('migration_group');
$migration_group = $migration_entity->get('migration_group');
if (!$migration_group) {
$migration_group = 'default';
}
@@ -60,7 +60,7 @@ class MigrationDeleteForm extends EntityConfirmFormBase {
$this->entity->delete();
// Set a message that the entity was deleted.
drupal_set_message(t('Migration %label was deleted.', [
$this->messenger()->addStatus($this->t('Migration %label was deleted.', [
'%label' => $this->entity->label(),
]));
@@ -29,7 +29,7 @@ class MigrationEditForm extends MigrationFormBase {
*/
public function actions(array $form, FormStateInterface $form_state) {
$actions = parent::actions($form, $form_state);
$actions['submit']['#value'] = t('Update Migration');
$actions['submit']['#value'] = $this->t('Update Migration');
return $actions;
}
@@ -67,24 +67,24 @@ class MigrationExecuteForm extends FormBase {
// Build the 'Update options' form.
$form = [
'#type' => 'fieldset',
'#title' => t('Operations'),
'#title' => $this->t('Operations'),
];
$options = [
'import' => t('Import'),
'rollback' => t('Rollback'),
'stop' => t('Stop'),
'reset' => t('Reset'),
'import' => $this->t('Import'),
'rollback' => $this->t('Rollback'),
'stop' => $this->t('Stop'),
'reset' => $this->t('Reset'),
];
$form['operation'] = [
'#type' => 'select',
'#title' => t('Choose an operation to run'),
'#title' => $this->t('Choose an operation to run'),
'#options' => $options,
'#default_value' => 'import',
'#required' => TRUE,
];
$form['submit'] = [
'#type' => 'submit',
'#value' => t('Execute'),
'#value' => $this->t('Execute'),
];
$definitions = [];
$definitions[] = $this->t('Import: Imports all previously unprocessed records from the source, plus any records marked for update, into destination Drupal objects.');
@@ -100,26 +100,31 @@ class MigrationExecuteForm extends FormBase {
$form['options'] = [
'#type' => 'fieldset',
'#title' => t('Options'),
'#title' => $this->t('Options'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
];
$form['options']['update'] = [
'#type' => 'checkbox',
'#title' => t('Update'),
'#description' => t('Check this box to update all previously-imported content
'#title' => $this->t('Update'),
'#description' => $this->t('Check this box to update all previously-imported content
in addition to importing new content. Leave unchecked to only import
new content'),
];
$form['options']['force'] = [
'#type' => 'checkbox',
'#title' => t('Ignore dependencies'),
'#description' => t('Check this box to ignore dependencies when running imports
'#title' => $this->t('Ignore dependencies'),
'#description' => $this->t('Check this box to ignore dependencies when running imports
- all tasks will run whether or not their dependent tasks have
completed.'),
];
// @TODO: Limit is not working. Perhaps because of batch? See
// https://www.drupal.org/project/migrate_tools/issues/2924298.
$form['options']['limit'] = [
'#type' => 'textfield',
'#title' => $this->t('Limit to:'),
'#size' => 10,
'#description' => $this->t('Set a limit of how many items to process for each migration task.'),
];
return $form;
}
@@ -97,7 +97,7 @@ class MigrationFormBase extends EntityForm {
foreach ($groups as $group) {
$group_options[$group->id()] = $group->label();
}
if (!$migration->get('migration_group') && isset($group_options['default'])) {
if (!$migration->migration_group && isset($group_options['default'])) {
$migration->set('migration_group', 'default');
}
@@ -105,7 +105,7 @@ class MigrationFormBase extends EntityForm {
'#type' => 'select',
'#title' => $this->t('Migration Group'),
'#empty_value' => '',
'#default_value' => $migration->get('migration_group'),
'#default_value' => $migration->migration_group,
'#options' => $group_options,
'#description' => $this->t('Assign this migration to an existing group.'),
];
@@ -150,7 +150,7 @@ class MigrationFormBase extends EntityForm {
* An array of supported actions for the current entity form.
*/
protected function actions(array $form, FormStateInterface $form_state) {
// Get the basic actins from the base class.
// Get the basic actions from the base class.
$actions = parent::actions($form, $form_state);
// Change the submit button text.
@@ -169,11 +169,11 @@ class MigrationFormBase extends EntityForm {
if ($status == SAVED_UPDATED) {
// If we edited an existing entity...
drupal_set_message($this->t('Migration %label has been updated.', ['%label' => $migration->label()]));
$this->messenger()->addStatus($this->t('Migration %label has been updated.', ['%label' => $migration->label()]));
}
else {
// If we created a new entity...
drupal_set_message($this->t('Migration %label has been added.', ['%label' => $migration->label()]));
$this->messenger()->addStatus($this->t('Migration %label has been added.', ['%label' => $migration->label()]));
}
// Redirect the user back to the listing route after the save operation.
@@ -60,7 +60,7 @@ class MigrationGroupDeleteForm extends EntityConfirmFormBase {
$this->entity->delete();
// Set a message that the entity was deleted.
drupal_set_message(t('Migration group %label was deleted.', [
$this->messenger()->addStatus($this->t('Migration group %label was deleted.', [
'%label' => $this->entity->label(),
]));
@@ -28,7 +28,7 @@ class MigrationGroupEditForm extends MigrationGroupFormBase {
*/
public function actions(array $form, FormStateInterface $form_state) {
$actions = parent::actions($form, $form_state);
$actions['submit']['#value'] = t('Update Migration Group');
$actions['submit']['#value'] = $this->t('Update Migration Group');
return $actions;
}
@@ -158,11 +158,11 @@ class MigrationGroupFormBase extends EntityForm {
if ($status == SAVED_UPDATED) {
// If we edited an existing entity...
drupal_set_message($this->t('Migration group %label has been updated.', ['%label' => $migration_group->label()]));
$this->messenger()->addStatus($this->t('Migration group %label has been updated.', ['%label' => $migration_group->label()]));
}
else {
// If we created a new entity...
drupal_set_message($this->t('Migration group %label has been added.', ['%label' => $migration_group->label()]));
$this->messenger()->addStatus($this->t('Migration group %label has been added.', ['%label' => $migration_group->label()]));
}
// Redirect the user back to the listing route after the save operation.
@@ -25,11 +25,11 @@ use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
* migration yml itself.
*
* Changes made to the column configuration, or aliases, are stored in the
* private migrate_toools private store keyed by the migration plugin id. The
* private migrate_tools private store keyed by the migration plugin id. The
* data stored for each migrations consists of two arrays, the 'original' column
* aliases and the 'updated' column aliases.
*
* An addtional list of all changed migration id is kept in the store, in the
* An additional list of all changed migration id is kept in the store, in the
* key 'migrations_changed'
*
* Private Store Usage:
@@ -38,7 +38,6 @@ use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
* [migration_id]: The original and changed values for this column assignments
*
* Format of the source configuration saved in the store.
* @code
* migration_id
* original
* column_index1
@@ -50,22 +49,19 @@ use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
* property 2 => label 2
* column_index2
* property 1 => label 1
* @endcode
*
* Example source configuration.
* @code
* Example source configuration:
* custom_migration
* original
* 2
* title => title
* 3
* body => foo
* updated
* 8
* title => new_title
* 9
* body => new_body
* @endcode
* original
* 2
* title => title
* 3
* body => foo
* updated
* 8
* title => new_title
* 9
* body => new_body
*/
class SourceCsvForm extends FormBase {
@@ -0,0 +1,42 @@
<?php
namespace Drupal\migrate_tools;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
/**
* Class to filter ID map by an ID list.
*/
class IdMapFilter extends \FilterIterator {
/**
* List of specific source IDs to import.
*
* @var array
*/
protected $idList;
/**
* IdMapFilter constructor.
*
* @param \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map
* The ID map.
* @param array $id_list
* The id list to use in the filter.
*/
public function __construct(MigrateIdMapInterface $id_map, array $id_list) {
parent::__construct($id_map);
$this->idList = $id_list;
}
/**
* {@inheritdoc}
*/
public function accept() {
// Row is included.
if (empty($this->idList) || in_array(array_values($this->getInnerIterator()->currentSource()), $this->idList)) {
return TRUE;
}
}
}
@@ -2,15 +2,16 @@
namespace Drupal\migrate_tools;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\migrate\MigrateMessage;
use Drupal\migrate\MigrateMessageInterface;
use Drupal\migrate\Plugin\Migration;
use Drupal\migrate\Plugin\MigrationInterface;
/**
* Defines a migrate executable class for batch migrations through UI.
*/
class MigrateBatchExecutable extends MigrateExecutable {
use StringTranslationTrait;
/**
* Representing a batch import operation.
@@ -98,10 +99,10 @@ class MigrateBatchExecutable extends MigrateExecutable {
if (count($operations) > 0) {
$batch = [
'operations' => $operations,
'title' => t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
'init_message' => t('Start migrating %migrate', ['%migrate' => $this->migration->label()]),
'progress_message' => t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
'error_message' => t('An error occurred while migrating %migrate.', ['%migrate' => $this->migration->label()]),
'title' => $this->t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
'init_message' => $this->t('Start migrating %migrate', ['%migrate' => $this->migration->label()]),
'progress_message' => $this->t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
'error_message' => $this->t('An error occurred while migrating %migrate.', ['%migrate' => $this->migration->label()]),
'finished' => '\Drupal\migrate_tools\MigrateBatchExecutable::batchFinishedImport',
];
@@ -181,6 +182,12 @@ class MigrateBatchExecutable extends MigrateExecutable {
$message = new MigrateMessage();
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = \Drupal::getContainer()->get('plugin.manager.migration')->createInstance($migration_id);
// Each batch run we need to reinitialize the counter for the migration.
if (!empty($options['limit']) && isset($context['results'][$migration->id()]['@numitems'])) {
$options['limit'] = $options['limit'] - $context['results'][$migration->id()]['@numitems'];
}
$executable = new MigrateBatchExecutable($migration, $message, $options);
if (empty($context['sandbox']['total'])) {
@@ -249,7 +256,7 @@ class MigrateBatchExecutable extends MigrateExecutable {
foreach ($results as $migration_id => $result) {
$singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
$plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
drupal_set_message(\Drupal::translation()->formatPlural($result['@numitems'],
\Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural($result['@numitems'],
$singular_message,
$plural_message,
$result));
@@ -2,19 +2,19 @@
namespace Drupal\migrate_tools;
use Drupal\migrate\Event\MigrateEvents;
use Drupal\migrate\Event\MigrateImportEvent;
use Drupal\migrate\Event\MigrateMapDeleteEvent;
use Drupal\migrate\Event\MigrateMapSaveEvent;
use Drupal\migrate\Event\MigratePreRowSaveEvent;
use Drupal\migrate\Event\MigrateRollbackEvent;
use Drupal\migrate\Event\MigrateRowDeleteEvent;
use Drupal\migrate\MigrateExecutable as MigrateExecutableBase;
use Drupal\migrate\MigrateMessageInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Event\MigrateEvents;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate_plus\Event\MigrateEvents as MigratePlusEvents;
use Drupal\migrate\Event\MigrateMapSaveEvent;
use Drupal\migrate\Event\MigrateMapDeleteEvent;
use Drupal\migrate\Event\MigrateImportEvent;
use Drupal\migrate_plus\Event\MigratePrepareRowEvent;
/**
@@ -104,14 +104,7 @@ class MigrateExecutable extends MigrateExecutableBase {
if (isset($options['feedback'])) {
$this->feedback = $options['feedback'];
}
if (isset($options['idlist'])) {
if (is_string($options['idlist'])) {
$this->idlist = explode(',', $options['idlist']);
array_walk($this->idlist, function (&$value, $key) {
$value = explode(':', $value);
});
}
}
$this->idlist = MigrateTools::buildIdList($options);
$this->listeners[MigrateEvents::MAP_SAVE] = [$this, 'onMapSave'];
$this->listeners[MigrateEvents::MAP_DELETE] = [$this, 'onMapDelete'];
@@ -294,6 +287,8 @@ class MigrateExecutable extends MigrateExecutableBase {
* The map event.
*/
public function onPostRollback(MigrateRollbackEvent $event) {
$migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
$migrate_last_imported_store->set($event->getMigration()->id(), FALSE);
$this->rollbackMessage();
$this->removeListeners();
}
@@ -363,6 +358,8 @@ class MigrateExecutable extends MigrateExecutableBase {
* @throws \Drupal\migrate\MigrateSkipRowException
*/
public function onPrepareRow(MigratePrepareRowEvent $event) {
// TODO: remove after 8.6 suppor is sunset.
// @see https://www.drupal.org/project/migrate_tools/issues/3008316
if (!empty($this->idlist)) {
$row = $event->getRow();
// TODO: replace for $source_id = $row->getSourceIdValues();
@@ -378,7 +375,7 @@ class MigrateExecutable extends MigrateExecutableBase {
}
}
if ($skip) {
throw new MigrateSkipRowException(NULL, FALSE);
throw new MigrateSkipRowException('Skipped due to idlist.', FALSE);
}
}
if ($this->feedback && ($this->counter) && $this->counter % $this->feedback == 0) {
@@ -389,7 +386,20 @@ class MigrateExecutable extends MigrateExecutableBase {
if ($this->itemLimit && ($this->itemLimitCounter + 1) >= $this->itemLimit) {
$event->getMigration()->interruptMigration(MigrationInterface::RESULT_COMPLETED);
}
}
/**
* {@inheritdoc}
*/
protected function getSource() {
return new SourceFilter(parent::getSource(), $this->idlist);
}
/**
* {@inheritdoc}
*/
protected function getIdMap() {
return new IdMapFilter(parent::getIdMap(), $this->idlist);
}
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\migrate_tools;
/**
* Utility functionality for use in migrate_tools.
*/
class MigrateTools {
/**
* Build the list of specific source IDs to import.
*
* @param array $options
* The migration executable options.
*
* @return array
* The ID list.
*/
public static function buildIdList(array $options) {
$options += [
'idlist' => NULL,
'idlist-delimiter' => ':',
];
$id_list = [];
if ($options['idlist']) {
$id_list = explode(',', $options['idlist']);
array_walk($id_list, function (&$value) use ($options) {
$value = str_getcsv($value, $options['idlist-delimiter']);
});
}
return $id_list;
}
}
@@ -0,0 +1,53 @@
<?php
namespace Drupal\migrate_tools;
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
use Drupal\migrate\Plugin\MigrateSourceInterface;
/**
* Class to filter source by an ID list.
*/
class SourceFilter extends \FilterIterator {
/**
* List of specific source IDs to import.
*
* @var array
*/
protected $idList;
/**
* SourceFilter constructor.
*
* @param \Drupal\migrate\Plugin\MigrateSourceInterface $source
* The ID map.
* @param array $id_list
* The id list to use in the filter.
*/
public function __construct(MigrateSourceInterface $source, array $id_list) {
parent::__construct($source);
$this->idList = $id_list;
}
/**
* {@inheritdoc}
*/
public function accept() {
// No idlist filtering, don't filter.
if (empty($this->idList)) {
return TRUE;
}
// Some source plugins do not extend SourcePluginBase. These cannot be
// filtered so warn and return all values.
if (!$this->getInnerIterator() instanceof SourcePluginBase) {
trigger_error(sprintf('The source plugin %s is not an instance of %s. Extend from %s to support idlist filtering.', $this->getInnerIterator()->getPluginId(), SourcePluginBase::class, SourcePluginBase::class));
return TRUE;
}
// Row is included.
if (in_array(array_values($this->getInnerIterator()->getCurrentIds()), $this->idList)) {
return TRUE;
}
}
}
@@ -8,8 +8,8 @@ dependencies:
- migrate_plus:migrate_plus
- migrate_plus:migrate_source_csv
# Information added by Drupal.org packaging script on 2018-08-27
version: '8.x-4.0'
# Information added by Drupal.org packaging script on 2019-01-07
version: '8.x-4.1'
core: '8.x'
project: 'migrate_tools'
datestamp: 1535380087
datestamp: 1546879109
@@ -7,8 +7,8 @@ dependencies:
- drupal:migrate (>=8.3)
- migrate_plus:migrate_plus
# Information added by Drupal.org packaging script on 2018-08-27
version: '8.x-4.0'
# Information added by Drupal.org packaging script on 2019-01-07
version: '8.x-4.1'
core: '8.x'
project: 'migrate_tools'
datestamp: 1535380087
datestamp: 1546879109
@@ -2,6 +2,7 @@
namespace Drupal\Tests\migrate_tools\Functional;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\BrowserTestBase;
@@ -11,6 +12,7 @@ use Drupal\Tests\BrowserTestBase;
* @group migrate_tools
*/
class MigrateExecutionFormTest extends BrowserTestBase {
use StringTranslationTrait;
/**
* {@inheritdoc}
@@ -77,21 +79,21 @@ class MigrateExecutionFormTest extends BrowserTestBase {
$edit = [
'operation' => 'import',
];
$this->drupalPostForm($urlPath, $edit, t('Execute'));
$this->drupalPostForm($urlPath, $edit, $this->t('Execute'));
$real_count = $this->vocabularyQuery->count()->execute();
$expected_count = 3;
$this->assertEquals($expected_count, $real_count);
$edit = [
'operation' => 'rollback',
];
$this->drupalPostForm($urlPath, $edit, t('Execute'));
$this->drupalPostForm($urlPath, $edit, $this->t('Execute'));
$real_count = $this->vocabularyQuery->count()->execute();
$expected_count = 0;
$this->assertEquals($expected_count, $real_count);
$edit = [
'operation' => 'import',
];
$this->drupalPostForm($urlPath, $edit, t('Execute'));
$this->drupalPostForm($urlPath, $edit, $this->t('Execute'));
$real_count = $this->vocabularyQuery->count()->execute();
$expected_count = 3;
$this->assertEquals($expected_count, $real_count);
@@ -4,6 +4,7 @@ namespace Drupal\Tests\migrate_tools\Functional;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\VocabularyInterface;
@@ -16,6 +17,7 @@ use Drupal\taxonomy\VocabularyInterface;
* @group migrate_tools
*/
class SourceCsvFormTest extends BrowserTestBase {
use StringTranslationTrait;
/**
* Temporary store for column assignment changes.
@@ -134,7 +136,7 @@ EOD;
'edit-name' => 1,
'edit-description' => 1,
];
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
$this->drupalPostForm($editUrlPath, $edit, $this->t('Submit'));
$session->responseContains('Source properties can not share the same source column.');
$this->assertTrue($session->optionExists('edit-vid', 'description')
->isSelected());
@@ -149,7 +151,7 @@ EOD;
'edit-name' => 0,
'edit-description' => 1,
];
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
$this->drupalPostForm($editUrlPath, $edit, $this->t('Submit'));
$this->assertTrue($session->optionExists('edit-vid', 'weight')
->isSelected());
$this->assertTrue($session->optionExists('edit-name', 'vid')
@@ -184,22 +186,22 @@ EOD;
$edit = [
'operation' => 'import',
];
$this->drupalPostForm($executeUrlPath, $edit, t('Execute'));
$this->drupalPostForm($executeUrlPath, $edit, $this->t('Execute'));
$session->responseContains("Processed 1 item (1 created, 0 updated, 0 failed, 0 ignored) - done with 'csv_source_test'");
// Rollback.
$edit = [
'operation' => 'rollback',
];
$this->drupalPostForm($executeUrlPath, $edit, t('Execute'));
$this->drupalPostForm($executeUrlPath, $edit, $this->t('Execute'));
// Restore to an order that will succesfully migrate.
// Restore to an order that will successfully migrate.
$edit = [
'edit-vid' => 0,
'edit-name' => 1,
'edit-description' => 2,
];
$this->drupalPostForm($editUrlPath, $edit, t('Submit'));
$this->drupalPostForm($editUrlPath, $edit, $this->t('Submit'));
$this->assertTrue($session->optionExists('edit-vid', 'vid')
->isSelected());
$this->assertTrue($session->optionExists('edit-name', 'name')
@@ -212,7 +214,7 @@ EOD;
'operation' => 'import',
];
drupal_flush_all_caches();
$this->drupalPostForm($executeUrlPath, $edit, t('Execute'));
$this->drupalPostForm($executeUrlPath, $edit, $this->t('Execute'));
$session->responseContains("Processed 4 items (4 created, 0 updated, 0 failed, 0 ignored) - done with 'csv_source_test'");
$this->assertEntity('tags', 'Tags', 'Use tags to group articles');
$this->assertEntity('forums', 'Sujet de discussion', 'Forum navigation vocabulary');
@@ -0,0 +1,217 @@
<?php
namespace Drupal\Tests\migrate_tools\Kernel;
use Drupal\migrate_tools\Commands\MigrateToolsCommands;
use Drupal\Tests\migrate\Kernel\MigrateTestBase;
use Drupal\migrate\Plugin\MigrationInterface;
/**
* Tests for the Drush 9 commands.
*
* @group migrate_tools
*/
class DrushTest extends MigrateTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'migrate_tools_test',
'migrate_tools',
'migrate_plus',
'taxonomy',
'text',
'system',
];
/**
* Base options array for import.
*
* @var array
*/
protected $importBaseOptions = [
'all' => NULL,
'group' => NULL,
'tag' => NULL,
'limit' => NULL,
'feedback' => NULL,
'idlist' => NULL,
'idlist-delimiter' => ':',
'update' => NULL,
'force' => NULL,
'execute-dependencies' => NULL,
];
/**
* The Migrate Tools Command drush service.
*
* @var \Drupal\migrate_tools\Commands\MigrateToolsCommands
*/
protected $commands;
/**
* The migration plugin manager.
*
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
*/
protected $migrationPluginManager;
/**
* The logger.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->installConfig('migrate_plus');
$this->installConfig('migrate_tools_test');
$this->installEntitySchema('taxonomy_term');
$this->installSchema('system', ['key_value', 'key_value_expire']);
$this->migrationPluginManager = $this->container->get('plugin.manager.migration');
$this->logger = $this->container->get('logger.channel.migrate_tools');
$this->commands = new MigrateToolsCommands(
$this->migrationPluginManager,
$this->container->get('date.formatter'),
$this->container->get('entity_type.manager'),
$this->container->get('keyvalue'));
$this->commands->setLogger($this->logger);
}
/**
* Tests drush ms.
*/
public function testStatus() {
$this->executeMigration('fruit_terms');
/** @var \Consolidation\OutputFormatters\StructuredData\RowsOfFields $result */
$result = $this->commands->status('fruit_terms');
$rows = $result->getArrayCopy();
$this->assertSame(1, count($rows));
$row = reset($rows);
$this->assertSame('fruit_terms', $row['id']);
$this->assertSame(3, $row['total']);
$this->assertSame(3, $row['imported']);
$this->assertSame('Idle', $row['status']);
}
/**
* Tests drush mim.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function testImport() {
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
$id_map = $migration->getIdMap();
$this->commands->import('fruit_terms', ['idlist' => 'Apple'] + $this->importBaseOptions);
$this->assertSame(1, $id_map->importedCount());
$this->commands->import('fruit_terms');
$this->assertSame(3, $id_map->importedCount());
$this->commands->import('fruit_terms', ['idlist' => 'Apple', 'update' => TRUE] + $this->importBaseOptions);
$this->assertSame(0, count($id_map->getRowsNeedingUpdate(100)));
}
/**
* Tests drush mmsg.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function testMessages() {
$this->executeMigration('fruit_terms');
$message = $this->getRandomGenerator()->string(16);
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
$id_map = $migration->getIdMap();
$id_map->saveMessage(['name' => 'Apple'], $message);
/** @var \Consolidation\OutputFormatters\StructuredData\RowsOfFields $result */
$result = $this->commands->messages('fruit_terms');
$rows = $result->getArrayCopy();
$this->assertSame($message, $rows[0]['message']);
}
/**
* Tests drush mr.
*/
public function testRollback() {
$this->executeMigration('fruit_terms');
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
$id_map = $migration->getIdMap();
$this->assertSame(3, $id_map->importedCount());
$this->commands->rollback('fruit_terms');
$this->assertSame(0, $id_map->importedCount());
}
/**
* Tests drush mrs.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function testReset() {
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
$migration->setStatus(MigrationInterface::STATUS_IMPORTING);
$this->assertSame('Importing', $this->commands->status('fruit_terms')->getArrayCopy()[0]['status']);
$this->commands->resetStatus('fruit_terms');
$this->assertSame(MigrationInterface::STATUS_IDLE, $migration->getStatus());
}
/**
* Tests drush mst.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function testStop() {
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance('fruit_terms');
$migration->setStatus(MigrationInterface::STATUS_IMPORTING);
$this->commands->stop('fruit_terms');
$this->assertSame(MigrationInterface::STATUS_STOPPING, $migration->getStatus());
}
/**
* Tests drush mfs.
*/
public function testFieldsSource() {
/** @var \Consolidation\OutputFormatters\StructuredData\RowsOfFields $result */
$result = $this->commands->fieldsSource('fruit_terms');
$rows = $result->getArrayCopy();
$this->assertSame(1, count($rows));
$this->assertSame('name', $rows[0]['machine_name']);
$this->assertSame('name', $rows[0]['description']);
}
}
namespace Drupal\migrate_tools\Commands;
/**
* Stub for drush_op.
*
* @param callable $callable
* The function to call.
*/
function drush_op(callable $callable) {
$args = func_get_args();
array_shift($args);
call_user_func_array($callable, $args);
}
/**
* Stub for dt().
*
* @param string $text
* The text.
*
* @return string
* The text.
*/
function dt($text) {
return $text;
}
@@ -0,0 +1,81 @@
<?php
namespace Drupal\Tests\migrate_tools\Kernel;
use Drupal\migrate_tools\MigrateExecutable;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\migrate\Kernel\MigrateTestBase;
/**
* Tests imports.
*
* @group migrate
*/
class MigrateImportTest extends MigrateTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['field', 'taxonomy', 'text', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
$this->installEntitySchema('taxonomy_vocabulary');
$this->installEntitySchema('taxonomy_term');
$this->installConfig(['taxonomy']);
}
/**
* Tests rolling back configuration and content entities.
*/
public function testImport() {
// We use vocabularies to demonstrate importing and rolling back
// configuration entities.
$vocabulary_data_rows = [
['id' => '1', 'name' => 'categories', 'weight' => '2'],
['id' => '2', 'name' => 'tags', 'weight' => '1'],
];
$ids = ['id' => ['type' => 'integer']];
$definition = [
'id' => 'vocabularies',
'migration_tags' => ['Import and rollback test'],
'source' => [
'plugin' => 'embedded_data',
'data_rows' => $vocabulary_data_rows,
'ids' => $ids,
],
'process' => [
'vid' => 'id',
'name' => 'name',
'weight' => 'weight',
],
'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
];
/** @var \Drupal\migrate\Plugin\MigrationInterface $vocabulary_migration */
$vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
$vocabulary_id_map = $vocabulary_migration->getIdMap();
// Test id list import.
$executable = new MigrateExecutable($vocabulary_migration, $this, ['idlist' => 2]);
$executable->import();
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
$vocabulary = Vocabulary::load(1);
$this->assertFalse($vocabulary);
$map_row = $vocabulary_id_map->getRowBySource(['id' => 1]);
$this->assertFalse($map_row);
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
$vocabulary = Vocabulary::load(2);
$this->assertTrue($vocabulary);
$map_row = $vocabulary_id_map->getRowBySource(['id' => 2]);
$this->assertNotNull($map_row['destid1']);
}
}
@@ -0,0 +1,106 @@
<?php
namespace Drupal\Tests\migrate_tools\Kernel;
use Drupal\migrate_tools\MigrateExecutable;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\migrate\Kernel\MigrateTestBase;
/**
* Tests rolling back of imports.
*
* @group migrate
*/
class MigrateRollbackTest extends MigrateTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['field', 'taxonomy', 'text', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
$this->installEntitySchema('taxonomy_vocabulary');
$this->installEntitySchema('taxonomy_term');
$this->installConfig(['taxonomy']);
}
/**
* Tests rolling back configuration and content entities.
*/
public function testRollback() {
// We use vocabularies to demonstrate importing and rolling back
// configuration entities.
$vocabulary_data_rows = [
['id' => '1', 'name' => 'categories', 'weight' => '2'],
['id' => '2', 'name' => 'tags', 'weight' => '1'],
];
$ids = ['id' => ['type' => 'integer']];
$definition = [
'id' => 'vocabularies',
'migration_tags' => ['Import and rollback test'],
'source' => [
'plugin' => 'embedded_data',
'data_rows' => $vocabulary_data_rows,
'ids' => $ids,
],
'process' => [
'vid' => 'id',
'name' => 'name',
'weight' => 'weight',
],
'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
];
/** @var \Drupal\migrate\Plugin\MigrationInterface $vocabulary_migration */
$vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
$vocabulary_id_map = $vocabulary_migration->getIdMap();
// Import and validate vocabulary config entities were created.
$executable = new MigrateExecutable($vocabulary_migration, $this, []);
$executable->import();
foreach ($vocabulary_data_rows as $row) {
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
$vocabulary = Vocabulary::load($row['id']);
$this->assertTrue($vocabulary);
$map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
$this->assertNotNull($map_row['destid1']);
}
// Test id list rollback.
$rollback_executable = new MigrateExecutable($vocabulary_migration, $this, ['idlist' => 1]);
$rollback_executable->rollback();
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
$vocabulary = Vocabulary::load(1);
$this->assertFalse($vocabulary);
$map_row = $vocabulary_id_map->getRowBySource(['id' => 1]);
$this->assertFalse($map_row);
// TODO: remove after 8.6 is sunset.
// @see https://www.drupal.org/project/migrate_tools/issues/3008316
include_once $this->root . '/core/includes/install.core.inc';
$version = _install_get_version_info(\Drupal::VERSION);
if ($version['minor'] == 6) {
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
$vocabulary = Vocabulary::load(1);
$this->assertFalse($vocabulary);
$map_row = $vocabulary_id_map->getRowBySource(['id' => 1]);
$this->assertFalse($map_row);
}
else {
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
$vocabulary = Vocabulary::load(2);
$this->assertTrue($vocabulary);
$map_row = $vocabulary_id_map->getRowBySource(['id' => 2]);
$this->assertNotNull($map_row['destid1']);
}
}
}
@@ -0,0 +1,85 @@
<?php
namespace Drupal\Tests\migrate_tools\Unit;
use Drupal\migrate_tools\MigrateTools;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\migrate_tools\MigrateTools
* @group migrate_tools
*/
class MigrateToolsTest extends UnitTestCase {
/**
* @covers ::buildIdList
*
* @dataProvider dataProviderIdList
*/
public function testBuildIdList(array $options, array $expected) {
$results = MigrateTools::buildIdList($options);
$this->assertEquals($results, $expected);
}
/**
* Data provider for testBuildIdList.
*/
public function dataProviderIdList() {
$cases = [];
$cases[] = [
'options' => [],
'expected' => [],
];
$cases['single id'] = [
'options' => [
'idlist' => 123,
],
'expected' => [[123]],
];
$cases['multiple ids'] = [
'options' => [
'idlist' => '123, 456',
],
'expected' => [
[123], [456],
],
];
$cases['default delimiter, composite key'] = [
'options' => [
'idlist' => '123:456',
],
'expected' => [
[123, 456],
],
];
$cases['special delimiter, single'] = [
'options' => [
'idlist' => '123:456',
'idlist-delimiter' => '~',
],
'expected' => [
['123:456'],
],
];
$cases['special delimiter, multiple'] = [
'options' => [
'idlist' => '123:456~987:654',
'idlist-delimiter' => '~',
],
'expected' => [
['123:456', '987:654'],
],
];
$cases['space delimiter, multiple'] = [
'options' => [
'idlist' => '123:456 987:654',
'idlist-delimiter' => ' ',
],
'expected' => [
['123:456', '987:654'],
],
];
return $cases;
}
}