added migration modules
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,15 @@
|
||||
The Migrate Tools module provides tools for running and managing Drupal 8
|
||||
migrations.
|
||||
|
||||
Drush commands supported include:
|
||||
|
||||
* migrate-status - Lists migrations and their status.
|
||||
* migrate-import - Performs import operations.
|
||||
* migrate-rollback - Performs rollback operations.
|
||||
* migrate-stop - Cleanly stops a running operation.
|
||||
* migrate-reset-status - Sets a migration status to Idle if it's gotten stuck.
|
||||
* migrate-messages - Lists any messages associated with a migration import.
|
||||
|
||||
The UI at this point provides a front-end equivalent to the migrate-status and
|
||||
migrate-messages commands. It will be enhanced to allow running the other
|
||||
operations, as well as provide the ability to create and alter migrations.
|
||||
@@ -0,0 +1,513 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* 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_tools\MigrateExecutable;
|
||||
use Drupal\migrate_tools\DrushLogMigrateMessage;
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
use Drupal\migrate_plus\Entity\MigrationGroup;
|
||||
|
||||
/**
|
||||
* Implements hook_drush_command().
|
||||
*/
|
||||
function migrate_tools_drush_command() {
|
||||
$items['migrate-status'] = [
|
||||
'description' => 'List all migrations with current status.',
|
||||
'options' => [
|
||||
'group' => 'A comma-separated list of migration groups to list',
|
||||
'tag' => 'Name of the migration tag to list',
|
||||
'names-only' => 'Only return names, not all the details (faster)',
|
||||
],
|
||||
'arguments' => [
|
||||
'migration' => 'Restrict to a comma-separated list of migrations. Optional',
|
||||
],
|
||||
'examples' => [
|
||||
'migrate-status' => 'Retrieve status for all migrations',
|
||||
'migrate-status --group=beer' => 'Retrieve status for all migrations in a given group',
|
||||
'migrate-status --tag=user' => 'Retrieve status for all migrations with a given tag',
|
||||
'migrate-status --group=beer --tag=user' => 'Retrieve status for all migrations in the beer group and with the user tag',
|
||||
'migrate-status beer_term,beer_node' => 'Retrieve status for specific migrations',
|
||||
],
|
||||
'drupal dependencies' => ['migrate_tools'],
|
||||
'aliases' => ['ms'],
|
||||
];
|
||||
|
||||
$items['migrate-import'] = [
|
||||
'description' => 'Perform one or more migration processes.',
|
||||
'options' => [
|
||||
'all' => 'Process all migrations.',
|
||||
'group' => 'A comma-separated list of migration groups to import',
|
||||
'tag' => 'Name of the migration tag to import',
|
||||
'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',
|
||||
'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.',
|
||||
],
|
||||
'arguments' => [
|
||||
'migration' => 'ID of migration(s) to import. Delimit multiple using commas.',
|
||||
],
|
||||
'examples' => [
|
||||
'migrate-import --all' => 'Perform all migrations',
|
||||
'migrate-import --group=beer' => 'Import all migrations in the beer group',
|
||||
'migrate-import --tag=user' => 'Import all migrations with the user tag',
|
||||
'migrate-import --group=beer --tag=user' => 'Import all migrations in the beer group and with the user tag',
|
||||
'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',
|
||||
],
|
||||
'drupal dependencies' => ['migrate_tools'],
|
||||
'aliases' => ['mi'],
|
||||
];
|
||||
|
||||
$items['migrate-rollback'] = array(
|
||||
'description' => 'Rollback one or more migrations.',
|
||||
'options' => array(
|
||||
'all' => 'Process all migrations.',
|
||||
'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',
|
||||
),
|
||||
'arguments' => array(
|
||||
'migration' => 'Name of migration(s) to rollback. Delimit multiple using commas.',
|
||||
),
|
||||
'examples' => array(
|
||||
'migrate-rollback --all' => 'Perform all migrations',
|
||||
'migrate-rollback --group=beer' => 'Rollback all migrations in the beer group',
|
||||
'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',
|
||||
),
|
||||
'drupal dependencies' => array('migrate_tools'),
|
||||
'aliases' => array('mr'),
|
||||
);
|
||||
|
||||
$items['migrate-stop'] = [
|
||||
'description' => 'Stop an active migration operation.',
|
||||
'arguments' => [
|
||||
'migration' => 'ID of migration to stop',
|
||||
],
|
||||
'drupal dependencies' => ['migrate_tools'],
|
||||
'aliases' => ['mst'],
|
||||
];
|
||||
|
||||
$items['migrate-reset-status'] = [
|
||||
'description' => 'Reset a active migration\'s status to idle.',
|
||||
'arguments' => [
|
||||
'migration' => 'ID of migration to reset',
|
||||
],
|
||||
'drupal dependencies' => ['migrate_tools'],
|
||||
'aliases' => ['mrs'],
|
||||
];
|
||||
|
||||
$items['migrate-messages'] = [
|
||||
'description' => 'View any messages associated with a migration.',
|
||||
'arguments' => [
|
||||
'migration' => 'ID of the migration',
|
||||
],
|
||||
'options' => [
|
||||
'csv' => 'Export messages as a CSV'
|
||||
],
|
||||
'examples' => [
|
||||
'migrate-messages MyNode' => 'Show all messages for the MyNode migration',
|
||||
],
|
||||
'drupal dependencies' => ['migrate_tools'],
|
||||
'aliases' => ['mmsg'],
|
||||
];
|
||||
|
||||
$items['migrate-fields-source'] = [
|
||||
'description' => 'List the fields available for mapping in a source.',
|
||||
'arguments' => [
|
||||
'migration' => 'ID of the migration',
|
||||
],
|
||||
'examples' => [
|
||||
'migrate-fields-source my_node' => 'List fields for the source in the my_node migration',
|
||||
],
|
||||
'drupal dependencies' => ['migrate_tools'],
|
||||
'aliases' => ['mfs'],
|
||||
];
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $migration_names
|
||||
*/
|
||||
function drush_migrate_tools_migrate_status($migration_names = '') {
|
||||
$names_only = drush_get_option('names-only');
|
||||
|
||||
$migrations = drush_migrate_tools_migration_list($migration_names);
|
||||
|
||||
$table = [];
|
||||
// Take it one group at a time, listing the migrations within each group.
|
||||
foreach ($migrations as $group_id => $migration_list) {
|
||||
$group = MigrationGroup::load($group_id);
|
||||
$group_name = !empty($group) ? "{$group->label()} ({$group->id()})" : $group_id;
|
||||
if ($names_only) {
|
||||
$table[] = [
|
||||
dt('Group: @name', array('@name' => $group_name))
|
||||
];
|
||||
}
|
||||
else {
|
||||
$table[] = [
|
||||
dt('Group: @name', array('@name' => $group_name)),
|
||||
dt('Status'),
|
||||
dt('Total'),
|
||||
dt('Imported'),
|
||||
dt('Unprocessed'),
|
||||
dt('Last imported'),
|
||||
];
|
||||
}
|
||||
foreach ($migration_list as $migration_id => $migration) {
|
||||
try {
|
||||
$map = $migration->getIdMap();
|
||||
$imported = $map->importedCount();
|
||||
$source_plugin = $migration->getSourcePlugin();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
drush_log(dt('Failure retrieving information on @migration: @message',
|
||||
['@migration' => $migration_id, '@message' => $e->getMessage()]));
|
||||
continue;
|
||||
}
|
||||
if ($names_only) {
|
||||
$table[] = [$migration_id];
|
||||
}
|
||||
else {
|
||||
try {
|
||||
$source_rows = $source_plugin->count();
|
||||
// -1 indicates uncountable sources.
|
||||
if ($source_rows == -1) {
|
||||
$source_rows = dt('N/A');
|
||||
$unprocessed = dt('N/A');
|
||||
}
|
||||
else {
|
||||
$unprocessed = $source_rows - $map->processedCount();
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
drush_print($e->getMessage());
|
||||
drush_log(dt('Could not retrieve source count from @migration: @message',
|
||||
['@migration' => $migration_id, '@message' => $e->getMessage()]));
|
||||
$source_rows = dt('N/A');
|
||||
$unprocessed = dt('N/A');
|
||||
}
|
||||
|
||||
$status = $migration->getStatusLabel();
|
||||
$migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
|
||||
$last_imported = $migrate_last_imported_store->get($migration->id(), FALSE);
|
||||
if ($last_imported) {
|
||||
/** @var DateFormatter $date_formatter */
|
||||
$date_formatter = \Drupal::service('date.formatter');
|
||||
$last_imported = $date_formatter->format($last_imported / 1000,
|
||||
'custom', 'Y-m-d H:i:s');
|
||||
}
|
||||
else {
|
||||
$last_imported = '';
|
||||
}
|
||||
$table[] = [$migration_id, $status, $source_rows, $imported, $unprocessed, $last_imported];
|
||||
}
|
||||
}
|
||||
}
|
||||
drush_print_table($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $migration_names
|
||||
*/
|
||||
function drush_migrate_tools_migrate_import($migration_names = '') {
|
||||
$group_names = drush_get_option('group');
|
||||
$tag_names = drush_get_option('tag');
|
||||
$all = drush_get_option('all');
|
||||
$options = [];
|
||||
if (!$all && !$group_names && !$migration_names && !$tag_names) {
|
||||
drush_set_error('MIGRATE_ERROR', dt('You must specify --all, --group, --tag or one or more migration names separated by commas'));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (['limit', 'feedback', 'idlist', 'update', 'force'] as $option) {
|
||||
if (drush_get_option($option)) {
|
||||
$options[$option] = drush_get_option($option);
|
||||
}
|
||||
}
|
||||
|
||||
$migrations = drush_migrate_tools_migration_list($migration_names);
|
||||
if (empty($migrations)) {
|
||||
drush_log(dt('No migrations found.'), 'error');
|
||||
}
|
||||
|
||||
// Take it one group at a time, importing the migrations within each group.
|
||||
foreach ($migrations as $group_id => $migration_list) {
|
||||
array_walk($migration_list, '_drush_migrate_tools_execute_migration', $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a single migration. If the --execute-dependencies option was given,
|
||||
* the migration's dependencies will also be executed first.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration to execute.
|
||||
* @param string $migration_id
|
||||
* The migration ID (not used, just an artifact of array_walk()).
|
||||
* @param array $options
|
||||
* Additional options for the migration.
|
||||
*/
|
||||
function _drush_migrate_tools_execute_migration(MigrationInterface $migration, $migration_id, array $options = []) {
|
||||
$log = new DrushLogMigrateMessage();
|
||||
|
||||
if (drush_get_option('execute-dependencies')) {
|
||||
if ($required_IDS = $migration->get('requirements')) {
|
||||
$manager = \Drupal::service('plugin.manager.config_entity_migration');
|
||||
$required_migrations = $manager->createInstances($required_IDS);
|
||||
$dependency_options = array_merge($options, ['is_dependency' => TRUE]);
|
||||
array_walk($required_migrations, __FUNCTION__, $dependency_options);
|
||||
}
|
||||
}
|
||||
if (!empty($options['force'])) {
|
||||
$migration->set('requirements', []);
|
||||
}
|
||||
if (!empty($options['update'])) {
|
||||
$migration->getIdMap()->prepareUpdate();
|
||||
}
|
||||
$executable = new MigrateExecutable($migration, $log, $options);
|
||||
// drush_op() provides --simulate support
|
||||
drush_op(array($executable, 'import'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $migration_names
|
||||
*/
|
||||
function drush_migrate_tools_migrate_rollback($migration_names = '') {
|
||||
$group_names = drush_get_option('group');
|
||||
$tag_names = drush_get_option('tag');
|
||||
$all = drush_get_option('all');
|
||||
$options = [];
|
||||
if (!$all && !$group_names && !$migration_names && !$tag_names) {
|
||||
drush_set_error('MIGRATE_ERROR', dt('You must specify --all, --group, --tag, or one or more migration names separated by commas'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (drush_get_option('feedback')) {
|
||||
$options['feedback'] = drush_get_option('feedback');
|
||||
}
|
||||
|
||||
$log = new DrushLogMigrateMessage();
|
||||
|
||||
$migrations = drush_migrate_tools_migration_list($migration_names);
|
||||
if (empty($migrations)) {
|
||||
drush_log(dt('No migrations found.'), 'error');
|
||||
}
|
||||
|
||||
// Take it one group at a time, rolling back the migrations within each group.
|
||||
foreach ($migrations as $group_id => $migration_list) {
|
||||
// Roll back in reverse order.
|
||||
$migration_list = array_reverse($migration_list);
|
||||
foreach ($migration_list as $migration_id => $migration) {
|
||||
$executable = new MigrateExecutable($migration, $log, $options);
|
||||
// drush_op() provides --simulate support.
|
||||
drush_op(array($executable, 'rollback'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $migration_id
|
||||
*/
|
||||
function drush_migrate_tools_migrate_stop($migration_id = '') {
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
|
||||
if ($migration) {
|
||||
$status = $migration->getStatus();
|
||||
switch ($status) {
|
||||
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');
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_log(dt('Migration @id does not exist', ['@id' => $migration_id]), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $migration_id
|
||||
*/
|
||||
function drush_migrate_tools_migrate_reset_status($migration_id = '') {
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
|
||||
if ($migration) {
|
||||
$status = $migration->getStatus();
|
||||
if ($status == MigrationInterface::STATUS_IDLE) {
|
||||
drush_log(dt('Migration @id is already Idle', ['@id' => $migration_id]), 'warning');
|
||||
}
|
||||
else {
|
||||
$migration->setStatus(MigrationInterface::STATUS_IDLE);
|
||||
drush_log(dt('Migration @id reset to Idle', ['@id' => $migration_id]), 'status');
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_log(dt('Migration @id does not exist', ['@id' => $migration_id]), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $migration_id
|
||||
*/
|
||||
function drush_migrate_tools_migrate_messages($migration_id) {
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
|
||||
if ($migration) {
|
||||
$map = $migration->getIdMap();
|
||||
$first = TRUE;
|
||||
$table = [];
|
||||
foreach ($map->getMessageIterator() as $row) {
|
||||
unset($row->msgid);
|
||||
if ($first) {
|
||||
// @todo: Ideally, replace sourceid* with source key names. Or, should
|
||||
// getMessageIterator() do that?
|
||||
foreach ($row as $column => $value) {
|
||||
$table[0][] = $column;
|
||||
}
|
||||
$first = FALSE;
|
||||
}
|
||||
$table[] = (array)$row;
|
||||
}
|
||||
if (empty($table)) {
|
||||
drush_log(dt('No messages for this migration'), 'status');
|
||||
}
|
||||
else {
|
||||
if (drush_get_option('csv')) {
|
||||
foreach ($table as $row) {
|
||||
fputcsv(STDOUT, $row);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$widths = [];
|
||||
foreach ($table[0] as $header) {
|
||||
$widths[] = strlen($header) + 1;
|
||||
}
|
||||
drush_print_table($table, TRUE, $widths);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_log(dt('Migration @id does not exist', ['@id' => $migration_id]), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $migration_id
|
||||
*/
|
||||
function drush_migrate_tools_migrate_fields_source($migration_id) {
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
|
||||
if ($migration) {
|
||||
$source = $migration->getSourcePlugin();
|
||||
$table = [];
|
||||
foreach ($source->fields() as $machine_name => $description) {
|
||||
$table[] = [strip_tags($description), $machine_name];
|
||||
}
|
||||
drush_print_table($table);
|
||||
}
|
||||
else {
|
||||
drush_log(dt('Migration @id does not exist', ['@id' => $migration_id]), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of active migrations.
|
||||
*
|
||||
* @param string $migration_ids
|
||||
* Comma-separated list of migrations - if present, return only these migrations.
|
||||
*
|
||||
* @return 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.
|
||||
*/
|
||||
function drush_migrate_tools_migration_list($migration_ids = '') {
|
||||
// Filter keys must match the migration configuration property name.
|
||||
$filter['migration_group'] = drush_get_option('group') ? explode(',', drush_get_option('group')) : [];
|
||||
$filter['migration_tags'] = drush_get_option('tag') ? explode(',', drush_get_option('tag')) : [];
|
||||
|
||||
$manager = \Drupal::service('plugin.manager.migration');
|
||||
$plugins = $manager->createInstances([]);
|
||||
$matched_migrations = [];
|
||||
|
||||
// Get the set of migrations that may be filtered.
|
||||
if (empty($migration_ids)) {
|
||||
$matched_migrations = $plugins;
|
||||
}
|
||||
else {
|
||||
// Get the requested migrations.
|
||||
$migration_ids = explode(',', Unicode::strtolower($migration_ids));
|
||||
foreach ($plugins as $id => $migration) {
|
||||
if (in_array(Unicode::strtolower($id), $migration_ids)) {
|
||||
$matched_migrations[$id] = $migration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not return any migrations which fail to meet requirements.
|
||||
/** @var \Drupal\migrate\Plugin\Migration $migration */
|
||||
foreach ($matched_migrations as $id => $migration) {
|
||||
if ($migration->getSourcePlugin() instanceof RequirementsInterface) {
|
||||
try {
|
||||
$migration->getSourcePlugin()->checkRequirements();
|
||||
}
|
||||
catch (RequirementsException $e) {
|
||||
unset($matched_migrations[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filters the matched migrations if a group or a tag has been input.
|
||||
if (!empty($filter['migration_group']) || !empty($filter['migration_tags'])) {
|
||||
// Get migrations in any of the specified groups and with any of the
|
||||
// specified tags.
|
||||
foreach ($filter as $property => $values) {
|
||||
if (!empty($values)) {
|
||||
$filtered_migrations = [];
|
||||
foreach ($values as $search_value) {
|
||||
foreach ($matched_migrations as $id => $migration) {
|
||||
// Cast to array because migration_tags can be an array.
|
||||
$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)) {
|
||||
$filtered_migrations[$id] = $migration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$matched_migrations = $filtered_migrations;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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');
|
||||
$migrations[$configured_group_id][$id] = $migration;
|
||||
}
|
||||
}
|
||||
return isset($migrations) ? $migrations : [];
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
type: module
|
||||
name: Migrate Tools
|
||||
description: 'Tools to assist in developing and running migrations.'
|
||||
package: Migration
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate (>=8.3)
|
||||
- migrate_plus:migrate_plus
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-05-10
|
||||
version: '8.x-4.0-beta1'
|
||||
core: '8.x'
|
||||
project: 'migrate_tools'
|
||||
datestamp: 1494450194
|
||||
@@ -0,0 +1,11 @@
|
||||
migrate_tools.add_group_action:
|
||||
route_name: entity.migration_group.add_form
|
||||
title: 'Add migration group'
|
||||
appears_on:
|
||||
- entity.migration_group.list
|
||||
|
||||
#migrate_tools.add_migration_action:
|
||||
# route_name: entity.migration.add_form
|
||||
# title: 'Add migration'
|
||||
# appears_on:
|
||||
# - entity.migration.list
|
||||
@@ -0,0 +1,5 @@
|
||||
migrate_tools.menu:
|
||||
title: Migrations
|
||||
parent: system.admin_structure
|
||||
description: Manage migration processes.
|
||||
route_name: entity.migration_group.list
|
||||
@@ -0,0 +1,41 @@
|
||||
# Component routing definition
|
||||
entity.migration.overview:
|
||||
route_name: entity.migration.overview
|
||||
base_route: entity.migration.overview
|
||||
title: View
|
||||
|
||||
entity.migration.overview_general:
|
||||
title: Overview
|
||||
route_name: entity.migration.overview
|
||||
parent_id: entity.migration.overview
|
||||
entity.migration.overview_source:
|
||||
title: Source
|
||||
route_name: entity.migration.source
|
||||
parent_id: entity.migration.overview
|
||||
weight: 1
|
||||
entity.migration.overview_process:
|
||||
title: Process
|
||||
route_name: entity.migration.process
|
||||
parent_id: entity.migration.overview
|
||||
weight: 2
|
||||
entity.migration.overview_destination:
|
||||
title: Destination
|
||||
route_name: entity.migration.destination
|
||||
parent_id: entity.migration.overview
|
||||
weight: 3
|
||||
|
||||
entity.migration.edit_form:
|
||||
route_name: entity.migration.edit_form
|
||||
base_route: entity.migration.overview
|
||||
title: Edit
|
||||
|
||||
migrate_tools.messages:
|
||||
route_name: migrate_tools.messages
|
||||
base_route: entity.migration.overview
|
||||
title: Messages
|
||||
|
||||
entity.migration.delete_form:
|
||||
route_name: entity.migration.delete_form
|
||||
base_route: entity.migration.overview
|
||||
title: Delete
|
||||
weight: 10
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides tools for implementing and managing migrations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_build().
|
||||
*/
|
||||
function migrate_tools_entity_type_build(array &$entity_types) {
|
||||
// Inject our UI into the general migration and migration group config entities.
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityType[] $entity_types */
|
||||
$entity_types['migration']
|
||||
->set('admin_permission', 'administer migrations')
|
||||
->setHandlerClass('list_builder', 'Drupal\migrate_tools\Controller\MigrationListBuilder')
|
||||
// ->setFormClass('add', 'Drupal\migrate_tools\Form\MigrationAddForm')
|
||||
->setFormClass('edit', 'Drupal\migrate_tools\Form\MigrationEditForm')
|
||||
->setFormClass('delete', 'Drupal\migrate_tools\Form\MigrationDeleteForm')
|
||||
// ->setLinkTemplate('edit-form', '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}')
|
||||
->setLinkTemplate('list-form', '/admin/structure/migrate/manage/{migration_group}/migrations')
|
||||
/* ->setLinkTemplate('delete-form', '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/delete')*/;
|
||||
|
||||
$entity_types['migration_group']
|
||||
->set('admin_permission', 'administer migrations')
|
||||
->setHandlerClass('list_builder', 'Drupal\migrate_tools\Controller\MigrationGroupListBuilder')
|
||||
->setFormClass('add', 'Drupal\migrate_tools\Form\MigrationGroupAddForm')
|
||||
->setFormClass('edit', 'Drupal\migrate_tools\Form\MigrationGroupEditForm')
|
||||
->setFormClass('delete', 'Drupal\migrate_tools\Form\MigrationGroupDeleteForm')
|
||||
->setLinkTemplate('edit-form', '/admin/structure/migrate/manage/{migration_group}')
|
||||
/* ->setLinkTemplate('list-form', '/admin/structure/migrate/manage/{migration_group}/migrations')*/
|
||||
->setLinkTemplate('delete-form', '/admin/structure/migrate/manage/{migration_group}/delete');
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
'administer migrations':
|
||||
title: 'Administer migrations'
|
||||
description: Create, edit, and manage migration processed.
|
||||
@@ -0,0 +1,109 @@
|
||||
# This is the router item for listing all migration group entities.
|
||||
entity.migration_group.list:
|
||||
path: '/admin/structure/migrate'
|
||||
defaults:
|
||||
_entity_list: 'migration_group'
|
||||
_title: 'Migrations'
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
|
||||
# This is the router item for adding our migration group entity.
|
||||
entity.migration_group.add_form:
|
||||
path: '/admin/structure/migrate/add'
|
||||
defaults:
|
||||
_title: 'Add migration group'
|
||||
_entity_form: migration_group.add
|
||||
requirements:
|
||||
_entity_create_access: migration_group
|
||||
|
||||
# This is the router item for editing our migration group entity.
|
||||
entity.migration_group.edit_form:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}'
|
||||
defaults:
|
||||
_title: 'Edit migration group'
|
||||
_entity_form: migration_group.edit
|
||||
requirements:
|
||||
_entity_access: migration_group.update
|
||||
|
||||
# This is the router item for deleting an instance of our migration group entity.
|
||||
entity.migration_group.delete_form:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/delete'
|
||||
defaults:
|
||||
_title: 'Delete migration group'
|
||||
_entity_form: migration_group.delete
|
||||
requirements:
|
||||
_entity_access: migration_group.delete
|
||||
|
||||
# This is the router item for listing all migration entities.
|
||||
entity.migration.list:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations'
|
||||
defaults:
|
||||
_entity_list: 'migration'
|
||||
_title: 'Migrations'
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
|
||||
# This is the router item for adding our migration entity.
|
||||
#entity.migration.add_form:
|
||||
# path: '/admin/structure/migrate/manage/{migration_group}/migrations/add'
|
||||
# defaults:
|
||||
# _title: 'Add migration'
|
||||
# _entity_form: migration.add
|
||||
# requirements:
|
||||
# _entity_create_access: migration
|
||||
|
||||
# This is the router item for viewing our migration entity.
|
||||
entity.migration.overview:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::overview'
|
||||
_title: 'Migration overview'
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
entity.migration.source:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/source'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::source'
|
||||
_title: 'Source'
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
entity.migration.process:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/process'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::process'
|
||||
_title: 'Process'
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
entity.migration.destination:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/destination'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MigrationController::destination'
|
||||
_title: 'Destination'
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
|
||||
# This is the router item for editing our migration entity.
|
||||
entity.migration.edit_form:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/edit'
|
||||
defaults:
|
||||
_title: 'Edit migration'
|
||||
_entity_form: migration.edit
|
||||
requirements:
|
||||
_entity_access: migration.update
|
||||
|
||||
# This is the router item for deleting an instance of our migration entity.
|
||||
entity.migration.delete_form:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/delete'
|
||||
defaults:
|
||||
_title: 'Delete migration'
|
||||
_entity_form: migration.delete
|
||||
requirements:
|
||||
_entity_access: migration.delete
|
||||
|
||||
migrate_tools.messages:
|
||||
path: '/admin/structure/migrate/manage/{migration_group}/migrations/{migration}/messages'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_tools\Controller\MessageController::overview'
|
||||
_title: 'Messages'
|
||||
requirements:
|
||||
_permission: 'administer migrations'
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Returns responses for migrate_tools message routes.
|
||||
*/
|
||||
class MessageController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The database service.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* Plugin manager for migration plugins.
|
||||
*
|
||||
* @var \Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager
|
||||
*/
|
||||
protected $migrationConfigEntityPluginManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('database'),
|
||||
$container->get('plugin.manager.config_entity_migration')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MessageController object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* A database connection.
|
||||
* @param \Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager $migration_config_entity_plugin_manager
|
||||
* The plugin manager for config entity-based migrations.
|
||||
*/
|
||||
public function __construct(Connection $database, MigrationConfigEntityPluginManager $migration_config_entity_plugin_manager) {
|
||||
$this->database = $database;
|
||||
$this->migrationConfigEntityPluginManager = $migration_config_entity_plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of log level classes.
|
||||
*
|
||||
* @return array
|
||||
* An array of log level classes.
|
||||
*/
|
||||
public static function getLogLevelClassMap() {
|
||||
return [
|
||||
MigrationInterface::MESSAGE_INFORMATIONAL => 'migrate-message-4',
|
||||
MigrationInterface::MESSAGE_NOTICE => 'migrate-message-3',
|
||||
MigrationInterface::MESSAGE_WARNING => 'migrate-message-2',
|
||||
MigrationInterface::MESSAGE_ERROR => 'migrate-message-1',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a listing of migration messages.
|
||||
*
|
||||
* Messages are truncated at 56 chars.
|
||||
*
|
||||
* @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 overview($migration_group, $migration) {
|
||||
$rows = [];
|
||||
$classes = static::getLogLevelClassMap();
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = $this->migrationConfigEntityPluginManager->createInstance($migration);
|
||||
$source_id_field_names = array_keys($migration->getSourcePlugin()->getIds());
|
||||
$column_number = 1;
|
||||
foreach ($source_id_field_names as $source_id_field_name) {
|
||||
$header[] = [
|
||||
'data' => $source_id_field_name,
|
||||
'field' => 'sourceid' . $column_number++,
|
||||
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
|
||||
];
|
||||
}
|
||||
$header[] = [
|
||||
'data' => $this->t('Severity level'),
|
||||
'field' => 'level',
|
||||
'class' => [RESPONSIVE_PRIORITY_LOW],
|
||||
];
|
||||
$header[] = [
|
||||
'data' => $this->t('Message'),
|
||||
'field' => 'message',
|
||||
];
|
||||
|
||||
$message_table = $migration->getIdMap()->messageTableName();
|
||||
$map_table = $migration->getIdMap()->mapTableName();
|
||||
$query = $this->database->select($message_table, 'msg')
|
||||
->extend('\Drupal\Core\Database\Query\PagerSelectExtender')
|
||||
->extend('\Drupal\Core\Database\Query\TableSortExtender');
|
||||
$query->innerJoin($map_table, 'map', 'msg.source_ids_hash=map.source_ids_hash');
|
||||
$query->fields('msg');
|
||||
$query->fields('map');
|
||||
$result = $query
|
||||
->limit(50)
|
||||
->orderByHeader($header)
|
||||
->execute();
|
||||
|
||||
foreach ($result as $message_row) {
|
||||
$column_number = 1;
|
||||
foreach ($source_id_field_names as $source_id_field_name) {
|
||||
$column_name = 'sourceid' . $column_number++;
|
||||
$row[$column_name] = $message_row->$column_name;
|
||||
}
|
||||
$row['level'] = $message_row->level;
|
||||
$row['message'] = $message_row->message;
|
||||
$row['class'] = [Html::getClass('migrate-message-' . $message_row->level), $classes[$message_row->level]];
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$build['message_table'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#attributes' => ['id' => $message_table, 'class' => [$message_table]],
|
||||
'#empty' => $this->t('No messages for this migration.'),
|
||||
];
|
||||
$build['message_pager'] = ['#type' => 'pager'];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Returns responses for migrate_tools migration view routes.
|
||||
*/
|
||||
class MigrationController extends ControllerBase implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* Plugin manager for migration plugins.
|
||||
*
|
||||
* @var \Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager
|
||||
*/
|
||||
protected $migrationConfigEntityPluginManager;
|
||||
|
||||
/**
|
||||
* Constructs a new MigrationController object.
|
||||
*
|
||||
* @param \Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager $migration_config_entity_plugin_manager
|
||||
* The plugin manager for config entity-based migrations.
|
||||
*/
|
||||
public function __construct(MigrationConfigEntityPluginManager $migration_config_entity_plugin_manager) {
|
||||
$this->migrationConfigEntityPluginManager = $migration_config_entity_plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.config_entity_migration')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an overview of a migration entity.
|
||||
*
|
||||
* @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 overview($migration_group, $migration) {
|
||||
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = $this->migrationConfigEntityPluginManager->createInstance($migration);
|
||||
|
||||
$build['overview'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Overview'),
|
||||
];
|
||||
|
||||
$build['overview']['group'] = [
|
||||
'#title' => $this->t('Group:'),
|
||||
'#markup' => Xss::filterAdmin($migration_group),
|
||||
'#type' => 'item',
|
||||
];
|
||||
|
||||
$build['overview']['description'] = [
|
||||
'#title' => $this->t('Description:'),
|
||||
'#markup' => Xss::filterAdmin($migration->label()),
|
||||
'#type' => 'item',
|
||||
];
|
||||
|
||||
$migration_dependencies = $migration->getMigrationDependencies();
|
||||
if (!empty($migration_dependencies['required'])) {
|
||||
$build['overview']['dependencies'] = [
|
||||
'#title' => $this->t('Migration Dependencies') ,
|
||||
'#markup' => Xss::filterAdmin(implode(', ', $migration_dependencies['required'])),
|
||||
'#type' => 'item',
|
||||
];
|
||||
}
|
||||
if (!empty($migration_dependencies['optional'])) {
|
||||
$build['overview']['soft_dependencies'] = [
|
||||
'#title' => $this->t('Soft Migration Dependencies'),
|
||||
'#markup' => Xss::filterAdmin(implode(', ', $migration_dependencies['optional'])),
|
||||
'#type' => 'item',
|
||||
];
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display source information of a migration entity.
|
||||
*
|
||||
* @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 source($migration_group, $migration) {
|
||||
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = $this->migrationConfigEntityPluginManager->createInstance($migration);
|
||||
|
||||
// Source field information.
|
||||
$build['source'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Source'),
|
||||
'#group' => 'detail',
|
||||
'#description' => $this->t('<p>These are the fields available from the source of this migration task. The machine names listed here may be used as sources in the process pipeline.</p>'),
|
||||
'#attributes' => [
|
||||
'id' => 'migration-detail-source',
|
||||
],
|
||||
];
|
||||
|
||||
$source = $migration->getSourcePlugin();
|
||||
$build['source']['query'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Query'),
|
||||
'#markup' => '<pre>' . Xss::filterAdmin($source) . '</pre>',
|
||||
];
|
||||
$header = [$this->t('Machine name'), $this->t('Description')];
|
||||
$rows = [];
|
||||
foreach ($source->fields($migration) as $machine_name => $description) {
|
||||
$rows[] = [
|
||||
['data' => Html::escape($machine_name)],
|
||||
['data' => Xss::filterAdmin($description)],
|
||||
];
|
||||
}
|
||||
|
||||
$build['source']['fields'] = [
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No fields'),
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display process information of a migration entity.
|
||||
*
|
||||
* @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 process($migration_group, $migration) {
|
||||
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = $this->migrationConfigEntityPluginManager->createInstance($migration);
|
||||
|
||||
// Process information.
|
||||
$build['process'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Process'),
|
||||
];
|
||||
|
||||
$header = [
|
||||
$this->t('Destination'),
|
||||
$this->t('Source'),
|
||||
$this->t('Process plugin'),
|
||||
$this->t('Default'),
|
||||
];
|
||||
$rows = [];
|
||||
foreach ($migration->getProcess() as $destination_id => $process_line) {
|
||||
$row = [];
|
||||
$row[] = ['data' => Html::escape($destination_id)];
|
||||
if (isset($process_line[0]['source'])) {
|
||||
$row[] = ['data' => Xss::filterAdmin($process_line[0]['source'])];
|
||||
}
|
||||
else {
|
||||
$row[] = '';
|
||||
}
|
||||
if (isset($process_line[0]['plugin'])) {
|
||||
$row[] = ['data' => Xss::filterAdmin($process_line[0]['plugin'])];
|
||||
}
|
||||
else {
|
||||
$row[] = '';
|
||||
}
|
||||
if (isset($process_line[0]['default_value'])) {
|
||||
$row[] = ['data' => Xss::filterAdmin($process_line[0]['default_value'])];
|
||||
}
|
||||
else {
|
||||
$row[] = '';
|
||||
}
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$build['process']['fields'] = [
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No process defined.'),
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays destination information of a migration entity.
|
||||
*
|
||||
* @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 destination($migration_group, $migration) {
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = $this->migrationConfigEntityPluginManager->createInstance($migration);
|
||||
|
||||
// Destination field information.
|
||||
$build['destination'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Destination'),
|
||||
'#group' => 'detail',
|
||||
'#description' => $this->t('<p>These are the fields available in the destination plugin of this migration task. The machine names are those available to be used as the keys in the process pipeline.</p>'),
|
||||
'#attributes' => [
|
||||
'id' => 'migration-detail-destination',
|
||||
],
|
||||
];
|
||||
$destination = $migration->getDestinationPlugin();
|
||||
$build['destination']['type'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Type'),
|
||||
'#markup' => Xss::filterAdmin($destination->getPluginId()),
|
||||
];
|
||||
$header = [$this->t('Machine name'), $this->t('Description')];
|
||||
$rows = [];
|
||||
$destination_fields = $destination->fields() ?: [];
|
||||
foreach ($destination_fields as $machine_name => $description) {
|
||||
$rows[] = [
|
||||
['data' => Html::escape($machine_name)],
|
||||
['data' => Xss::filterAdmin($description)],
|
||||
];
|
||||
}
|
||||
|
||||
$build['destination']['fields'] = [
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No fields'),
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Controller;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides a listing of migration group entities.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Controller
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationGroupListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* Builds the header row for the entity listing.
|
||||
*
|
||||
* @return array
|
||||
* A render array structure of header strings.
|
||||
*
|
||||
* @see Drupal\Core\Entity\EntityListController::render()
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = $this->t('Migration Group');
|
||||
$header['machine_name'] = $this->t('Machine Name');
|
||||
$header['description'] = $this->t('Description');
|
||||
$header['source_type'] = $this->t('Source Type');
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a row for an entity in the entity listing.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* The entity for which to build the row.
|
||||
*
|
||||
* @return array
|
||||
* A render array of the table row for displaying the entity.
|
||||
*
|
||||
* @see Drupal\Core\Entity\EntityListController::render()
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['label'] = $entity->label();
|
||||
$row['machine_name'] = $entity->id();
|
||||
$row['description'] = $entity->get('description');
|
||||
$row['source_type'] = $entity->get('source_type');
|
||||
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
$operations['list'] = [
|
||||
'title' => $this->t('List migrations'),
|
||||
'weight' => 0,
|
||||
'url' => Url::fromRoute('entity.migration.list', ['migration_group' => $entity->id()]),
|
||||
];
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Controller;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Routing\CurrentRouteMatch;
|
||||
use Drupal\migrate_plus\Entity\MigrationGroup;
|
||||
use Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
|
||||
/**
|
||||
* Provides a listing of migration entities in a given group.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Controller
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationListBuilder extends ConfigEntityListBuilder implements EntityHandlerInterface {
|
||||
|
||||
/**
|
||||
* Default object for current_route_match service.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\CurrentRouteMatch
|
||||
*/
|
||||
protected $currentRouteMatch;
|
||||
|
||||
/**
|
||||
* Plugin manager for migration plugins.
|
||||
*
|
||||
* @var \Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager
|
||||
*/
|
||||
protected $migrationConfigEntityPluginManager;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Core\Routing\CurrentRouteMatch $current_route_match
|
||||
* The current route match service.
|
||||
* @param \Drupal\migrate_plus\Plugin\MigrationConfigEntityPluginManager $migration_config_entity_plugin_manager
|
||||
* The plugin manager for config entity-based migrations.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, CurrentRouteMatch $current_route_match, MigrationConfigEntityPluginManager $migration_config_entity_plugin_manager) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
$this->currentRouteMatch = $current_route_match;
|
||||
$this->migrationConfigEntityPluginManager = $migration_config_entity_plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager')->getStorage($entity_type->id()),
|
||||
$container->get('current_route_match'),
|
||||
$container->get('plugin.manager.config_entity_migration')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the migrations belonging to the appropriate group.
|
||||
*
|
||||
* @return array
|
||||
* An array of entity IDs.
|
||||
*/
|
||||
protected function getEntityIds() {
|
||||
$migration_group = $this->currentRouteMatch->getParameter('migration_group');
|
||||
|
||||
$query = $this->getStorage()->getQuery()
|
||||
->sort($this->entityType->getKey('id'));
|
||||
|
||||
$migration_groups = MigrationGroup::loadMultiple();
|
||||
|
||||
if (array_key_exists($migration_group, $migration_groups)) {
|
||||
$query->condition('migration_group', $migration_group);
|
||||
}
|
||||
else {
|
||||
$query->notExists('migration_group');
|
||||
}
|
||||
// Only add the pager if a limit is specified.
|
||||
if ($this->limit) {
|
||||
$query->pager($this->limit);
|
||||
}
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the header row for the entity listing.
|
||||
*
|
||||
* @return array
|
||||
* A render array structure of header strings.
|
||||
*
|
||||
* @see Drupal\Core\Entity\EntityListController::render()
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = $this->t('Migration');
|
||||
$header['machine_name'] = $this->t('Machine Name');
|
||||
$header['status'] = $this->t('Status');
|
||||
$header['total'] = $this->t('Total');
|
||||
$header['imported'] = $this->t('Imported');
|
||||
$header['unprocessed'] = $this->t('Unprocessed');
|
||||
$header['messages'] = $this->t('Messages');
|
||||
$header['last_imported'] = $this->t('Last Imported');
|
||||
return $header; // + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a row for a migration plugin.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $migration_entity
|
||||
* The migration plugin for which to build the row.
|
||||
*
|
||||
* @return array
|
||||
* A render array of the table row for displaying the plugin information.
|
||||
*
|
||||
* @see Drupal\Core\Entity\EntityListController::render()
|
||||
*/
|
||||
public function buildRow(EntityInterface $migration_entity) {
|
||||
$migration = $this->migrationConfigEntityPluginManager->createInstance($migration_entity->id());
|
||||
$migration_group = $migration->get('migration_group');
|
||||
if (!$migration_group) {
|
||||
$migration_group = 'default';
|
||||
}
|
||||
$route_parameters = array(
|
||||
'migration_group' => $migration_group,
|
||||
'migration' => $migration->id(),
|
||||
);
|
||||
$row['label'] = array(
|
||||
'data' => array(
|
||||
'#type' => 'link',
|
||||
'#title' => $migration->label(),
|
||||
'#url' => Url::fromRoute("entity.migration.overview", $route_parameters),
|
||||
),
|
||||
);
|
||||
$row['machine_name'] = $migration->id();
|
||||
$row['status'] = $migration->getStatusLabel();
|
||||
|
||||
// Derive the stats.
|
||||
$source_plugin = $migration->getSourcePlugin();
|
||||
$row['total'] = $source_plugin->count();
|
||||
$map = $migration->getIdMap();
|
||||
$row['imported'] = $map->importedCount();
|
||||
// -1 indicates uncountable sources.
|
||||
if ($row['total'] == -1) {
|
||||
$row['total'] = $this->t('N/A');
|
||||
$row['unprocessed'] = $this->t('N/A');
|
||||
}
|
||||
else {
|
||||
$row['unprocessed'] = $row['total'] - $map->processedCount();
|
||||
}
|
||||
$row['messages'] = array(
|
||||
'data' => array(
|
||||
'#type' => 'link',
|
||||
'#title' => $map->messageCount(),
|
||||
'#url' => Url::fromRoute("migrate_tools.messages", $route_parameters),
|
||||
),
|
||||
);
|
||||
$migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
|
||||
$last_imported = $migrate_last_imported_store->get($migration->id(), FALSE);
|
||||
if ($last_imported) {
|
||||
/** @var DateFormatter $date_formatter */
|
||||
$date_formatter = \Drupal::service('date.formatter');
|
||||
$row['last_imported'] = $date_formatter->format($last_imported / 1000,
|
||||
'custom', 'Y-m-d H:i:s');
|
||||
}
|
||||
else {
|
||||
$row['last_imported'] = '';
|
||||
}
|
||||
return $row; // + parent::buildRow($migration_entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
$migration_group = $entity->get('migration_group');
|
||||
if (!$migration_group) {
|
||||
$migration_group = 'default';
|
||||
}
|
||||
// $this->addGroupParameter($operations['edit']['url'], $migration_group);
|
||||
// $this->addGroupParameter($operations['delete']['url'], $migration_group);
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The URL associated with an operation.
|
||||
*
|
||||
* @param $migration_group
|
||||
* The migration's parent group.
|
||||
*/
|
||||
protected function addGroupParameter(Url $url, $migration_group) {
|
||||
if (!$migration_group) {
|
||||
$migration_group = 'default';
|
||||
}
|
||||
$route_parameters = $url->getRouteParameters() + ['migration_group' => $migration_group];
|
||||
$url->setRouteParameters($route_parameters);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools;
|
||||
|
||||
use Drupal\migrate\MigrateMessageInterface;
|
||||
|
||||
class DrushLogMigrateMessage implements MigrateMessageInterface {
|
||||
|
||||
/**
|
||||
* Output a message from the migration.
|
||||
*
|
||||
* @param string $message
|
||||
* The message to display.
|
||||
* @param string $type
|
||||
* The type of message to display.
|
||||
*
|
||||
* @see drush_log()
|
||||
*/
|
||||
public function display($message, $type = 'status') {
|
||||
drush_log($message, $type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Class MigrationAddForm.
|
||||
*
|
||||
* Provides the add form for our migration entity.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Form
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationAddForm extends MigrationFormBase {
|
||||
|
||||
/**
|
||||
* Returns the actions provided by this form.
|
||||
*
|
||||
* For our add form, we only need to change the text of the submit button.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* An array of supported actions for the current entity form.
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
// $actions['submit']['#value'] = $this->t('Create Migration');
|
||||
unset($actions['submit']);
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Class MigrationDeleteForm.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Form
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationDeleteForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* Gathers a confirmation question.
|
||||
*
|
||||
* @return string
|
||||
* Translated string.
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete migration %label?', array(
|
||||
'%label' => $this->entity->label(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather the confirmation text.
|
||||
*
|
||||
* @return string
|
||||
* Translated string.
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete Migration');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cancel URL.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* The URL to go to if the user cancels the deletion.
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.migration_group.list');
|
||||
}
|
||||
|
||||
/**
|
||||
* The submit handler for the confirm form.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Delete the entity.
|
||||
$this->entity->delete();
|
||||
|
||||
// Set a message that the entity was deleted.
|
||||
drupal_set_message(t('Migration %label was deleted.', array(
|
||||
'%label' => $this->entity->label(),
|
||||
)));
|
||||
|
||||
// Redirect the user to the list controller when complete.
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Class MigrationEditForm
|
||||
*
|
||||
* Provides the edit form for our Migration entity.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Form
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationEditForm extends MigrationFormBase {
|
||||
|
||||
/**
|
||||
* Returns the actions provided by this form.
|
||||
*
|
||||
* For the edit form, we only need to change the text of the submit button.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* An array of supported actions for the current entity form.
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = t('Update Migration');
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The URL associated with an operation.
|
||||
*
|
||||
* @param $migration_group
|
||||
* The migration's parent group.
|
||||
*/
|
||||
protected function addGroupParameter(Url $url, $migration_group) {
|
||||
$route_parameters = $url->getRouteParameters() + array('migration_group' => $migration_group);
|
||||
$url->setRouteParameters($route_parameters);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\migrate_plus\Entity\MigrationGroup;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Class MigrationFormBase.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Form
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationFormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $entityQueryFactory;
|
||||
|
||||
/**
|
||||
* Construct the MigrationGroupFormBase.
|
||||
*
|
||||
* For simple entity forms, there's no need for a constructor. Our migration form
|
||||
* base, however, requires an entity query factory to be injected into it
|
||||
* from the container. We later use this query factory to build an entity
|
||||
* query for the exists() method.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
|
||||
* An entity query factory for the migration group entity type.
|
||||
*/
|
||||
public function __construct(QueryFactory $query_factory) {
|
||||
$this->entityQueryFactory = $query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for MigrationFormBase.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* A container interface service.
|
||||
*
|
||||
* @return \Drupal\migrate_tools\Form\MigrationGroupFormBase
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('entity.query'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\EntityFormController::form().
|
||||
*
|
||||
* Builds the entity add/edit form.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* An associative array containing the migration add/edit form.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
// Get anything we need from the base class.
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
/** @var MigrationInterface $migration */
|
||||
$migration = $this->entity;
|
||||
|
||||
$form['warning'] = [
|
||||
'#markup' => $this->t('Creating migrations is not yet supported. See <a href=":url">:url</a>', [
|
||||
':url' => 'https://www.drupal.org/node/2573241',
|
||||
])
|
||||
];
|
||||
|
||||
// Build the form.
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $migration->label(),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#title' => $this->t('Machine name'),
|
||||
'#default_value' => $migration->id(),
|
||||
'#machine_name' => array(
|
||||
'exists' => array($this, 'exists'),
|
||||
'replace_pattern' => '([^a-z0-9_]+)|(^custom$)',
|
||||
'error' => 'The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".',
|
||||
),
|
||||
'#disabled' => !$migration->isNew(),
|
||||
);
|
||||
|
||||
$groups = MigrationGroup::loadMultiple();
|
||||
$group_options = [];
|
||||
foreach ($groups as $group) {
|
||||
$group_options[$group->id()] = $group->label();
|
||||
}
|
||||
if (!$migration->get('migration_group') && isset($group_options['default'])) {
|
||||
$migration->set('migration_group', 'default');
|
||||
}
|
||||
|
||||
$form['migration_group'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Migration Group'),
|
||||
'#empty_value' => '',
|
||||
'#default_value' => $migration->get('migration_group'),
|
||||
'#options' => $group_options,
|
||||
'#description' => $this->t('Assign this migration to an existing group.'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for an existing migration group.
|
||||
*
|
||||
* @param string|int $entity_id
|
||||
* The entity ID.
|
||||
* @param array $element
|
||||
* The form element.
|
||||
* @param FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if this format already exists, FALSE otherwise.
|
||||
*/
|
||||
public function exists($entity_id, array $element, FormStateInterface $form_state) {
|
||||
// Use the query factory to build a new migration entity query.
|
||||
$query = $this->entityQueryFactory->get('migration');
|
||||
|
||||
// Query the entity ID to see if its in use.
|
||||
$result = $query->condition('id', $element['#field_prefix'] . $entity_id)
|
||||
->execute();
|
||||
|
||||
// We don't need to return the ID, only if it exists or not.
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\EntityFormController::actions().
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* 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.
|
||||
$actions = parent::actions($form, $form_state);
|
||||
|
||||
// Change the submit button text.
|
||||
$actions['submit']['#value'] = $this->t('Save');
|
||||
|
||||
// Return the result.
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\EntityFormController::save().
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$migration = $this->getEntity();
|
||||
$status = $migration->save();
|
||||
|
||||
if ($status == SAVED_UPDATED) {
|
||||
// If we edited an existing entity...
|
||||
drupal_set_message($this->t('Migration %label has been updated.', array('%label' => $migration->label())));
|
||||
}
|
||||
else {
|
||||
// If we created a new entity...
|
||||
drupal_set_message($this->t('Migration %label has been added.', array('%label' => $migration->label())));
|
||||
}
|
||||
|
||||
// Redirect the user back to the listing route after the save operation.
|
||||
$form_state->setRedirect('entity.migration.list',
|
||||
array('migration_group' => $migration->get('migration_group')));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Class MigrationGroupAddForm.
|
||||
*
|
||||
* Provides the add form for our migration_group entity.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Form
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationGroupAddForm extends MigrationGroupFormBase {
|
||||
|
||||
/**
|
||||
* Returns the actions provided by this form.
|
||||
*
|
||||
* For our add form, we only need to change the text of the submit button.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* An array of supported actions for the current entity form.
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Create Migration Group');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Class MigrationGroupDeleteForm.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Form
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationGroupDeleteForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* Gathers a confirmation question.
|
||||
*
|
||||
* @return string
|
||||
* Translated string.
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete migration group %label?', array(
|
||||
'%label' => $this->entity->label(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather the confirmation text.
|
||||
*
|
||||
* @return string
|
||||
* Translated string.
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete Migration Group');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cancel URL.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* The URL to go to if the user cancels the deletion.
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.migration_group.list');
|
||||
}
|
||||
|
||||
/**
|
||||
* The submit handler for the confirm form.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Delete the entity.
|
||||
$this->entity->delete();
|
||||
|
||||
// Set a message that the entity was deleted.
|
||||
drupal_set_message(t('Migration group %label was deleted.', array(
|
||||
'%label' => $this->entity->label(),
|
||||
)));
|
||||
|
||||
// Redirect the user to the list controller when complete.
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Class MigrationGroupEditForm
|
||||
*
|
||||
* Provides the edit form for our Migration Group entity.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Form
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationGroupEditForm extends MigrationGroupFormBase {
|
||||
|
||||
/**
|
||||
* Returns the actions provided by this form.
|
||||
*
|
||||
* For the edit form, we only need to change the text of the submit button.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* An array of supported actions for the current entity form.
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = t('Update Migration Group');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\migrate_plus\Entity\MigrationGroupInterface;
|
||||
|
||||
/**
|
||||
* Class MigrationGroupFormBase.
|
||||
*
|
||||
* @package Drupal\migrate_tools\Form
|
||||
*
|
||||
* @ingroup migrate_tools
|
||||
*/
|
||||
class MigrationGroupFormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $entityQueryFactory;
|
||||
|
||||
/**
|
||||
* Construct the MigrationGroupFormBase.
|
||||
*
|
||||
* For simple entity forms, there's no need for a constructor. Our migration group form
|
||||
* base, however, requires an entity query factory to be injected into it
|
||||
* from the container. We later use this query factory to build an entity
|
||||
* query for the exists() method.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
|
||||
* An entity query factory for the migration group entity type.
|
||||
*/
|
||||
public function __construct(QueryFactory $query_factory) {
|
||||
$this->entityQueryFactory = $query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for MigrationGroupFormBase.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* A container interface service.
|
||||
*
|
||||
* @return \Drupal\migrate_tools\Form\MigrationFormBase
|
||||
*
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('entity.query'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\EntityFormController::form().
|
||||
*
|
||||
* Builds the entity add/edit form.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* An associative array containing the migration group add/edit form.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
// Get anything we need from the base class.
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
/** @var MigrationGroupInterface $migration_group */
|
||||
$migration_group = $this->entity;
|
||||
|
||||
// Build the form.
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $migration_group->label(),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#title' => $this->t('Machine name'),
|
||||
'#default_value' => $migration_group->id(),
|
||||
'#machine_name' => array(
|
||||
'exists' => array($this, 'exists'),
|
||||
'replace_pattern' => '([^a-z0-9_]+)|(^custom$)',
|
||||
'error' => 'The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".',
|
||||
),
|
||||
'#disabled' => !$migration_group->isNew(),
|
||||
);
|
||||
$form['description'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Description'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $migration_group->get('description'),
|
||||
);
|
||||
$form['source_type'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Source type'),
|
||||
'#description' => $this->t('Type of source system the group is migrating from, for example "Drupal 6" or "WordPress 4".'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $migration_group->get('source_type'),
|
||||
);
|
||||
|
||||
// Return the form.
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for an existing migration group.
|
||||
*
|
||||
* @param string|int $entity_id
|
||||
* The entity ID.
|
||||
* @param array $element
|
||||
* The form element.
|
||||
* @param FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if this format already exists, FALSE otherwise.
|
||||
*/
|
||||
public function exists($entity_id, array $element, FormStateInterface $form_state) {
|
||||
// Use the query factory to build a new migration group entity query.
|
||||
$query = $this->entityQueryFactory->get('migration_group');
|
||||
|
||||
// Query the entity ID to see if its in use.
|
||||
$result = $query->condition('id', $element['#field_prefix'] . $entity_id)
|
||||
->execute();
|
||||
|
||||
// We don't need to return the ID, only if it exists or not.
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\EntityFormController::actions().
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* 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.
|
||||
$actions = parent::actions($form, $form_state);
|
||||
|
||||
// Change the submit button text.
|
||||
$actions['submit']['#value'] = $this->t('Save');
|
||||
|
||||
// Return the result.
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\EntityFormController::save().
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* An associative array containing the current state of the form.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$migration_group = $this->getEntity();
|
||||
$status = $migration_group->save();
|
||||
|
||||
if ($status == SAVED_UPDATED) {
|
||||
// If we edited an existing entity...
|
||||
drupal_set_message($this->t('Migration group %label has been updated.', array('%label' => $migration_group->label())));
|
||||
}
|
||||
else {
|
||||
// If we created a new entity...
|
||||
drupal_set_message($this->t('Migration group %label has been added.', array('%label' => $migration_group->label())));
|
||||
}
|
||||
|
||||
// Redirect the user back to the listing route after the save operation.
|
||||
$form_state->setRedirect('entity.migration_group.list');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_tools;
|
||||
|
||||
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_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;
|
||||
|
||||
class MigrateExecutable extends MigrateExecutableBase {
|
||||
|
||||
/**
|
||||
* Counters of map statuses.
|
||||
*
|
||||
* @var array
|
||||
* Set of counters, keyed by MigrateIdMapInterface::STATUS_* constant.
|
||||
*/
|
||||
protected $saveCounters = array(
|
||||
MigrateIdMapInterface::STATUS_FAILED => 0,
|
||||
MigrateIdMapInterface::STATUS_IGNORED => 0,
|
||||
MigrateIdMapInterface::STATUS_IMPORTED => 0,
|
||||
MigrateIdMapInterface::STATUS_NEEDS_UPDATE => 0,
|
||||
);
|
||||
|
||||
/**
|
||||
* Counter of map deletions.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $deleteCounter = 0;
|
||||
|
||||
/**
|
||||
* Maximum number of items to process in this migration. 0 indicates no limit
|
||||
* is to be applied.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $itemLimit = 0;
|
||||
|
||||
/**
|
||||
* Frequency (in items) at which progress messages should be emitted.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $feedback = 0;
|
||||
|
||||
/**
|
||||
* List of specific source IDs to import.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $idlist = [];
|
||||
|
||||
/**
|
||||
* Count of number of items processed so far in this migration.
|
||||
* @var int
|
||||
*/
|
||||
protected $counter = 0;
|
||||
|
||||
/**
|
||||
* Whether the destination item exists before saving.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $preExistingItem = FALSE;
|
||||
|
||||
/**
|
||||
* List of event listeners we have registered.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listeners = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(MigrationInterface $migration, MigrateMessageInterface $message, array $options = []) {
|
||||
parent::__construct($migration, $message);
|
||||
if (isset($options['limit'])) {
|
||||
$this->itemLimit = $options['limit'];
|
||||
}
|
||||
if (isset($options['feedback'])) {
|
||||
$this->feedback = $options['feedback'];
|
||||
}
|
||||
if (isset($options['idlist'])) {
|
||||
$this->idlist = explode(',', $options['idlist']);
|
||||
array_walk($this->idlist , function(&$value, $key) {
|
||||
$value = explode(':', $value);
|
||||
});
|
||||
}
|
||||
|
||||
$this->listeners[MigrateEvents::MAP_SAVE] = [$this, 'onMapSave'];
|
||||
$this->listeners[MigrateEvents::MAP_DELETE] = [$this, 'onMapDelete'];
|
||||
$this->listeners[MigrateEvents::POST_IMPORT] = [$this, 'onPostImport'];
|
||||
$this->listeners[MigrateEvents::POST_ROLLBACK] = [$this, 'onPostRollback'];
|
||||
$this->listeners[MigrateEvents::PRE_ROW_SAVE] = [$this, 'onPreRowSave'];
|
||||
$this->listeners[MigrateEvents::POST_ROW_DELETE] = [$this, 'onPostRowDelete'];
|
||||
$this->listeners[MigratePlusEvents::PREPARE_ROW] = [$this, 'onPrepareRow'];
|
||||
foreach ($this->listeners as $event => $listener) {
|
||||
\Drupal::service('event_dispatcher')->addListener($event, $listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count up any map save events.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateMapSaveEvent $event
|
||||
* The map event.
|
||||
*/
|
||||
public function onMapSave(MigrateMapSaveEvent $event) {
|
||||
// Only count saves for this migration.
|
||||
if ($event->getMap()->getQualifiedMapTableName() == $this->migration->getIdMap()->getQualifiedMapTableName()) {
|
||||
$fields = $event->getFields();
|
||||
// Distinguish between creation and update.
|
||||
if ($fields['source_row_status'] == MigrateIdMapInterface::STATUS_IMPORTED &&
|
||||
$this->preExistingItem
|
||||
) {
|
||||
$this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE]++;
|
||||
}
|
||||
else {
|
||||
$this->saveCounters[$fields['source_row_status']]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count up any rollback events.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateMapDeleteEvent $event
|
||||
* The map event.
|
||||
*/
|
||||
public function onMapDelete(MigrateMapDeleteEvent $event) {
|
||||
$this->deleteCounter++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of items created.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCreatedCount() {
|
||||
return $this->saveCounters[MigrateIdMapInterface::STATUS_IMPORTED];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of items updated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUpdatedCount() {
|
||||
return $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of items ignored.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getIgnoredCount() {
|
||||
return $this->saveCounters[MigrateIdMapInterface::STATUS_IGNORED];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of items that failed.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFailedCount() {
|
||||
return $this->saveCounters[MigrateIdMapInterface::STATUS_FAILED];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of items processed. Note that STATUS_NEEDS_UPDATE
|
||||
* is not counted, since this is typically set on stubs created as side
|
||||
* effects, not on the primary item being imported.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getProcessedCount() {
|
||||
return $this->saveCounters[MigrateIdMapInterface::STATUS_IMPORTED] +
|
||||
$this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE] +
|
||||
$this->saveCounters[MigrateIdMapInterface::STATUS_IGNORED] +
|
||||
$this->saveCounters[MigrateIdMapInterface::STATUS_FAILED];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of items rolled back.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRollbackCount() {
|
||||
return $this->deleteCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the per-status counters to 0.
|
||||
*/
|
||||
protected function resetCounters() {
|
||||
foreach ($this->saveCounters as $status => $count) {
|
||||
$this->saveCounters[$status] = 0;
|
||||
}
|
||||
$this->deleteCounter = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* React to migration completion.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateImportEvent $event
|
||||
* The map event.
|
||||
*/
|
||||
public function onPostImport(MigrateImportEvent $event) {
|
||||
$migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
|
||||
$migrate_last_imported_store->set($event->getMigration()->id(), round(microtime(TRUE) * 1000));
|
||||
$this->progressMessage();
|
||||
$this->removeListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up all our event listeners.
|
||||
*/
|
||||
protected function removeListeners() {
|
||||
foreach ($this->listeners as $event => $listener) {
|
||||
\Drupal::service('event_dispatcher')->removeListener($event, $listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit information on what we've done since the last feedback (or the
|
||||
* beginning of this migration).
|
||||
*
|
||||
* @param bool $done
|
||||
*/
|
||||
protected function progressMessage($done = TRUE) {
|
||||
$processed = $this->getProcessedCount();
|
||||
if ($done) {
|
||||
$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'";
|
||||
}
|
||||
else {
|
||||
$singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
|
||||
$plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
|
||||
}
|
||||
$this->message->display(\Drupal::translation()->formatPlural($processed,
|
||||
$singular_message, $plural_message,
|
||||
array('@numitems' => $processed,
|
||||
'@created' => $this->getCreatedCount(),
|
||||
'@updated' => $this->getUpdatedCount(),
|
||||
'@failures' => $this->getFailedCount(),
|
||||
'@ignored' => $this->getIgnoredCount(),
|
||||
'@name' => $this->migration->id())));
|
||||
}
|
||||
|
||||
/**
|
||||
* React to rollback completion.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateRollbackEvent $event
|
||||
* The map event.
|
||||
*/
|
||||
public function onPostRollback(MigrateRollbackEvent $event) {
|
||||
$this->rollbackMessage();
|
||||
$this->removeListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit information on what we've done since the last feedback (or the
|
||||
* beginning of this migration).
|
||||
*
|
||||
* @param bool $done
|
||||
*/
|
||||
protected function rollbackMessage($done = TRUE) {
|
||||
$rolled_back = $this->getRollbackCount();
|
||||
if ($done) {
|
||||
$singular_message = "Rolled back 1 item - done with '@name'";
|
||||
$plural_message = "Rolled back @numitems items - done with '@name'";
|
||||
}
|
||||
else {
|
||||
$singular_message = "Rolled back 1 item - continuing with '@name'";
|
||||
$plural_message = "Rolled back @numitems items - continuing with '@name'";
|
||||
}
|
||||
$this->message->display(\Drupal::translation()->formatPlural($rolled_back,
|
||||
$singular_message, $plural_message,
|
||||
array('@numitems' => $rolled_back,
|
||||
'@name' => $this->migration->id())));
|
||||
}
|
||||
|
||||
/**
|
||||
* React to an item about to be imported.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event
|
||||
* The pre-save event.
|
||||
*/
|
||||
public function onPreRowSave(MigratePreRowSaveEvent $event) {
|
||||
$id_map = $event->getRow()->getIdMap();
|
||||
if (!empty($id_map['destid1'])) {
|
||||
$this->preExistingItem = TRUE;
|
||||
}
|
||||
else {
|
||||
$this->preExistingItem = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React to item rollback.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateRowDeleteEvent $event
|
||||
* The post-save event.
|
||||
*/
|
||||
public function onPostRowDelete(MigrateRowDeleteEvent $event) {
|
||||
if ($this->feedback && ($this->deleteCounter) && $this->deleteCounter % $this->feedback == 0) {
|
||||
$this->rollbackMessage(FALSE);
|
||||
$this->resetCounters();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React to a new row.
|
||||
*
|
||||
* @param \Drupal\migrate_plus\Event\MigratePrepareRowEvent $event
|
||||
* The prepare-row event.
|
||||
*
|
||||
* @throws \Drupal\migrate\MigrateSkipRowException
|
||||
*
|
||||
*/
|
||||
public function onPrepareRow(MigratePrepareRowEvent $event) {
|
||||
if (!empty($this->idlist)) {
|
||||
$row = $event->getRow();
|
||||
/**
|
||||
* @TODO replace for $source_id = $row->getSourceIdValues(); when https://www.drupal.org/node/2698023 is fixed
|
||||
*/
|
||||
$migration = $event->getMigration();
|
||||
$source_id = array_merge(array_flip(array_keys($migration->getSourcePlugin()
|
||||
->getIds())), $row->getSourceIdValues());
|
||||
$skip = TRUE;
|
||||
foreach ($this->idlist as $item) {
|
||||
if (array_values($source_id) == $item) {
|
||||
$skip = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($skip) {
|
||||
throw new MigrateSkipRowException(NULL, FALSE);
|
||||
}
|
||||
}
|
||||
if ($this->feedback && ($this->counter) && $this->counter % $this->feedback == 0) {
|
||||
$this->progressMessage(FALSE);
|
||||
$this->resetCounters();
|
||||
}
|
||||
$this->counter++;
|
||||
if ($this->itemLimit && ($this->getProcessedCount() + 1) >= $this->itemLimit) {
|
||||
$event->getMigration()->interruptMigration(MigrationInterface::RESULT_COMPLETED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user