diff --git a/sites/all/modules/contrib/migrate/migrate_plus/LICENSE.txt b/sites/all/modules/contrib/migrate/migrate_plus/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/LICENSE.txt @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/sites/all/modules/contrib/migrate/migrate_plus/README.txt b/sites/all/modules/contrib/migrate/migrate_plus/README.txt new file mode 100644 index 000000000..797082e38 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/README.txt @@ -0,0 +1,77 @@ +The migrate_plus module extends the core migration system with API enhancements +and additional functionality, as well as providing practical examples. + +Extensions to base API +====================== +* A Migration configuration entity is provided, enabling persistance of dynamic + migration configuration. +* A MigrationGroup configuration entity is provided, which enables migrations to + be organized in groups, and to maintain shared configuration in one place. +* A MigrateEvents::PREPARE_ROW event is provided to dispatch hook_prepare_row() + invocations as events. +* A SourcePluginExtension class is provided, enabling one to define fields and + IDs for a source plugin via configuration rather than requiring PHP code. + +Plugin types +============ +migrate_plus provides the following plugin types, for use with the url source +plugin. + +* A data_parser type, for parsing different formats on behalf of the url source + plugin. +* A data_fetcher type, for fetching data to feed into a data_parser plugin. +* An authentication type, for adding authentication headers with the http + data_fetcher plugin. + +Plugins +======= + +Process +------- +* The entity_lookup process plugin allows you to populate references to entities + which already exist in Drupal, whether they were migrated or not. +* The entity_generate process plugin extends entity_lookup to also create the + desired entity when it doesn't already exist. +* The file_blob process plugin supports creating file entities from blob data. +* The merge process plugin allows the merging of multiple arrays into a single + field. +* The skip_on_value process plugin allows you to skip a row, or a given field, + for specific source values. + +Source +------ +* A url source plugin is provided, implementing a common structure for + file-based data providers. + +Data parsers +------------ +* The xml parser plugin uses PHP's XMLReader interface to incrementally parse + XML files. This should be used for XML sources which are potentially very + large. +* The simple_xml parser plugin uses PHP's SimpleXML interface to fully parse + XML files. This should be used for XML sources where you need to be able to + use complex xpaths for your item selectors, or have to access elements outside + of the current item element via xpaths. +* The json parser plugin supports JSON sources. +* The soap parser plugin supports SOAP sources. + +Data fetchers +------------- +* The file fetcher plugin works for most URLs regardless of protocol, as well as + local filesystems. +* The http fetcher plugin provides the ability to add headers to an HTTP + request (particularly through authentication plugins). + +Authentication +-------------- +* The basic authentication plugin provides HTTP Basic authentication. +* The digest authentication plugin provides HTTP Digest authentication. +* The oauth2 authentication plugin provides OAuth2 authentication over HTTP. + +Examples +======== +* The migrate_example submodule provides a fully functional and runnable +example migration scenario demonstrating the basic concepts and most common +techniques for SQL-based migrations. +* The migrate_example_advanced submodule provides examples of migration from +different kinds of sources, as well as less common techniques. diff --git a/sites/all/modules/contrib/migrate/migrate_plus/composer.json b/sites/all/modules/contrib/migrate/migrate_plus/composer.json new file mode 100644 index 000000000..b1dbd8570 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/composer.json @@ -0,0 +1,24 @@ +{ + "name": "drupal/migrate_plus", + "description": "Enhancements to core migration support.", + "type": "drupal-module", + "license": "GPL-2.0+", + "homepage": "https://www.drupal.org/project/migrate_plus", + "authors": [ + { + "name": "Mike Ryan", + "homepage":"https://www.drupal.org/u/mikeryan", + "role": "Maintainer" + } + ], + "support": { + "issues": "https://www.drupal.org/project/issues/migrate_plus", + "irc": "irc://irc.freenode.org/drupal-migrate", + "source": "https://cgit.drupalcode.org/migrate_plus" + }, + "minimum-stability": "dev", + "require": {}, + "suggest": { + "sainsburys/guzzle-oauth2-plugin": "3.0 required for the OAuth2 authentication plugin" + } +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.data_types.schema.yml b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.data_types.schema.yml new file mode 100644 index 000000000..27c8006f4 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.data_types.schema.yml @@ -0,0 +1,40 @@ +# Basic data types for Migrate. + +migrate_plugin: + type: mapping + mapping: + plugin: + type: string + label: 'Plugin' + +migrate_destination: + type: migrate_plugin + label: 'Destination' + mapping: + overwrite_properties: + type: sequence + label: 'Properties to overwrite' + sequence: + type: string + label: 'Property' + +migrate_source: + type: migrate_plugin + label: 'Source' + mapping: + constants: + type: ignore + label: 'Constants' + +migrate_process: + type: migrate_plugin + label: 'Process' + +# Base schema for migrate source plugins that extend +# \Drupal\migrate\Plugin\migrate\source\SqlBase. +migrate_source_sql: + type: migrate_source + mapping: + target: + type: string + label: 'The migration database target' diff --git a/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.destination.schema.yml b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.destination.schema.yml new file mode 100644 index 000000000..58ad74e53 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.destination.schema.yml @@ -0,0 +1,18 @@ +# Schema for the migrate destination plugins. + +migrate_plus.destination.*: + type: migrate_destination + label: 'Default destination' + mapping: + no_stub: + type: boolean + label: 'Whether stubbing is allowed.' + default: false + +migrate_plus.destination.config: + type: migrate_destination + label: 'Config' + mapping: + config_name: + type: string + label: 'Configuration name' diff --git a/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.process.schema.yml b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.process.schema.yml new file mode 100644 index 000000000..b1f123bc3 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.process.schema.yml @@ -0,0 +1,145 @@ +# Schema for the migrate process plugins. + +migrate_plus.process.*: + type: migrate_process + label: 'Default process' + +migrate_plus.process.callback: + type: migrate_process + label: 'Callback process' + mapping: + callback: + type: string + label: 'Callback' + +migrate_plus.process.concat: + type: migrate_process + label: 'Concat process' + mapping: + delimiter: + type: string + label: 'Delimiter' + +migrate_plus.process.dedupe_entity: + type: migrate_process + label: 'Dedupe Entity process' + mapping: + entity_type: + type: string + label: 'Entity type' + field: + type: string + label: 'Field name' + postfix: + type: string + label: 'Postfix' + start: + type: integer + label: 'Start' + length: + type: integer + label: 'Length' + +migrate_plus.process.explode: + type: migrate_process + label: 'Explode process' + mapping: + delimiter: + type: string + label: 'Delimiter' + limit: + type: integer + label: 'Limit' + +migrate_plus.process.extract: + type: migrate_process + label: 'Extract process' + mapping: + default: + type: string + label: 'Default value' + +migrate_plus.process.flatten: + type: migrate_process + label: 'Flatten process' + +migrate_plus.process.get: + type: migrate_process + label: 'Get process' + mapping: + source: + type: string + label: 'Source key' + +migrate_plus.process.iterator: + type: migrate_process + label: 'Iterator process' + mapping: + process: + type: ignore + label: 'Process' + key: + type: string + label: 'Key' + +migrate_plus.process.machine_name: + type: migrate_process + label: 'Machine name process' + +migrate_plus.process.migration: + type: migrate_process + label: 'Migration process' + mapping: + migration: + type: sequence + label: 'Migration' + source: + type: sequence + label: 'Source keys' + source_ids: + type: string + label: 'Source IDs' + stub_id: + type: string + label: 'Stub ID' + +migrate_plus.process.route: + type: migrate_process + label: 'Route process' + +migrate_plus.process.skip_on_empty: + type: migrate_process + label: 'Skip on Empty' + +migrate_plus.process.skip_row_if_not_set: + type: migrate_process + label: 'Skip Row process if not set' + mapping: + index: + type: integer + label: 'Index' + +migrate_plus.process.static_map: + type: migrate_process + label: 'Static Map' + mapping: + map: + type: sequence + label: 'Map' + default_value: + type: string + label: 'Default value' + bypass: + type: boolean + label: 'Bypass lookup' + +migrate_plus.process.default_value: + type: migrate_process + label: 'Default value' + mapping: + strict: + type: boolean + label: 'Strict type check' + default_value: + type: string + label: 'Default value' diff --git a/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.schema.yml b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.schema.yml new file mode 100644 index 000000000..99aef5523 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.schema.yml @@ -0,0 +1,78 @@ +# Schema for the configuration files of the Migrate Plus module. + +migrate_plus.migration.*: + type: config_entity + label: 'Migration' + mapping: + id: + type: string + label: 'ID' + class: + type: string + label: 'Class' + field_plugin_method: + type: string + label: 'Field Plugin Method' + cck_plugin_method: + type: string + label: 'BC layer for Field Plugin Method' + migration_tags: + type: sequence + label: 'Migration Tags' + sequence: + type: string + label: 'Tag' + migration_group: + type: string + label: 'Group' + label: + type: label + label: 'Label' + source: + type: migrate_plus.source.[plugin] + label: 'Source' + process: + type: ignore + label: 'Process' + destination: + type: migrate_plus.destination.[plugin] + label: 'Destination' + migration_dependencies: + type: mapping + label: 'Dependencies' + mapping: + required: + type: sequence + label: 'Required dependencies' + sequence: + type: string + label: 'Dependency' + optional: + type: sequence + label: 'Optional dependencies' + sequence: + type: string + label: 'Dependency' + +migrate_plus.migration_group.*: + type: config_entity + label: 'Migration Group' + mapping: + id: + type: string + label: 'ID' + label: + type: label + label: 'Label' + description: + type: string + label: 'Description' + source_type: + type: string + label: 'Source type' + module: + type: string + label: 'Dependent module' + shared_configuration: + type: ignore + label: 'Shared migration configuration' diff --git a/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.source.schema.yml b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.source.schema.yml new file mode 100644 index 000000000..4117ea410 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/config/schema/migrate_plus.source.schema.yml @@ -0,0 +1,34 @@ +# Schema for the migrate source plugins. + +migrate_plus.source.*: + type: migrate_source + label: 'Default source' + +migrate_plus.source.empty: + type: migrate_source_sql + label: 'Empty source' + mapping: + provider: + type: string + label: 'Provider' + +migrate_plus.source.embedded_data: + type: migrate_source + label: 'Embedded data source' + mapping: + data_rows: + type: sequence + label: 'Data rows' + sequence: + type: ignore + label: 'Data row' + ids: + type: sequence + label: 'Unique key' + sequence: + type: mapping + label: 'Key column' + mapping: + type: + type: string + label: 'Column type' diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/README.txt b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/README.txt new file mode 100644 index 000000000..32d77da99 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/README.txt @@ -0,0 +1,111 @@ +INTRODUCTION +------------ +The migrate_example module demonstrates how to implement custom migrations +for Drupal 8. It includes a group of "beer" migrations demonstrating a complete +simple migration scenario. + +THE BEER SITE +------------- +In this scenario, we have a beer aficionado site which stores its data in MySQL +tables - there are content items for each beer on the site, user accounts with +profile data, categories to classify the beers, and user-generated comments on +the beers. We want to convert this site to Drupal with just a few modifications +to the basic structure. + +To make the example as simple as to run as possible, the source data is placed +in tables directly in your Drupal database - in most real-world scenarios, your +source data will be in an external database. The migrate_example_setup submodule +creates and populates these tables, as well as configuring your Drupal 8 site +(creating a node type, vocabulary, fields, etc.) to receive the data. + +STRUCTURE +--------- +There are two primary components to this example: + +1. Migration configuration, in the migrations and config/install directories. + These YAML files describe the migration process and provide the mappings from + the source data to Drupal's destination entities. The difference between the + two possible directories: + + a. Files in the migrations directory provide configuration directly for the + migration plugins. The filenames are of the form .yml. This + approach is recommended when your migration configuration is fully hardcoded + and does not need to be overridden (e.g., you don't need to change the URL to + a source web service through an admin UI). While developing migrations, + changes to these files require at most a 'drush cr' to load your changes. + + b. Files in the config/install directory provide migration configuration as + configuration entities, and have names of the form + migrate_plus.migration..yml ("migration" because they define + entities of the "migration" type, and "migrate_plus" because that is the + module which implements the "migration" type). Migrations defined in this way + may have their configuration modified (in particular, through a web UI) by + loading the configuration entity, modifying its configuration, and saving the + entity. When developing, to get edits to the .yml files in config/install to + take effect in active configuration, use the config_devel module. + + Configuration in either type of file is identical - the only differences are + the directories and filenames. + +2. Source plugins, in src/Plugin/migrate/source. These are referenced from the + configuration files, and provide the source data to the migration processing + pipeline, as well as manipulating that data where necessary to put it into + a canonical form for migrations. + +UNDERSTANDING THE MIGRATIONS +---------------------------- +The YAML and PHP files are copiously documented in-line. To best understand +the concepts described in a more-or-less narrative form, it is recommended you +read the files in the following order: + +1. migrate_plus.migration_group.beer.yml +2. migrate_plus.migration.beer_term.yml +3. BeerTerm.php +4. migrate_plus.migration.beer_user.yml +5. BeerUser.php +6. migrate_plus.migration.beer_node.yml +7. BeerNode.php +8. migrate_plus.migration.beer_comment.yml +9. BeerComment.php + +RUNNING THE MIGRATIONS +---------------------- +The migrate_tools module (https://www.drupal.org/project/migrate_tools) provides +the tools you need to perform migration processes. At this time, the web UI only +provides status information - to perform migration operations, you need to use +the drush commands. + +# Enable the tools and the example module if you haven't already. +drush en -y migrate_tools,migrate_example + +# Look at the migrations. Just look at them. Notice that they are displayed in +# the order they will be run, which reflects their dependencies. For example, +# because the node migration references the imported terms and users, it must +# run after those migrations have been run. +drush ms # Abbreviation for migrate-status + +# Run the import operation for all the beer migrations. +drush mi --group=beer # Abbreviation for migrate-import + +# Look at what you've done! Also, visit the site and see the imported content, +# user accounts, etc. +drush ms + +# Look at the duplicate username message. +drush mmsg beer_user # Abbreviation for migrate-messages + +# Run the rollback operation for all the migrations (removing all the imported +# content, user accounts, etc.). Note that it will rollback the migrations in +# the opposite order as they were imported. +drush mr --group=beer # Abbreviation for migrate-rollback + +# You can import specific migrations. +drush mi beer_term,beer_user +# At this point, go look at your content listing - you'll see beer nodes named +# "Stub", generated from the user's favbeers references. + +drush mi beer_node,beer_comment +# Refresh your content listing - the stub nodes have been filled with real beer! + +# You can rollback specific migrations. +drush mr beer_comment,beer_node diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_node.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_node.yml new file mode 100644 index 000000000..e2a9aefcb --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_node.yml @@ -0,0 +1,53 @@ +# Migration configuration for beer content. +id: beer_node +label: Beers of the world +migration_group: beer +source: + plugin: beer_node +destination: + plugin: entity:node +process: + # Hardcode the destination node type (bundle) as 'migrate_example_beer'. + type: + plugin: default_value + default_value: migrate_example_beer + title: name + nid: bid + uid: + plugin: migration + migration: beer_user + source: aid + sticky: + plugin: default_value + default_value: 0 + field_migrate_example_country: countries + field_migrate_example_beer_style: + plugin: migration + migration: beer_term + source: terms + # Some Drupal fields may have multiple components we may want to set + # separately. For example, text fields may have summaries (teasers) in + # addition to the full text value. We use / to separate the field name from + # the internal field value being set, and put it in quotes because / is a + # YAML special character. + 'body/value': body + 'body/summary': excerpt +# Our beer nodes have references to terms and users, so we want those to be +# imported first. We make that dependency explicit here - by putting those +# migrations under the 'required' key, we ensure that the tools will prevent +# us from running the beer_node migration unless the beer_term and beer_user +# migrations are complete (although we can override the dependency check by +# passing --force to the drush migrate-import command). We can also add +# 'optional' dependencies - these affect the order in which migrations are +# displayed, and run by default, but does not force you run them in that +# order. +# The general rule of thumb is that any migrations referenced by migration +# process plugins should be required here. +migration_dependencies: + required: + - beer_term + - beer_user +dependencies: + enforced: + module: + - migrate_example diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_term.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_term.yml new file mode 100644 index 000000000..a7ca1858c --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_term.yml @@ -0,0 +1,87 @@ +# A "migration" is, in technical terms, a plugin whose configuration describes +# how to read source data, process it (generally by mapping source fields to +# destination fields), and write it to Drupal. + +# The machine name for a migration, used to uniquely identify it. +id: beer_term + +# A human-friendly description of the migration. +label: Migrate style categories from the source database to taxonomy terms + +# The machine name of the group containing this migration (which contains shared +# configuration to be merged with our own configuration here). +migration_group: beer + +# Every migration must have a source plugin, which controls the delivery of our +# source data. In this case, our source plugin has the name "beer_term", which +# Drupal resolves to the PHP class defined in +# src/Plugin/migrate/source/BeerTerm.php. +source: + plugin: beer_term + +# Every migration must also have a destination plugin, which handles writing +# the migrated data in the appropriate form for that particular kind of data. +# Most Drupal content is an "entity" of one type or another, and we need to +# specify what entity type we are populating (in this case, taxonomy terms). +# Unlike the source plugin (which is specific to our particular scenario), this +# destination plugin is implemented in Drupal itself. +destination: + plugin: entity:taxonomy_term + +# Here's the meat of the migration - the processing pipeline. This describes how +# each destination field is to be populated based on the source data. For each +# destination field, one or more process plugins may be invoked. +process: + # The simplest process plugin is named 'get' - it is the default plugin, so + # does not need to be explicitly named. It simply copies the source value + # (the 'style' field from the source database in this case) to the destination + # field (the taxonomy term 'name' field). You can see we simply copy the + # source 'details' field to destination 'description' field in the same way. + name: style + description: details + + # Here is a new plugin - default_value. In its simplest usage here, it is used + # to hard-code a destination value, the vid (vocabulary ID) our taxonomy terms + # should be assigned to. It's important to note that while above the right + # side of the mappings was a source field name, here the right side of the + # 'default_value:' line is an actual value. + vid: + plugin: default_value + default_value: migrate_example_beer_styles + + # Here's another new plugin - migration. When importing data from another + # system, typically the unique identifiers for items on the destination side + # are not the same as the identifiers were on the source side. For example, in + # our style data the term names are the unique identifiers for each term, + # while in Drupal each term is assigned a unique integer term ID (tid). When + # any such items are referenced in Drupal, the reference needs to be + # translated from the old ID ('ale') to the new ID (1). The migration + # framework keeps track of the relationships between source and destination + # IDs in map tables, and the migration plugin is the means of performing a + # lookup in those map tables during processing. + parent: + plugin: migration + # Here we reference the migration whose map table we're performing a lookup + # against. You'll note that in this case we're actually referencing this + # migration itself, since category parents are imported by the same + # migration. This works best when we're sure the parents are imported + # before the children, and in this case our source plugin is guaranteeing + # that. + migration: beer_term + # 'style_parent' is the parent reference field from the source data. The + # result of this plugin is that the destination 'parent' field is populated + # with the Drupal term ID of the referenced style (or NULL if style_parent + # was empty). + source: style_parent + +# We'll learn more about dependencies in beer_node - here, we leave them empty. +migration_dependencies: {} + +# By default, configuration entities (like this migration) are not automatically +# removed when the migration which installed them is uninstalled. To have your +# migrations uninstalled with your migration module, add an enforced dependency +# on your module. +dependencies: + enforced: + module: + - migrate_example diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_user.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_user.yml new file mode 100644 index 000000000..66b08682b --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_user.yml @@ -0,0 +1,105 @@ +# Migration configuration for user accounts. We've described most of what goes +# into migration configuration in migrate_plus.migration.beer_term.yml, so won't +# repeat that here. +id: beer_user +label: Beer Drinkers of the world +migration_group: beer +source: + plugin: beer_user +destination: + plugin: entity:user +process: + pass: password + mail: email + init: email + status: status + roles: + plugin: default_value + default_value: 2 + + # Here's a new process plugin - dedupe_entity. Our source site allowed there + # to be multiple user accounts with the same username, but Drupal wants + # usernames to be unique. This plugin allows us to automatically generate + # unique usernames when we detect collisions. + name: + plugin: dedupe_entity + # The name of the source field containing the username. + source: username + # These next two settings identify the destination-side field to check for + # duplicates. They say "see if the incoming 'name' matches any existing + # 'name' field in any 'user' entity". + entity_type: user + field: name + # Finally, this specifies a string to use between the original value and the + # sequence number appended to make the value unique. Thus, the first 'alice' + # account gets the name 'alice' in Drupal, and the second one gets the name + # 'alice_1'. + postfix: _ + + # Another new process plugin - callback. This allows us to filter an incoming + # source value through an arbitrary PHP function. The function called must + # have one required argument. + created: + plugin: callback + # The 'registered' timestamp in the source data is a string of the form + # 'yyyy-mm-dd hh:mm:ss', but Drupal wants a UNIX timestamp for 'created'. + source: registered + callable: strtotime + + # Our source data only has a single timestamp value, 'registered', which we + # want to use for all four of Drupal's user timestamp fields. We could + # duplicate the callback plugin we used for 'created' above - but we have a + # shortcut. Putting an @ sign at the beginning of the source value indicates + # that it is to be interpreted as a *destination* field name instead of a + # *source* field name. Thus, if a value we need in more than one place + # requires some processing beyond simply copying it directly, we can perform + # that processing a single time and use the result in multiple places. + changed: '@created' + access: '@created' + login: '@created' + + # Yet another new process plugin - static_map. We're making a transformation + # in how we represent gender data - formerly it was integer values 0 for male + # and 1 for female, but in our modern Drupal site we will be making this a + # free-form text field, so we want to replace the obscure integers with + # simple strings. + field_migrate_example_gender: + plugin: static_map + # Specify the source field we're reading (containing 0's and 1's). + source: sex + # Tell it to transform 0 to 'Male', and 1 to 'Female'. + map: + 0: Male + 1: Female + # If the input is missing, leave the field empty. Without this, an empty + # or invalid source value would cause the user record to be skipped + # entirely. + bypass: true + + # This looks like a simple migration process plugin, but there's magic + # happening here. We import nodes after terms and users, because they have + # references to terms and users, so of course the terms and users must be + # migrated first - right? However, the favbeers field is a reference to the + # beer nodes which haven't yet been migrated - we have a circular relationship + # between users and nodes. The way the migration system resolves this + # situation is by creating "stubs". In this case, because no beer nodes have + # been created, each time a beer is looked up against the beer_node migration + # nothing is found, and by default the migration process plugin creates an + # empty stub node as a placeholder so the favbeers reference field has + # something to point to. The stub is recorded in the beer_node map table, so + # when that migration runs it knows that each incoming beer should overwrite + # its stub instead of creating a new node. + field_migrate_example_favbeers: + plugin: migration + source: beers + migration: beer_node + +migration_dependencies: {} + +# When a module is creating a custom content type it needs to add an +# enforced dependency to itself, otherwise the content type will persist +# after the module is disabled. See: https://www.drupal.org/node/2629516. +dependencies: + enforced: + module: + - migrate_example diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration_group.beer.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration_group.beer.yml new file mode 100644 index 000000000..7d802d3a9 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration_group.beer.yml @@ -0,0 +1,38 @@ +# A "migration group" is - surprise! - a group of migrations. It is used to +# group migrations for display by our tools, and to perform operations on a +# specific set of migrations. It can also be used to hold any configuration +# common to those migrations, so it doesn't have to be duplicated in each one. + +# The machine name of the group, by which it is referenced in individual +# migrations. +id: beer + +# A human-friendly label for the group. +label: Beer Imports + +# More information about the group. +description: A few simple beer-related imports, to demonstrate how to implement migrations. + +# Short description of the type of source, e.g. "Drupal 6" or "WordPress". +source_type: Custom tables + +# Here we add any default configuration settings to be shared among all +# migrations in the group. For this example, the source tables are in the +# Drupal (default) database, but usually if your source data is in a +# database it will be external. +shared_configuration: + # Specifying 'source' here means that this configuration will be merged into + # the 'source' configuration of each migration. + source: + # A better practice for real-world migrations would be to add a database + # connection to your external database in settings.php and reference its + # key here. + key: default + +# As with the migration configuration (see beer_term), we add an enforced +# dependency so the migration_group configuration will be removed on module +# uninstall. +dependencies: + enforced: + module: + - migrate_example diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example.info.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example.info.yml new file mode 100644 index 000000000..678daccfd --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example.info.yml @@ -0,0 +1,15 @@ +type: module +name: Migrate Example +description: 'Examples of how Drupal 8 migration compares to previous versions.' +package: Examples +# core: 8.x +dependencies: + - drupal:migrate + - migrate_plus:migrate_example_setup + - 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_plus' +datestamp: 1494450189 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/comment.type.node_comments.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/comment.type.node_comments.yml new file mode 100644 index 000000000..07629a81a --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/comment.type.node_comments.yml @@ -0,0 +1,7 @@ +langcode: en +status: true +dependencies: { } +id: node_comments +label: 'Node comments' +target_entity_type_id: node +description: '' diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_form_display.comment.node_comments.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_form_display.comment.node_comments.default.yml new file mode 100644 index 000000000..fc6a78e41 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_form_display.comment.node_comments.default.yml @@ -0,0 +1,30 @@ +langcode: en +status: true +dependencies: + config: + - comment.type.node_comments + - field.field.comment.node_comments.comment_body + module: + - text +id: comment.node_comments.default +targetEntityType: comment +bundle: node_comments +mode: default +content: + author: + weight: -2 + comment_body: + type: text_textarea + weight: 11 + settings: + rows: 5 + placeholder: '' + third_party_settings: { } + subject: + type: string_textfield + weight: 10 + settings: + size: 60 + placeholder: '' + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_form_display.node.migrate_example_beer.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_form_display.node.migrate_example_beer.default.yml new file mode 100644 index 000000000..306af0e3f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_form_display.node.migrate_example_beer.default.yml @@ -0,0 +1,87 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.migrate_example_beer.body + - field.field.node.migrate_example_beer.field_comments + - field.field.node.migrate_example_beer.field_migrate_example_beer_style + - field.field.node.migrate_example_beer.field_migrate_example_country + - field.field.node.migrate_example_beer.field_migrate_example_image + - node.type.migrate_example_beer + module: + - comment + - image + - text +id: node.migrate_example_beer.default +targetEntityType: node +bundle: migrate_example_beer +mode: default +content: + body: + type: text_textarea_with_summary + weight: 6 + settings: + rows: 9 + summary_rows: 3 + placeholder: '' + third_party_settings: { } + created: + type: datetime_timestamp + weight: 2 + settings: { } + third_party_settings: { } + field_comments: + weight: 10 + settings: { } + third_party_settings: { } + type: comment_default + field_migrate_example_beer_style: + weight: 7 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + third_party_settings: { } + type: entity_reference_autocomplete + field_migrate_example_country: + weight: 8 + settings: + size: 60 + placeholder: '' + third_party_settings: { } + type: string_textfield + field_migrate_example_image: + weight: 9 + settings: + progress_indicator: throbber + preview_image_style: thumbnail + third_party_settings: { } + type: image_image + promote: + type: boolean_checkbox + settings: + display_label: true + weight: 3 + third_party_settings: { } + sticky: + type: boolean_checkbox + settings: + display_label: true + weight: 4 + third_party_settings: { } + title: + type: string_textfield + weight: 0 + settings: + size: 60 + placeholder: '' + third_party_settings: { } + uid: + type: entity_reference_autocomplete + weight: 1 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.comment.node_comments.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.comment.node_comments.default.yml new file mode 100644 index 000000000..38e2b4339 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.comment.node_comments.default.yml @@ -0,0 +1,22 @@ +langcode: en +status: true +dependencies: + config: + - comment.type.node_comments + - field.field.comment.node_comments.comment_body + module: + - text +id: comment.node_comments.default +targetEntityType: comment +bundle: node_comments +mode: default +content: + comment_body: + label: hidden + type: text_default + weight: 0 + settings: { } + third_party_settings: { } + links: + weight: 100 +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.node.migrate_example_beer.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.node.migrate_example_beer.default.yml new file mode 100644 index 000000000..3a751080d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.node.migrate_example_beer.default.yml @@ -0,0 +1,60 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.migrate_example_beer.body + - field.field.node.migrate_example_beer.field_comments + - field.field.node.migrate_example_beer.field_migrate_example_beer_style + - field.field.node.migrate_example_beer.field_migrate_example_country + - field.field.node.migrate_example_beer.field_migrate_example_image + - node.type.migrate_example_beer + module: + - comment + - image + - text + - user +id: node.migrate_example_beer.default +targetEntityType: node +bundle: migrate_example_beer +mode: default +content: + body: + label: hidden + type: text_default + weight: 2 + settings: { } + third_party_settings: { } + field_comments: + weight: 5 + label: above + settings: + pager_id: 0 + third_party_settings: { } + type: comment_default + field_migrate_example_beer_style: + weight: 3 + label: above + settings: + link: true + third_party_settings: { } + type: entity_reference_label + field_migrate_example_country: + weight: 4 + label: above + settings: + link_to_entity: false + third_party_settings: { } + type: string + field_migrate_example_image: + weight: 1 + label: above + settings: + image_style: '' + image_link: '' + third_party_settings: { } + type: image + links: + weight: 0 + settings: { } + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.node.migrate_example_beer.teaser.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.node.migrate_example_beer.teaser.yml new file mode 100644 index 000000000..dfc388bd2 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/core.entity_view_display.node.migrate_example_beer.teaser.yml @@ -0,0 +1,35 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + - field.field.node.migrate_example_beer.body + - field.field.node.migrate_example_beer.field_comments + - field.field.node.migrate_example_beer.field_migrate_example_beer_style + - field.field.node.migrate_example_beer.field_migrate_example_country + - field.field.node.migrate_example_beer.field_migrate_example_image + - node.type.migrate_example_beer + module: + - text + - user +id: node.migrate_example_beer.teaser +targetEntityType: node +bundle: migrate_example_beer +mode: teaser +content: + body: + label: hidden + type: text_summary_or_trimmed + weight: 1 + settings: + trim_length: 600 + third_party_settings: { } + links: + weight: 0 + settings: { } + third_party_settings: { } +hidden: + field_comments: true + field_migrate_example_beer_style: true + field_migrate_example_country: true + field_migrate_example_image: true diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.comment.node_comments.comment_body.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.comment.node_comments.comment_body.yml new file mode 100644 index 000000000..844c7965a --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.comment.node_comments.comment_body.yml @@ -0,0 +1,20 @@ +langcode: en +status: true +dependencies: + config: + - comment.type.node_comments + - field.storage.comment.comment_body + module: + - text +id: comment.node_comments.comment_body +field_name: comment_body +entity_type: comment +bundle: node_comments +label: Comment +description: '' +required: true +translatable: true +default_value: { } +default_value_callback: '' +settings: { } +field_type: text_long diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.body.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.body.yml new file mode 100644 index 000000000..6e54441f0 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.body.yml @@ -0,0 +1,21 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.body + - node.type.migrate_example_beer + module: + - text +id: node.migrate_example_beer.body +field_name: body +entity_type: node +bundle: migrate_example_beer +label: Body +description: '' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: + display_summary: true +field_type: text_with_summary diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_comments.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_comments.yml new file mode 100644 index 000000000..04a9900a1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_comments.yml @@ -0,0 +1,32 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_comments + - node.type.migrate_example_beer + module: + - comment +id: node.migrate_example_beer.field_comments +field_name: field_comments +entity_type: node +bundle: migrate_example_beer +label: Comments +description: '' +required: false +translatable: false +default_value: + - + status: 2 + cid: 0 + last_comment_timestamp: 0 + last_comment_name: null + last_comment_uid: 0 + comment_count: 0 +default_value_callback: '' +settings: + default_mode: 1 + per_page: 50 + anonymous: 0 + form_location: true + preview: 1 +field_type: comment diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_beer_style.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_beer_style.yml new file mode 100644 index 000000000..a5cb27ae6 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_beer_style.yml @@ -0,0 +1,26 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_beer_style + - node.type.migrate_example_beer + - taxonomy.vocabulary.migrate_example_beer_styles +id: node.migrate_example_beer.field_migrate_example_beer_style +field_name: field_migrate_example_beer_style +entity_type: node +bundle: migrate_example_beer +label: 'Migrate Example Beer Styles' +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + handler: 'default:taxonomy_term' + handler_settings: + target_bundles: + migrate_example_beer_styles: migrate_example_beer_styles + sort: + field: _none + auto_create: false +field_type: entity_reference diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_country.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_country.yml new file mode 100644 index 000000000..4a0a525a7 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_country.yml @@ -0,0 +1,18 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_country + - node.type.migrate_example_beer +id: node.migrate_example_beer.field_migrate_example_country +field_name: field_migrate_example_country +entity_type: node +bundle: migrate_example_beer +label: Countries +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: { } +field_type: string diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_image.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_image.yml new file mode 100644 index 000000000..5337e4e96 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.node.migrate_example_beer.field_migrate_example_image.yml @@ -0,0 +1,37 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_image + - node.type.migrate_example_beer + module: + - image +id: node.migrate_example_beer.field_migrate_example_image +field_name: field_migrate_example_image +entity_type: node +bundle: migrate_example_beer +label: Image +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + file_directory: '' + file_extensions: 'png gif jpg jpeg' + max_filesize: '' + max_resolution: '' + min_resolution: '' + alt_field: true + alt_field_required: false + title_field: false + title_field_required: false + default_image: + uuid: '' + alt: '' + title: '' + width: null + height: null + handler: 'default:file' + handler_settings: { } +field_type: image diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.user.user.field_migrate_example_favbeers.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.user.user.field_migrate_example_favbeers.yml new file mode 100644 index 000000000..b85f3b62f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.user.user.field_migrate_example_favbeers.yml @@ -0,0 +1,27 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.user.field_migrate_example_favbeers + - node.type.migrate_example_beer + module: + - user +id: user.user.field_migrate_example_favbeers +field_name: field_migrate_example_favbeers +entity_type: user +bundle: user +label: 'Favorite Beers' +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + handler: 'default:node' + handler_settings: + target_bundles: + migrate_example_beer: migrate_example_beer + sort: + field: title + direction: ASC +field_type: entity_reference diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.user.user.field_migrate_example_gender.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.user.user.field_migrate_example_gender.yml new file mode 100644 index 000000000..c3dff123d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.field.user.user.field_migrate_example_gender.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.user.field_migrate_example_gender + module: + - user +id: user.user.field_migrate_example_gender +field_name: field_migrate_example_gender +entity_type: user +bundle: user +label: Gender +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: { } +field_type: string diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_comments.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_comments.yml new file mode 100644 index 000000000..fd01476fe --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_comments.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - comment + - node +id: node.field_comments +field_name: field_comments +entity_type: node +type: comment +settings: + comment_type: node_comments +module: comment +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_beer_style.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_beer_style.yml new file mode 100644 index 000000000..9a037d7d6 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_beer_style.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - node + - taxonomy +id: node.field_migrate_example_beer_style +field_name: field_migrate_example_beer_style +entity_type: node +type: entity_reference +settings: + target_type: taxonomy_term +module: core +locked: false +cardinality: -1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_country.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_country.yml new file mode 100644 index 000000000..7f2ca7603 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_country.yml @@ -0,0 +1,20 @@ +langcode: en +status: true +dependencies: + module: + - node +id: node.field_migrate_example_country +field_name: field_migrate_example_country +entity_type: node +type: string +settings: + max_length: 255 + is_ascii: false + case_sensitive: false +module: core +locked: false +cardinality: -1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_image.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_image.yml new file mode 100644 index 000000000..db02f1626 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.node.field_migrate_example_image.yml @@ -0,0 +1,29 @@ +langcode: en +status: true +dependencies: + module: + - file + - image + - node +id: node.field_migrate_example_image +field_name: field_migrate_example_image +entity_type: node +type: image +settings: + uri_scheme: public + default_image: + uuid: '' + alt: '' + title: '' + width: null + height: null + target_type: file + display_field: false + display_default: false +module: image +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.user.field_migrate_example_favbeers.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.user.field_migrate_example_favbeers.yml new file mode 100644 index 000000000..54bbe12c5 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.user.field_migrate_example_favbeers.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - node + - user +id: user.field_migrate_example_favbeers +field_name: field_migrate_example_favbeers +entity_type: user +type: entity_reference +settings: + target_type: node +module: core +locked: false +cardinality: -1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.user.field_migrate_example_gender.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.user.field_migrate_example_gender.yml new file mode 100644 index 000000000..34fcc9032 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/field.storage.user.field_migrate_example_gender.yml @@ -0,0 +1,20 @@ +langcode: en +status: true +dependencies: + module: + - user +id: user.field_migrate_example_gender +field_name: field_migrate_example_gender +entity_type: user +type: string +settings: + max_length: 255 + is_ascii: false + case_sensitive: false +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/node.type.migrate_example_beer.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/node.type.migrate_example_beer.yml new file mode 100644 index 000000000..24c2e1da5 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/node.type.migrate_example_beer.yml @@ -0,0 +1,9 @@ +langcode: en +status: true +name: Beer +type: migrate_example_beer +description: 'Beer is what we drink.' +help: '' +new_revision: false +preview_mode: 1 +display_submitted: true diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/taxonomy.vocabulary.migrate_example_beer_styles.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/taxonomy.vocabulary.migrate_example_beer_styles.yml new file mode 100644 index 000000000..afad4b0dd --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/config/install/taxonomy.vocabulary.migrate_example_beer_styles.yml @@ -0,0 +1,8 @@ +langcode: en +status: true +dependencies: { } +name: 'Migrate Example Beer Styles' +vid: migrate_example_beer_styles +description: 'Use tags to group beers on similar topics into categories.' +hierarchy: 0 +weight: 0 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.info.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.info.yml new file mode 100644 index 000000000..814be16de --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.info.yml @@ -0,0 +1,18 @@ +type: module +name: Migrate Example Setup +description: 'Separate site configuration for the example from the actual migration.' +package: Migration +# core: 8.x +hidden: 1 +dependencies: + - drupal:comment + - drupal:image + - drupal:text + - drupal:options + - drupal:taxonomy + +# Information added by Drupal.org packaging script on 2017-05-10 +version: '8.x-4.0-beta1' +core: '8.x' +project: 'migrate_plus' +datestamp: 1494450189 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.install b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.install new file mode 100644 index 000000000..a8bda3088 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.install @@ -0,0 +1,345 @@ + 'Beers of the world.', + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Beer ID.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'body' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Full description of the beer.', + ), + 'excerpt' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Abstract for this beer.', + ), + 'countries' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Countries of origin. Multiple values, delimited by pipe', + ), + 'aid' => array( + 'type' => 'int', + 'not null' => FALSE, + 'description' => 'Account Id of the author.', + ), + 'image' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Image path', + ), + 'image_alt' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Image ALT', + ), + 'image_title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Image title', + ), + 'image_description' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Image description', + ), + ), + 'primary key' => array('bid'), + ); +} + +function migrate_example_beer_schema_topic() { + return array( + 'description' => 'Categories', + 'fields' => array( + 'style' => array( + 'type' => 'varchar_ascii', + 'length' => 255, + 'not null' => TRUE, + ), + 'details' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + 'style_parent' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Parent topic, if any', + ), + 'region' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Region first associated with this style', + ), + 'hoppiness' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Relative hoppiness of the beer', + ), + ), + 'primary key' => array('style'), + ); +} + +function migrate_example_beer_schema_topic_node() { + return array( + 'description' => 'Beers topic pairs.', + 'fields' => array( + 'bid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Beer ID.', + ), + 'style' => array( + 'type' => 'varchar_ascii', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Topic name', + ), + ), + 'primary key' => array('style', 'bid'), + ); +} + +function migrate_example_beer_schema_comment() { + return array( + 'description' => 'Beers comments.', + 'fields' => array( + 'cid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Comment ID.', + ), + 'bid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Beer ID that is being commented upon', + ), + 'cid_parent' => array( + 'type' => 'int', + 'not null' => FALSE, + 'description' => 'Parent comment ID in case of comment replies.', + ), + 'subject' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment subject', + ), + 'body' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment body', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment name (if anon)', + ), + 'mail' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment email (if anon)', + ), + 'aid' => array( + 'type' => 'int', + 'not null' => FALSE, + 'description' => 'Account ID (if any).', + ), + ), + 'primary key' => array('cid'), + ); +} + +function migrate_example_beer_schema_account() { + return array( + 'description' => 'Beers accounts.', + 'fields' => array( + 'aid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Account ID', + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Blocked_Allowed', + ), + 'registered' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Registration date', + ), + 'username' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Account name (for login)', + ), + 'nickname' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Account name (for display)', + ), + 'password' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Account password (raw)', + ), + 'email' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Account email', + ), + 'sex' => array( + 'type' => 'int', + 'not null' => FALSE, + 'description' => 'Gender (0 for male, 1 for female)', + ), + 'beers' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Favorite Beers', + ), + ), + 'primary key' => array('aid'), + ); +} + +function migrate_example_beer_data_node() { + $fields = array('bid', 'name', 'body', 'excerpt', 'countries', 'aid', 'image', + 'image_alt', 'image_title', 'image_description'); + $query = db_insert('migrate_example_beer_node') + ->fields($fields); + // Use high bid numbers to avoid overwriting an existing node id. + $data = array( + array(99999999, 'Heineken', 'Blab Blah Blah Green', 'Green', 'Netherlands|Belgium', 0, 'heineken.jpg', 'Heinekin alt', 'Heinekin title', 'Heinekin description'), // comes with migrate_example project. + array(99999998, 'Miller Lite', 'We love Miller Brewing', 'Tasteless', 'USA|Canada', 1, NULL, NULL, NULL, NULL), + array(99999997, 'Boddington', 'English occasionally get something right', 'A treat', 'United Kingdom', 1, NULL, NULL, NULL, NULL), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +// Note that alice has duplicate username. Exercises dedupe_entity plugin. +// @TODO duplicate email also. +function migrate_example_beer_data_account() { + $fields = array('status', 'registered', 'username', 'nickname', 'password', 'email', 'sex', 'beers'); + $query = db_insert('migrate_example_beer_account') + ->fields($fields); + $data = array( + array(1, '2010-03-30 10:31:05', 'alice', 'alice in beerland', 'alicepass', 'alice@example.com', '1', '99999999|99999998|99999997'), + array(1, '2010-04-04 10:31:05', 'alice', 'alice in aleland', 'alicepass', 'alice2@example.com', '1', '99999999|99999998|99999997'), + array(0, '2007-03-15 10:31:05', 'bob', 'rebob', 'bobpass', 'bob@example.com', '0', '99999999|99999997'), + array(1, '2004-02-29 10:31:05', 'charlie', 'charlie chocolate', 'mykids', 'charlie@example.com', '0', '99999999|99999998'), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_beer_data_comment() { + $fields = array('bid', 'cid_parent', 'subject', 'body', 'name', 'mail', 'aid'); + $query = db_insert('migrate_example_beer_comment') + ->fields($fields); + $data = array( + array(99999998, NULL, 'im first', 'full body', 'alice', 'alice@example.com', 0), + array(99999998, NULL, 'im second', 'aromatic', 'alice', 'alice@example.com', 0), + array(99999999, NULL, 'im parent', 'malty', 'alice', 'alice@example.com', 0), + array(99999999, 1, 'im child', 'cold body', 'bob', NULL, 1), + array(99999999, 4, 'im grandchild', 'bitter body', 'charlie@example.com', NULL, 1), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_beer_data_topic() { + $fields = array('style', 'details', 'style_parent', 'region', 'hoppiness'); + $query = db_insert('migrate_example_beer_topic') + ->fields($fields); + $data = array( + array('ale', 'traditional', NULL, 'Medieval British Isles', 'Medium'), + array('red ale', 'colorful', 'ale', NULL, NULL), + array('pilsner', 'refreshing', NULL, 'Pilsen, Bohemia (now Czech Republic)', 'Low'), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_beer_data_topic_node() { + $fields = array('bid', 'style'); + $query = db_insert('migrate_example_beer_topic_node') + ->fields($fields); + $data = array( + array(99999999, 'pilsner'), + array(99999999, 'red ale'), + array(99999998, 'red ale'), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrations/beer_comment.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrations/beer_comment.yml new file mode 100644 index 000000000..e631e9ac8 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrations/beer_comment.yml @@ -0,0 +1,45 @@ +# Migration configuration for beer comments. No new concepts here. +id: beer_comment +label: Comments on beers +migration_group: beer +source: + plugin: beer_comment +destination: + plugin: entity:comment +process: + pid: + plugin: migration + migration: beer_comment + source: cid_parent + entity_id: + plugin: migration + migration: beer_node + source: bid + entity_type: + plugin: default_value + default_value: node + field_name: + plugin: default_value + default_value: field_comments + comment_type: + plugin: default_value + default_value: node_comments + subject: subject + uid: + plugin: migration + migration: beer_user + source: aid + name: name + mail: mail + status: + plugin: default_value + default_value: 1 + 'comment_body/value': body +migration_dependencies: + required: + - beer_node + - beer_user +dependencies: + enforced: + module: + - migrate_example diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerComment.php b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerComment.php new file mode 100644 index 000000000..217fad262 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerComment.php @@ -0,0 +1,56 @@ +select('migrate_example_beer_comment', 'mec') + ->fields('mec', ['cid', 'cid_parent', 'name', 'mail', 'aid', + 'body', 'bid', 'subject']) + ->orderBy('cid_parent', 'ASC'); + return $query; + } + + /** + * {@inheritdoc} + */ + public function fields() { + $fields = [ + 'cid' => $this->t('Comment ID'), + 'cid_parent' => $this->t('Parent comment ID in case of comment replies'), + 'name' => $this->t('Comment name (if anon)'), + 'mail' => $this->t('Comment email (if anon)'), + 'aid' => $this->t('Account ID (if any)'), + 'bid' => $this->t('Beer ID that is being commented upon'), + 'subject' => $this->t('Comment subject'), + ]; + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + return [ + 'cid' => [ + 'type' => 'integer', + 'alias' => 'mec', + ], + ]; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerNode.php b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerNode.php new file mode 100644 index 000000000..8d9f0bbfd --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerNode.php @@ -0,0 +1,99 @@ +select('migrate_example_beer_node', 'b') + ->fields('b', ['bid', 'name', 'body', 'excerpt', 'aid', + 'countries', 'image', 'image_alt', 'image_title', + 'image_description']); + return $query; + } + + /** + * {@inheritdoc} + */ + public function fields() { + $fields = [ + 'bid' => $this->t('Beer ID'), + 'name' => $this->t('Name of beer'), + 'body' => $this->t('Full description of the beer'), + 'excerpt' => $this->t('Abstract for this beer'), + 'aid' => $this->t('Account ID of the author'), + 'countries' => $this->t('Countries of origin. Multiple values, delimited by pipe'), + 'image' => $this->t('Image path'), + 'image_alt' => $this->t('Image ALT'), + 'image_title' => $this->t('Image title'), + 'image_description' => $this->t('Image description'), + // Note that this field is not part of the query above - it is populated + // by prepareRow() below. You should document all source properties that + // are available for mapping after prepareRow() is called. + 'terms' => $this->t('Applicable styles'), + ]; + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + return [ + 'bid' => [ + 'type' => 'integer', + 'alias' => 'b', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function prepareRow(Row $row) { + /** + * As explained above, we need to pull the style relationships into our + * source row here, as an array of 'style' values (the unique ID for + * the beer_term migration). + */ + $terms = $this->select('migrate_example_beer_topic_node', 'bt') + ->fields('bt', ['style']) + ->condition('bid', $row->getSourceProperty('bid')) + ->execute() + ->fetchCol(); + $row->setSourceProperty('terms', $terms); + + // As we did for favorite beers in the user migration, we need to explode + // the multi-value country names. + if ($value = $row->getSourceProperty('countries')) { + $row->setSourceProperty('countries', explode('|', $value)); + } + return parent::prepareRow($row); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerTerm.php b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerTerm.php new file mode 100644 index 000000000..f8ea51efe --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerTerm.php @@ -0,0 +1,88 @@ +select() call - this ensures + * that the query is executed against the database configured for this + * source plugin. + */ + return $this->select('migrate_example_beer_topic', 'met') + ->fields('met', ['style', 'details', 'style_parent', 'region', 'hoppiness']) + // We sort this way to ensure parent terms are imported first. + ->orderBy('style_parent', 'ASC'); + } + + /** + * {@inheritdoc} + */ + public function fields() { + /** + * This method simply documents the available source fields provided by + * the source plugin, for use by front-end tools. It returns an array keyed + * by field/column name, with the value being a translated string explaining + * to humans what the field represents. You should always + */ + $fields = [ + 'style' => $this->t('Account ID'), + 'details' => $this->t('Blocked/Allowed'), + 'style_parent' => $this->t('Registered date'), + // These values are not currently migrated - it's OK to skip fields you + // don't need. + 'region' => $this->t('Region the style is associated with'), + 'hoppiness' => $this->t('Hoppiness of the style'), + ]; + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + /** + * This method indicates what field(s) from the source row uniquely identify + * that source row, and what their types are. This is critical information + * for managing the migration. The keys of the returned array are the field + * names from the query which comprise the unique identifier. The values are + * arrays indicating the type of the field, used for creating compatible + * columns in the map tables that track processed items. + */ + return [ + 'style' => [ + 'type' => 'string', + // 'alias' is the alias for the table containing 'style' in the query + // defined above. Optional in this case, but necessary if the same + // column may occur in multiple tables in a join. + 'alias' => 'met', + ], + ]; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerUser.php b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerUser.php new file mode 100644 index 000000000..b0d963d3f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerUser.php @@ -0,0 +1,85 @@ +select('migrate_example_beer_account', 'mea') + ->fields('mea', ['aid', 'status', 'registered', 'username', 'nickname', + 'password', 'email', 'sex', 'beers']); + } + + /** + * {@inheritdoc} + */ + public function fields() { + $fields = [ + 'aid' => $this->t('Account ID'), + 'status' => $this->t('Blocked/Allowed'), + 'registered' => $this->t('Registered date'), + 'username' => $this->t('Account name (for login)'), + 'nickname' => $this->t('Account name (for display)'), + 'password' => $this->t('Account password (raw)'), + 'email' => $this->t('Account email'), + 'sex' => $this->t('Gender'), + 'beers' => $this->t('Favorite beers, pipe-separated'), + ]; + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + return [ + 'aid' => [ + 'type' => 'integer', + 'alias' => 'mea', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function prepareRow(Row $row) { + /** + * prepareRow() is the most common place to perform custom run-time + * processing that isn't handled by an existing process plugin. It is called + * when the raw data has been pulled from the source, and provides the + * opportunity to modify or add to that data, creating the canonical set of + * source data that will be fed into the processing pipeline. + * + * In our particular case, the list of a user's favorite beers is a pipe- + * separated list of beer IDs. The processing pipeline deals with arrays + * representing multi-value fields naturally, so we want to explode that + * string to an array of individual beer IDs. + */ + if ($value = $row->getSourceProperty('beers')) { + $row->setSourceProperty('beers', explode('|', $value)); + } + /** + * Always call your parent! Essential processing is performed in the base + * class. Be mindful that prepareRow() returns a boolean status - if FALSE + * that indicates that the item being processed should be skipped. Unless + * we're deciding to skip an item ourselves, let the parent class decide. + */ + return parent::prepareRow($row); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/README.txt b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/README.txt new file mode 100644 index 000000000..8d5a25d80 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/README.txt @@ -0,0 +1,58 @@ +INTRODUCTION +------------ +The migrate_example_advanced module demonstrates some techniques for Drupal 8 +migrations beyond the basics in migrate_example. It includes a group of +migrations with a wine theme. + +SETUP +----- +To demonstrate XML migrations as realistically as possible, the setup module +provides the source data as REST services. So the migrations' references to these +services can be set up accurately, if you install migrate_example_advanced via +drush be sure to use the --uri parameter, e.g. + +drush en -y migrate_example_advanced --uri=http://d8.local:8083/ + +THE WINE SITE +------------- +In this scenario, we have a wine aficionado site which stores data in SQL tables +as well is pulling in additional data from XML services. + +To make the example as simple as to run as possible, the SQL data is placed in +tables directly in your Drupal database - in most real-world scenarios, your +source data will be in an external database. The migrate_example_advanced_setup +submodule creates and populates these tables, as well as configuring your Drupal +8 site (creating node types, vocabularies, fields, etc.) to receive the data, +and providing service endpoints for XML data. + +STRUCTURE +--------- +As with most custom migrations, there are two primary components to this +example: + +1. Migration configuration, in the config/install directory. These YAML files + describe the migration process and provide the mappings from the source data + to Drupal's destination entities. + +2. Source plugins, in src/Plugin/migrate/source. These are referenced from the + configuration files, and provide the source data to the migration processing + pipeline, as well as manipulating that data where necessary to put it into + a canonical form for migrations. + +UNDERSTANDING THE MIGRATIONS +---------------------------- +Basic techniques demonstrated in the migrate_example module are not rehashed +here - it is expected that if you are learning Drupal 8 migration, you will +study and understand those examples first, and use migrate_example_advanced to +learn about specific techniques beyond those basics. This example doesn't have +the narrative form of migrate_example - it's more of a grab-bag demonstrating +varous features, and is more of a reference for, say, copying the code to set +up an XML migration. An index of things demonstrated by this module: + +Multiple vocabularies populated in one migration +------------------------------------------------ +See migrate_plus.migration.wine_terms.yml. + +Importing from XML services +--------------------------- +See migrate_plus.migration.wine_role_xml.yml. diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.weather_soap.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.weather_soap.yml new file mode 100755 index 000000000..ae2ad03ab --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.weather_soap.yml @@ -0,0 +1,50 @@ +# This migration demonstrates importing from SOAP/WSDL. +id: weather_soap +label: SOAP service providing weather. +migration_group: wine +source: + # We use the SOAP parser source plugin. + plugin: url + data_fetcher_plugin: http # Ignored - SoapClient does the fetching. + data_parser_plugin: soap + # URL of a WSDL endpoint. + urls: http://www.webservicex.net/globalweather.asmx?WSDL + # The function to call on the service, and the parameters to pass. See + # http://www.webservicex.net/New/Home/ServiceDetail/56 for the XML structure + # of this feed - how CountryName is passed within the GetCitiesByCountry + # XML element. + function: GetCitiesByCountry + parameters: + CountryName: Spain + # Responses may be returned as an XML string, an object, or an array - specify + # the type of response here. + response_type: xml + # Looking at the XML response at http://www.webservicex.net/globalweather.asmx/GetCitiesByCountry, + # we see that the data items we want are within . + item_selector: /NewDataSet/Table + # For each field, 'name' is the source property name to be used in the process + # steps below, 'label' is optional (to document the property), and selector + # is an xpath (-like, for array and object returns) string relative to the + # item_selector for retrieving that data value. + fields: + - + name: Country + label: Country + selector: Country + - + name: City + label: City + selector: City + # 'ids' tells us what source property ('City') holds the unique identifying + # value for each imported item, and what schema type to use to hold that + # value in the migration map an message tables. + ids: + City: + type: string +process: + vid: + plugin: default_value + default_value: migrate_example_wine_varieties + name: City +destination: + plugin: entity:taxonomy_term diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_role_json.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_role_json.yml new file mode 100755 index 000000000..e8991a699 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_role_json.yml @@ -0,0 +1,55 @@ +# This migration demonstrates importing from a monolithic JSON file. +id: wine_role_json +label: JSON feed of roles (positions) +migration_group: wine +source: + # We use the JSON source plugin. + plugin: url + data_fetcher_plugin: http + data_parser_plugin: json + # The data_parser normally limits the fields passed on to the source plugin + # to fields configured to be used as part of the migration. To support more + # dynamic migrations, the JSON data parser supports including the original + # data for the current row. Simply include the 'include_raw_data' flag set + # to `true` to enable this. This option is disabled by default to minimize + # memory footprint for migrations that do not need this capability. + # include_raw_data: true + # Normally, this is one or more fully-qualified URLs or file paths. Because + # we can't hardcode your local URL, we provide a relative path here which + # hook_install() will rewrite to a full URL for the current site. + urls: /migrate_example_advanced_position?_format=json + # An xpath-like selector corresponding to the items to be imported. + item_selector: position + # Under 'fields', we list the data items to be imported. The first level keys + # are the source field names we want to populate (the names to be used as + # sources in the process configuration below). For each field we're importing, + # we provide a label (optional - this is for display in migration tools) and + # an xpath for retrieving that value. It's important to note that this xpath + # is relative to the elements retrieved by item_selector. + fields: + - + name: machine_name + label: 'Unique position identifier' + selector: sourceid + - + name: friendly_name + label: 'Position name' + selector: name + # Under 'ids', we identify source fields populated above which will uniquely + # identify each imported item. The 'type' makes sure the migration map table + # uses the proper schema type for stored the IDs. + ids: + machine_name: + type: string +process: + # Note that the source field names here (machine_name and friendly_name) were + # defined by the 'fields' configuration for the source plugin above. + id: machine_name + label: friendly_name +destination: + plugin: entity:user_role +migration_dependencies: {} +dependencies: + enforced: + module: + - migrate_example_advanced diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_role_xml.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_role_xml.yml new file mode 100755 index 000000000..6b7234c24 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_role_xml.yml @@ -0,0 +1,51 @@ +# This migration demonstrates importing from a monolithic XML file. +id: wine_role_xml +label: XML feed of roles (positions) +migration_group: wine +source: + # We use the XML data parser plugin. + plugin: url + data_fetcher_plugin: http + data_parser_plugin: xml + # Normally, this is one or more fully-qualified URLs or file paths. Because + # we can't hardcode your local URL, we provide a relative path here which + # hook_install() will rewrite to a full URL for the current site. + urls: /migrate_example_advanced_position?_format=xml + # Visit the URL above (relative to your site root) and look at it. You can see + # that is the outer element, and each item we want to import is a + # element. The item_xpath value is the xpath to use to query the + # desired elements. + item_selector: /response/position + # Under 'fields', we list the data items to be imported. The first level keys + # are the source field names we want to populate (the names to be used as + # sources in the process configuration below). For each field we're importing, + # we provide a label (optional - this is for display in migration tools) and + # an xpath for retrieving that value. It's important to note that this xpath + # is relative to the elements retrieved by item_xpath. + fields: + - + name: machine_name + label: 'Unique position identifier' + selector: sourceid + - + name: friendly_name + label: 'Position name' + selector: name + # Under 'ids', we identify source fields populated above which will uniquely + # identify each imported item. The 'type' makes sure the migration map table + # uses the proper schema type for stored the IDs. + ids: + machine_name: + type: string +process: + # Note that the source field names here (machine_name and friendly_name) were + # defined by the 'fields' configuration for the source plugin above. + id: machine_name + label: friendly_name +destination: + plugin: entity:user_role +migration_dependencies: {} +dependencies: + enforced: + module: + - migrate_example_advanced diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_terms.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_terms.yml new file mode 100644 index 000000000..f11b82f84 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_terms.yml @@ -0,0 +1,37 @@ +id: wine_terms +label: Migrate all categories into Drupal taxonomy terms +migration_group: wine +source: + plugin: wine_term +destination: + plugin: entity:taxonomy_term +process: + name: name + description: details + # Usually, one wants to have a separate migration for each entity_type/bundle + # combination - e.g., separate migrations for articles and blog posts, as + # opposed to a single monolithic node migration. This affords maximum + # control - the ability to import just one bundle at a time, and most + # importantly to have distinct field mappings (because different node types + # usually have different fields). In this case, though, because all of the + # vocabularies we're importing come from a common table, and on the Drupal + # side there are no distinct custom fields, we are able to import them all in + # one migration. The static_map tells the migration in which vocabulary to + # place each term, based on the incoming 'type' column. + vid: + plugin: static_map + source: type + map: + best_with: migrate_example_wine_best_with + region: migrate_example_wine_regions + variety: migrate_example_wine_varieties + parent: + plugin: migration + migration: wine_terms + source: category_parent + weight: ordering +migration_dependencies: {} +dependencies: + enforced: + module: + - migrate_example_advanced diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_list.yml.txt b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_list.yml.txt new file mode 100755 index 000000000..ae4d2b747 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_list.yml.txt @@ -0,0 +1,56 @@ +# This migration demonstrates importing from an endpoint listing other endpoints +# containing individual item data. +id: wine_variety_list +label: XML feed of varieties +migration_group: wine +source: + # We use the XML source plugin. + plugin: xml + # Normally, this is one or more fully-qualified URLs or file paths. Because + # we can't hardcode your local URL, we provide a relative path here which + # hook_install() will rewrite to a full URL for the current site. + urls: /migrate_example_advanced_variety_list?_format=xml + item_url: /migrate_example_advanced_variety_list/:id?_format=xml + id_selector: /response/items + # Visit the URL above (relative to your site root) and look at it. You can see + # that is the outer element, and each item we want to import is a + # element. The item_xpath value is the xpath to use to query the + # desired elements. + item_selector: /response/variety + # Under 'fields', we list the data items to be imported. The first level keys + # are the source field names we want to populate (the names to be used as + # sources in the process configuration below). For each field we're importing, + # we provide a label (optional - this is for display in migration tools) and + # an xpath for retrieving that value. It's important to note that this xpath + # is relative to the elements retrieved by item_xpath. + fields: + category_name: + label: + selector: name + category_details: + label: + selector: details + category_parent: + label: 'Unique position identifier' + selector: parent + # Under 'ids', we identify source fields populated above which will uniquely + # identify each imported item. The 'type' makes sure the migration map table + # uses the proper schema type for stored the IDs. + ids: + category_name: + type: string +process: + vid: + plugin: default_value + default_value: migrate_example_wine_varieties + name: category_name + description: category_details + parent: + plugin: migration + migration: wine_terms + source: category_parent +destination: + plugin: entity:taxonomy_term +migration_dependencies: + require: + - wine_terms diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_multi_xml.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_multi_xml.yml new file mode 100755 index 000000000..e26579e71 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_multi_xml.yml @@ -0,0 +1,69 @@ +# This migration demonstrates importing from multiple XML files. +id: wine_variety_multi_xml +label: XML feed of varieties +migration_group: wine +source: + # We use the XML source plugin. + plugin: url + data_fetcher_plugin: http + data_parser_plugin: xml + # Normally, this is one or more fully-qualified URLs or file paths. Because + # we can't hardcode your local URL, we provide a relative path here which + # hook_install() will rewrite to a full URL for the current site. + urls: + - /migrate_example_advanced_variety_multiple/red?_format=xml + - /migrate_example_advanced_variety_multiple/white?_format=xml + # Visit the URL above (relative to your site root) and look at it. You can see + # that is the outer element, and each item we want to import is a + # element. The item_xpath value is the xpath to use to query the + # desired elements. + item_selector: /response/variety + # Under 'fields', we list the data items to be imported. The first level keys + # are the source field names we want to populate (the names to be used as + # sources in the process configuration below). For each field we're importing, + # we provide a label (optional - this is for display in migration tools) and + # an xpath for retrieving that value. It's important to note that this xpath + # is relative to the elements retrieved by item_xpath. + fields: + - + name: category_name + label: Name + selector: name + - + name: category_details + label: Details + selector: details + - + name: category_parent + label: 'Unique position identifier' + selector: parent + - + name: category_attributes + label: 'List of variety attributes' + selector: attributes + # Under 'ids', we identify source fields populated above which will uniquely + # identify each imported item. The 'type' makes sure the migration map table + # uses the proper schema type for stored the IDs. + ids: + category_name: + type: string +process: + vid: + plugin: default_value + default_value: migrate_example_wine_varieties + name: category_name + description: category_details + parent: + plugin: migration + migration: wine_terms + source: category_parent + field_variety_attributes: category_attributes +destination: + plugin: entity:taxonomy_term +migration_dependencies: + required: + - wine_terms +dependencies: + enforced: + module: + - migrate_example_advanced diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration_group.wine.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration_group.wine.yml new file mode 100644 index 000000000..5a14bf29b --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration_group.wine.yml @@ -0,0 +1,11 @@ +id: wine +label: Wine Imports +description: A few wine-related imports, demonstrating some more advanced migration techniques. +source_type: Custom tables and XML +shared_configuration: + source: + key: default +dependencies: + enforced: + module: + - migrate_example_advanced diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/0001.xml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/0001.xml new file mode 100644 index 000000000..a78c02bf0 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/0001.xml @@ -0,0 +1,7 @@ + + + Lolonis Winery + Makers of Ladybug Red + 3 + Redwood Valley + diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/0002.xml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/0002.xml new file mode 100644 index 000000000..380113b92 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/0002.xml @@ -0,0 +1,7 @@ + + + Château Latour + Makers of grand vin Chateau Latour, Les Forts de Latour and Pauillac + 3 + Bordeaux + diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/index.xml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/index.xml new file mode 100644 index 000000000..28053984e --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/index.xml @@ -0,0 +1,4 @@ + + + 0001 + diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/index2.xml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/index2.xml new file mode 100644 index 000000000..9dfa50022 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/data/index2.xml @@ -0,0 +1,4 @@ + + + 0002 + diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced.info.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced.info.yml new file mode 100644 index 000000000..ba5e45b74 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced.info.yml @@ -0,0 +1,15 @@ +type: module +name: Migrate Example (Advanced) +description: 'Specialized examples of Drupal 8 migration.' +package: Examples +# core: 8.x +dependencies: + - drupal:migrate + - migrate_plus:migrate_example_advanced_setup + - 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_plus' +datestamp: 1494450189 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced.install b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced.install new file mode 100644 index 000000000..b2506e4a9 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced.install @@ -0,0 +1,50 @@ +get('source'); + $request = \Drupal::request(); + $source['urls'] = 'http://' . $request->getHttpHost() . $source['urls']; + $wine_role_xml_migration->set('source', $source); + $wine_role_xml_migration->save(); + } + /** @var \Drupal\migrate_plus\Entity\MigrationInterface $wine_role_json_migration */ + $wine_role_json_migration = Migration::load('wine_role_json'); + if ($wine_role_json_migration) { + $source = $wine_role_json_migration->get('source'); + $request = \Drupal::request(); + $source['urls'] = 'http://' . $request->getHttpHost() . $source['urls']; + $wine_role_json_migration->set('source', $source); + $wine_role_json_migration->save(); + } + /** @var \Drupal\migrate_plus\Entity\MigrationInterface $wine_variety_multi_xml_migration */ + $wine_variety_multi_xml_migration = Migration::load('wine_variety_multi_xml'); + if ($wine_variety_multi_xml_migration) { + $source = $wine_variety_multi_xml_migration->get('source'); + $request = \Drupal::request(); + $urls = []; + foreach ($source['urls'] as $url) { + $urls[] = 'http://' . $request->getHttpHost() . $url; + } + $source['urls'] = $urls; + $wine_variety_multi_xml_migration->set('source', $source); + $wine_variety_multi_xml_migration->save(); + } +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/comment.type.migrate_example_advanced_comment.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/comment.type.migrate_example_advanced_comment.yml new file mode 100644 index 000000000..ba582793d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/comment.type.migrate_example_advanced_comment.yml @@ -0,0 +1,7 @@ +langcode: en +status: true +dependencies: { } +id: migrate_example_advanced_comment +label: 'Node comments' +target_entity_type_id: node +description: '' diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.comment.migrate_example_advanced_comment.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.comment.migrate_example_advanced_comment.default.yml new file mode 100644 index 000000000..518664e2b --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.comment.migrate_example_advanced_comment.default.yml @@ -0,0 +1,30 @@ +langcode: en +status: true +dependencies: + config: + - comment.type.migrate_example_advanced_comment + - field.field.comment.migrate_example_advanced_comment.comment_body + module: + - text +id: comment.migrate_example_advanced_comment.default +targetEntityType: comment +bundle: migrate_example_advanced_comment +mode: default +content: + author: + weight: -2 + comment_body: + type: text_textarea + weight: 11 + settings: + rows: 5 + placeholder: '' + third_party_settings: { } + subject: + type: string_textfield + weight: 10 + settings: + size: 60 + placeholder: '' + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.node.migrate_example_producer.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.node.migrate_example_producer.default.yml new file mode 100644 index 000000000..89df7690e --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.node.migrate_example_producer.default.yml @@ -0,0 +1,69 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.migrate_example_producer.body + - field.field.node.migrate_example_producer.field_migrate_example_wine_regio + - node.type.migrate_example_producer + module: + - path + - text +id: node.migrate_example_producer.default +targetEntityType: node +bundle: migrate_example_producer +mode: default +content: + body: + type: text_textarea_with_summary + weight: 31 + settings: + rows: 9 + summary_rows: 3 + placeholder: '' + third_party_settings: { } + created: + type: datetime_timestamp + weight: 10 + settings: { } + third_party_settings: { } + field_migrate_example_wine_regio: + weight: 32 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + third_party_settings: { } + type: entity_reference_autocomplete + path: + type: path + weight: 30 + settings: { } + third_party_settings: { } + promote: + type: boolean_checkbox + settings: + display_label: true + weight: 15 + third_party_settings: { } + sticky: + type: boolean_checkbox + settings: + display_label: true + weight: 16 + third_party_settings: { } + title: + type: string_textfield + weight: -5 + settings: + size: 60 + placeholder: '' + third_party_settings: { } + uid: + type: entity_reference_autocomplete + weight: 5 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.node.migrate_example_wine.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.node.migrate_example_wine.default.yml new file mode 100644 index 000000000..0ef5edb66 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.node.migrate_example_wine.default.yml @@ -0,0 +1,117 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.migrate_example_wine.body + - field.field.node.migrate_example_wine.field_migrate_example_comments + - field.field.node.migrate_example_wine.field_migrate_example_adv_image + - field.field.node.migrate_example_wine.field_migrate_example_top_vintag + - field.field.node.migrate_example_wine.field_migrate_example_wine_best + - field.field.node.migrate_example_wine.field_migrate_example_wine_ratin + - field.field.node.migrate_example_wine.field_migrate_example_wine_regio + - field.field.node.migrate_example_wine.field_migrate_example_wine_var + - node.type.migrate_example_wine + module: + - comment + - image + - path + - text +id: node.migrate_example_wine.default +targetEntityType: node +bundle: migrate_example_wine +mode: default +content: + body: + type: text_textarea_with_summary + weight: 31 + settings: + rows: 9 + summary_rows: 3 + placeholder: '' + third_party_settings: { } + created: + type: datetime_timestamp + weight: 10 + settings: { } + third_party_settings: { } + field_migrate_example_comments: + weight: 38 + settings: { } + third_party_settings: { } + type: comment_default + field_migrate_example_adv_image: + weight: 35 + settings: + progress_indicator: throbber + preview_image_style: thumbnail + third_party_settings: { } + type: image_image + field_migrate_example_top_vintag: + weight: 37 + settings: + placeholder: '' + third_party_settings: { } + type: number + field_migrate_example_wine_best: + weight: 34 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + third_party_settings: { } + type: entity_reference_autocomplete_tags + field_migrate_example_wine_ratin: + weight: 36 + settings: + placeholder: '' + third_party_settings: { } + type: number + field_migrate_example_wine_regio: + weight: 33 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + third_party_settings: { } + type: entity_reference_autocomplete + field_migrate_example_wine_var: + weight: 32 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + third_party_settings: { } + type: entity_reference_autocomplete + path: + type: path + weight: 30 + settings: { } + third_party_settings: { } + promote: + type: boolean_checkbox + settings: + display_label: true + weight: 15 + third_party_settings: { } + sticky: + type: boolean_checkbox + settings: + display_label: true + weight: 16 + third_party_settings: { } + title: + type: string_textfield + weight: -5 + settings: + size: 60 + placeholder: '' + third_party_settings: { } + uid: + type: entity_reference_autocomplete + weight: 5 + settings: + match_operator: CONTAINS + size: 60 + placeholder: '' + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.taxonomy_term.migrate_example_wine_varieties.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.taxonomy_term.migrate_example_wine_varieties.default.yml new file mode 100644 index 000000000..32bd69244 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_form_display.taxonomy_term.migrate_example_wine_varieties.default.yml @@ -0,0 +1,41 @@ +langcode: en +status: true +dependencies: + config: + - field.field.taxonomy_term.migrate_example_wine_varieties.field_variety_attributes + - taxonomy.vocabulary.migrate_example_wine_varieties + module: + - path + - text +id: taxonomy_term.migrate_example_wine_varieties.default +targetEntityType: taxonomy_term +bundle: migrate_example_wine_varieties +mode: default +content: + description: + type: text_textarea + weight: 1 + settings: + placeholder: '' + rows: 5 + third_party_settings: { } + field_variety_attributes: + type: string_textfield + weight: 3 + settings: + size: 60 + placeholder: '' + third_party_settings: { } + name: + type: string_textfield + weight: 0 + settings: + size: 60 + placeholder: '' + third_party_settings: { } + path: + type: path + weight: 2 + settings: { } + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.comment.migrate_example_advanced_comment.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.comment.migrate_example_advanced_comment.default.yml new file mode 100644 index 000000000..b4b97f7de --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.comment.migrate_example_advanced_comment.default.yml @@ -0,0 +1,22 @@ +langcode: en +status: true +dependencies: + config: + - comment.type.migrate_example_advanced_comment + - field.field.comment.migrate_example_advanced_comment.comment_body + module: + - text +id: comment.migrate_example_advanced_comment.default +targetEntityType: comment +bundle: migrate_example_advanced_comment +mode: default +content: + comment_body: + label: hidden + type: text_default + weight: 0 + settings: { } + third_party_settings: { } + links: + weight: 100 +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_producer.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_producer.default.yml new file mode 100644 index 000000000..90c620b9c --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_producer.default.yml @@ -0,0 +1,33 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.migrate_example_producer.body + - field.field.node.migrate_example_producer.field_migrate_example_wine_regio + - node.type.migrate_example_producer + module: + - text + - user +id: node.migrate_example_producer.default +targetEntityType: node +bundle: migrate_example_producer +mode: default +content: + body: + label: hidden + type: text_default + weight: 101 + settings: { } + third_party_settings: { } + field_migrate_example_wine_regio: + weight: 102 + label: inline + settings: + link: true + third_party_settings: { } + type: entity_reference_label + links: + weight: 100 + settings: { } + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_producer.teaser.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_producer.teaser.yml new file mode 100644 index 000000000..ec5fcd544 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_producer.teaser.yml @@ -0,0 +1,27 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + - field.field.node.migrate_example_producer.body + - field.field.node.migrate_example_producer.field_migrate_example_wine_regio + - node.type.migrate_example_producer + module: + - text + - user +id: node.migrate_example_producer.teaser +targetEntityType: node +bundle: migrate_example_producer +mode: teaser +content: + body: + label: hidden + type: text_summary_or_trimmed + weight: 101 + settings: + trim_length: 600 + third_party_settings: { } + links: + weight: 100 +hidden: + field_migrate_example_wine_regio: true diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_wine.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_wine.default.yml new file mode 100644 index 000000000..57ad1155e --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_wine.default.yml @@ -0,0 +1,88 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.migrate_example_wine.body + - field.field.node.migrate_example_wine.field_migrate_example_comments + - field.field.node.migrate_example_wine.field_migrate_example_adv_image + - field.field.node.migrate_example_wine.field_migrate_example_top_vintag + - field.field.node.migrate_example_wine.field_migrate_example_wine_best + - field.field.node.migrate_example_wine.field_migrate_example_wine_ratin + - field.field.node.migrate_example_wine.field_migrate_example_wine_regio + - field.field.node.migrate_example_wine.field_migrate_example_wine_var + - node.type.migrate_example_wine + module: + - comment + - image + - text + - user +id: node.migrate_example_wine.default +targetEntityType: node +bundle: migrate_example_wine +mode: default +content: + body: + label: hidden + type: text_default + weight: 101 + settings: { } + third_party_settings: { } + field_migrate_example_comments: + weight: 108 + label: hidden + settings: + pager_id: 0 + third_party_settings: { } + type: comment_default + field_migrate_example_adv_image: + weight: 105 + label: hidden + settings: + image_style: '' + image_link: '' + third_party_settings: { } + type: image + field_migrate_example_top_vintag: + weight: 107 + label: inline + settings: + thousand_separator: '' + prefix_suffix: true + third_party_settings: { } + type: number_integer + field_migrate_example_wine_best: + weight: 104 + label: inline + settings: + link: true + third_party_settings: { } + type: entity_reference_label + field_migrate_example_wine_ratin: + weight: 106 + label: inline + settings: + thousand_separator: '' + decimal_separator: . + scale: 2 + prefix_suffix: true + third_party_settings: { } + type: number_decimal + field_migrate_example_wine_regio: + weight: 103 + label: inline + settings: + link: true + third_party_settings: { } + type: entity_reference_label + field_migrate_example_wine_var: + weight: 102 + label: inline + settings: + link: true + third_party_settings: { } + type: entity_reference_label + links: + weight: 100 + settings: { } + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_wine.teaser.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_wine.teaser.yml new file mode 100644 index 000000000..85874f7c9 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.node.migrate_example_wine.teaser.yml @@ -0,0 +1,39 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + - field.field.node.migrate_example_wine.body + - field.field.node.migrate_example_wine.field_migrate_example_comments + - field.field.node.migrate_example_wine.field_migrate_example_adv_image + - field.field.node.migrate_example_wine.field_migrate_example_top_vintag + - field.field.node.migrate_example_wine.field_migrate_example_wine_best + - field.field.node.migrate_example_wine.field_migrate_example_wine_ratin + - field.field.node.migrate_example_wine.field_migrate_example_wine_regio + - field.field.node.migrate_example_wine.field_migrate_example_wine_var + - node.type.migrate_example_wine + module: + - text + - user +id: node.migrate_example_wine.teaser +targetEntityType: node +bundle: migrate_example_wine +mode: teaser +content: + body: + label: hidden + type: text_summary_or_trimmed + weight: 101 + settings: + trim_length: 600 + third_party_settings: { } + links: + weight: 100 +hidden: + field_migrate_example_comments: true + field_migrate_example_adv_image: true + field_migrate_example_top_vintag: true + field_migrate_example_wine_best: true + field_migrate_example_wine_ratin: true + field_migrate_example_wine_regio: true + field_migrate_example_wine_var: true diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.taxonomy_term.migrate_example_wine_varieties.default.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.taxonomy_term.migrate_example_wine_varieties.default.yml new file mode 100644 index 000000000..b475708a7 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/core.entity_view_display.taxonomy_term.migrate_example_wine_varieties.default.yml @@ -0,0 +1,27 @@ +langcode: en +status: true +dependencies: + config: + - field.field.taxonomy_term.migrate_example_wine_varieties.field_variety_attributes + - taxonomy.vocabulary.migrate_example_wine_varieties + module: + - text +id: taxonomy_term.migrate_example_wine_varieties.default +targetEntityType: taxonomy_term +bundle: migrate_example_wine_varieties +mode: default +content: + description: + label: hidden + type: text_default + weight: 0 + settings: { } + third_party_settings: { } + field_variety_attributes: + type: string + weight: 0 + label: inline + settings: + link_to_entity: false + third_party_settings: { } +hidden: { } diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.comment.migrate_example_advanced_comment.comment_body.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.comment.migrate_example_advanced_comment.comment_body.yml new file mode 100644 index 000000000..456d6230f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.comment.migrate_example_advanced_comment.comment_body.yml @@ -0,0 +1,20 @@ +langcode: en +status: true +dependencies: + config: + - comment.type.migrate_example_advanced_comment + - field.storage.comment.comment_body + module: + - text +id: comment.migrate_example_advanced_comment.comment_body +field_name: comment_body +entity_type: comment +bundle: migrate_example_advanced_comment +label: Comment +description: '' +required: true +translatable: true +default_value: { } +default_value_callback: '' +settings: { } +field_type: text_long diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_producer.body.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_producer.body.yml new file mode 100644 index 000000000..4ddb0abd5 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_producer.body.yml @@ -0,0 +1,21 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.body + - node.type.migrate_example_producer + module: + - text +id: node.migrate_example_producer.body +field_name: body +entity_type: node +bundle: migrate_example_producer +label: Body +description: '' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: + display_summary: true +field_type: text_with_summary diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_producer.field_migrate_example_wine_regio.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_producer.field_migrate_example_wine_regio.yml new file mode 100644 index 000000000..fa8b92a41 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_producer.field_migrate_example_wine_regio.yml @@ -0,0 +1,26 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_wine_regio + - node.type.migrate_example_producer + - taxonomy.vocabulary.migrate_example_wine_regions +id: node.migrate_example_producer.field_migrate_example_wine_regio +field_name: field_migrate_example_wine_regio +entity_type: node +bundle: migrate_example_producer +label: 'Migrate Example Wine Regions' +description: 'Select the region this wine comes from' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: + handler: 'default:taxonomy_term' + handler_settings: + target_bundles: + migrate_example_wine_regions: migrate_example_wine_regions + sort: + field: _none + auto_create: false +field_type: entity_reference diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.body.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.body.yml new file mode 100644 index 000000000..b8c0cff34 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.body.yml @@ -0,0 +1,21 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.body + - node.type.migrate_example_wine + module: + - text +id: node.migrate_example_wine.body +field_name: body +entity_type: node +bundle: migrate_example_wine +label: Body +description: '' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: + display_summary: true +field_type: text_with_summary diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_adv_image.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_adv_image.yml new file mode 100644 index 000000000..8933c38dc --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_adv_image.yml @@ -0,0 +1,37 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_adv_image + - node.type.migrate_example_wine + module: + - image +id: node.migrate_example_wine.field_migrate_example_adv_image +field_name: field_migrate_example_adv_image +entity_type: node +bundle: migrate_example_wine +label: Image +description: 'Upload an image to go with this wine.' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + file_directory: field/migrate_example/image + file_extensions: 'png gif jpg jpeg' + max_filesize: '' + max_resolution: '' + min_resolution: '' + alt_field: true + alt_field_required: false + title_field: false + title_field_required: false + default_image: + uuid: '' + alt: '' + title: '' + width: null + height: null + handler: 'default:file' + handler_settings: { } +field_type: image diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_comments.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_comments.yml new file mode 100644 index 000000000..f50dcd772 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_comments.yml @@ -0,0 +1,32 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_comments + - node.type.migrate_example_wine + module: + - comment +id: node.migrate_example_wine.field_migrate_example_comments +field_name: field_migrate_example_comments +entity_type: node +bundle: migrate_example_wine +label: Comments +description: '' +required: false +translatable: false +default_value: + - + status: 2 + cid: 0 + last_comment_timestamp: 0 + last_comment_name: null + last_comment_uid: 0 + comment_count: 0 +default_value_callback: '' +settings: + default_mode: 1 + per_page: 50 + anonymous: 0 + form_location: true + preview: 1 +field_type: comment diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_top_vintag.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_top_vintag.yml new file mode 100644 index 000000000..5b95bb5e0 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_top_vintag.yml @@ -0,0 +1,22 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_top_vintag + - node.type.migrate_example_wine +id: node.migrate_example_wine.field_migrate_example_top_vintag +field_name: field_migrate_example_top_vintag +entity_type: node +bundle: migrate_example_wine +label: 'Recommended vintages' +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + min: null + max: null + prefix: '' + suffix: '' +field_type: integer diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_best.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_best.yml new file mode 100644 index 000000000..224fa66df --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_best.yml @@ -0,0 +1,26 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_wine_best + - node.type.migrate_example_wine + - taxonomy.vocabulary.migrate_example_wine_best_with +id: node.migrate_example_wine.field_migrate_example_wine_best +field_name: field_migrate_example_wine_best +entity_type: node +bundle: migrate_example_wine +label: 'Migrate Example Wine Best With' +description: 'Enter any foods this wine may be paired with, separated by commas' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + handler: 'default:taxonomy_term' + handler_settings: + target_bundles: + migrate_example_wine_best_with: migrate_example_wine_best_with + sort: + field: _none + auto_create: false +field_type: entity_reference diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_ratin.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_ratin.yml new file mode 100644 index 000000000..5841eae0f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_ratin.yml @@ -0,0 +1,22 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_wine_ratin + - node.type.migrate_example_wine +id: node.migrate_example_wine.field_migrate_example_wine_ratin +field_name: field_migrate_example_wine_ratin +entity_type: node +bundle: migrate_example_wine +label: Rating +description: 'Rating on a 1-100 scale' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + min: !!float 1 + max: !!float 100 + prefix: '' + suffix: '' +field_type: decimal diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_regio.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_regio.yml new file mode 100644 index 000000000..67b3534f5 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_regio.yml @@ -0,0 +1,26 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_wine_regio + - node.type.migrate_example_wine + - taxonomy.vocabulary.migrate_example_wine_regions +id: node.migrate_example_wine.field_migrate_example_wine_regio +field_name: field_migrate_example_wine_regio +entity_type: node +bundle: migrate_example_wine +label: 'Migrate Example Wine Regions' +description: 'Select the region this wine comes from' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + handler: 'default:taxonomy_term' + handler_settings: + target_bundles: + migrate_example_wine_regions: migrate_example_wine_regions + sort: + field: _none + auto_create: false +field_type: entity_reference diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_var.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_var.yml new file mode 100644 index 000000000..28b81942d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.node.migrate_example_wine.field_migrate_example_wine_var.yml @@ -0,0 +1,26 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_migrate_example_wine_var + - node.type.migrate_example_wine + - taxonomy.vocabulary.migrate_example_wine_varieties +id: node.migrate_example_wine.field_migrate_example_wine_var +field_name: field_migrate_example_wine_var +entity_type: node +bundle: migrate_example_wine +label: 'Migrate Example Wine Varieties' +description: 'Select the variety of this wine' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + handler: 'default:taxonomy_term' + handler_settings: + target_bundles: + migrate_example_wine_varieties: migrate_example_wine_varieties + sort: + field: _none + auto_create: false +field_type: entity_reference diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.taxonomy_term.migrate_example_wine_varieties.field_variety_attributes.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.taxonomy_term.migrate_example_wine_varieties.field_variety_attributes.yml new file mode 100644 index 000000000..7164459f8 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.field.taxonomy_term.migrate_example_wine_varieties.field_variety_attributes.yml @@ -0,0 +1,18 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.taxonomy_term.field_variety_attributes + - taxonomy.vocabulary.migrate_example_wine_varieties +id: taxonomy_term.migrate_example_wine_varieties.field_variety_attributes +field_name: field_variety_attributes +entity_type: taxonomy_term +bundle: migrate_example_wine_varieties +label: Attributes +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: { } +field_type: string diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_adv_image.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_adv_image.yml new file mode 100644 index 000000000..62fc9b090 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_adv_image.yml @@ -0,0 +1,29 @@ +langcode: en +status: true +dependencies: + module: + - file + - image + - node +id: node.field_migrate_example_adv_image +field_name: field_migrate_example_adv_image +entity_type: node +type: image +settings: + uri_scheme: public + default_image: + uuid: '' + alt: '' + title: '' + width: null + height: null + target_type: file + display_field: false + display_default: false +module: image +locked: false +cardinality: -1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_comments.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_comments.yml new file mode 100644 index 000000000..1d7b9f10a --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_comments.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - comment + - node +id: node.field_migrate_example_comments +field_name: field_migrate_example_comments +entity_type: node +type: comment +settings: + comment_type: migrate_example_advanced_comment +module: comment +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_top_vintag.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_top_vintag.yml new file mode 100644 index 000000000..86b0c27b2 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_top_vintag.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - node +id: node.field_migrate_example_top_vintag +field_name: field_migrate_example_top_vintag +entity_type: node +type: integer +settings: + unsigned: false + size: normal +module: core +locked: false +cardinality: -1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_best.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_best.yml new file mode 100644 index 000000000..9026e5881 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_best.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - node + - taxonomy +id: node.field_migrate_example_wine_best +field_name: field_migrate_example_wine_best +entity_type: node +type: entity_reference +settings: + target_type: taxonomy_term +module: core +locked: false +cardinality: -1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_ratin.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_ratin.yml new file mode 100644 index 000000000..fc3dbe9c7 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_ratin.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - node +id: node.field_migrate_example_wine_ratin +field_name: field_migrate_example_wine_ratin +entity_type: node +type: decimal +settings: + precision: 10 + scale: 0 +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_regio.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_regio.yml new file mode 100644 index 000000000..3b2b16339 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_regio.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - node + - taxonomy +id: node.field_migrate_example_wine_regio +field_name: field_migrate_example_wine_regio +entity_type: node +type: entity_reference +settings: + target_type: taxonomy_term +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_var.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_var.yml new file mode 100644 index 000000000..cca7a39fc --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.node.field_migrate_example_wine_var.yml @@ -0,0 +1,19 @@ +langcode: en +status: true +dependencies: + module: + - node + - taxonomy +id: node.field_migrate_example_wine_var +field_name: field_migrate_example_wine_var +entity_type: node +type: entity_reference +settings: + target_type: taxonomy_term +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.taxonomy_term.field_variety_attributes.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.taxonomy_term.field_variety_attributes.yml new file mode 100644 index 000000000..e5db23f20 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/field.storage.taxonomy_term.field_variety_attributes.yml @@ -0,0 +1,20 @@ +langcode: en +status: true +dependencies: + module: + - taxonomy +id: taxonomy_term.field_variety_attributes +field_name: field_variety_attributes +entity_type: taxonomy_term +type: string +settings: + max_length: 255 + is_ascii: false + case_sensitive: false +module: core +locked: false +cardinality: -1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/node.type.migrate_example_producer.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/node.type.migrate_example_producer.yml new file mode 100644 index 000000000..4d78fee87 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/node.type.migrate_example_producer.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: { } +name: 'Wine Producer' +type: migrate_example_producer +description: 'Wineries, vineyards, and large producers.' +help: '' +new_revision: false +preview_mode: 1 +display_submitted: true diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/node.type.migrate_example_wine.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/node.type.migrate_example_wine.yml new file mode 100644 index 000000000..6f442d5e6 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/node.type.migrate_example_wine.yml @@ -0,0 +1,10 @@ +langcode: en +status: true +dependencies: { } +name: Wine +type: migrate_example_wine +description: 'Wine is what we drink.' +help: '' +new_revision: false +preview_mode: 1 +display_submitted: true diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_position.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_position.yml new file mode 100644 index 000000000..6a79fc90f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_position.yml @@ -0,0 +1,10 @@ +id: migrate_example_advanced_position +plugin_id: 'migrate_example_advanced_position' +granularity: method +configuration: + GET: + supported_formats: + - json + - xml + supported_auth: + - cookie diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_items.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_items.yml new file mode 100644 index 000000000..bf2518a5f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_items.yml @@ -0,0 +1,10 @@ +id: migrate_example_advanced_variety_items +plugin_id: 'migrate_example_advanced_variety_items' +granularity: method +configuration: + GET: + supported_formats: + - json + - xml + supported_auth: + - cookie diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_list.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_list.yml new file mode 100644 index 000000000..f19dc60fe --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_list.yml @@ -0,0 +1,10 @@ +id: migrate_example_advanced_variety_list +plugin_id: 'migrate_example_advanced_variety_list' +granularity: method +configuration: + GET: + supported_formats: + - json + - xml + supported_auth: + - cookie diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_multiple.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_multiple.yml new file mode 100644 index 000000000..c8960369f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/rest.resource.migrate_example_advanced_variety_multiple.yml @@ -0,0 +1,10 @@ +id: migrate_example_advanced_variety_multiple +plugin_id: 'migrate_example_advanced_variety_multiple' +granularity: method +configuration: + GET: + supported_formats: + - json + - xml + supported_auth: + - cookie diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_best_with.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_best_with.yml new file mode 100644 index 000000000..51cb1c93e --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_best_with.yml @@ -0,0 +1,8 @@ +langcode: en +status: true +dependencies: { } +name: 'Migrate Example Wine Best With' +vid: migrate_example_wine_best_with +description: 'Foods the wine goes best with' +hierarchy: 0 +weight: 0 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_regions.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_regions.yml new file mode 100644 index 000000000..9ebbadfcd --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_regions.yml @@ -0,0 +1,8 @@ +langcode: en +status: true +dependencies: { } +name: 'Migrate Example Wine Regions' +vid: migrate_example_wine_regions +description: 'Wine regions' +hierarchy: 0 +weight: 0 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_varieties.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_varieties.yml new file mode 100644 index 000000000..ca12aca5f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/config/install/taxonomy.vocabulary.migrate_example_wine_varieties.yml @@ -0,0 +1,8 @@ +langcode: en +status: true +dependencies: { } +name: 'Migrate Example Wine Varieties' +vid: migrate_example_wine_varieties +description: 'Wine varieties' +hierarchy: 0 +weight: 0 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.info.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.info.yml new file mode 100644 index 000000000..992cff06e --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.info.yml @@ -0,0 +1,18 @@ +type: module +name: Migrate Advanced Example Setup +description: 'Separate site configuration for the example from the actual migration.' +package: Migration +# core: 8.x +hidden: 1 +dependencies: + - drupal:comment + - drupal:image + - drupal:text + - drupal:taxonomy + - drupal:rest + +# Information added by Drupal.org packaging script on 2017-05-10 +version: '8.x-4.0-beta1' +core: '8.x' +project: 'migrate_plus' +datestamp: 1494450189 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.install b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.install new file mode 100644 index 000000000..3c585b19c --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.install @@ -0,0 +1,895 @@ + 'Wines of the world', + 'fields' => [ + 'wineid' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Wine ID', + ], + 'name' => [ + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ], + 'body' => [ + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Full description of the wine.', + ], + 'excerpt' => [ + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Abstract for this wine.', + ], + 'accountid' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'ID of the author.', + ], + 'posted' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Original creation date', + ], + 'last_changed' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Last change date', + ], + 'variety' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Wine variety', + ], + 'region' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Wine region', + ], + 'rating' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'Rating (100-point scale)', + ], + ], + 'primary key' => ['wineid'], + ]; +} + +function migrate_example_advanced_schema_updates() { + return array( + 'description' => 'Updated wine ratings', + 'fields' => [ + 'wineid' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Wine ID', + ], + 'rating' => [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'Rating (100-point scale)', + ], + ], + 'primary key' => ['wineid'], + ); +} + +function migrate_example_advanced_schema_producer() { + return array( + 'description' => 'Wine producers of the world', + 'fields' => array( + 'producerid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Producer ID', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'body' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Full description of the producer.', + ), + 'excerpt' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Abstract for this producer.', + ), + 'accountid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'Account ID of the author.', + ), + ), + 'primary key' => array('producerid'), + ); +} + +function migrate_example_advanced_schema_categories() { + return array( + 'description' => 'Categories', + 'fields' => array( + 'categoryid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'unsigned' => TRUE, + 'description' => 'Category ID', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Type of category: variety, region, best_with', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'details' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + 'category_parent' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'Parent category, if any', + ), + 'ordering' => array( + 'type' => 'int', + 'unsigned' => FALSE, + 'not null' => FALSE, + 'description' => 'Order in which to display categories', + ), + ), + 'primary key' => array('categoryid'), + ); +} + +function migrate_example_advanced_schema_vintages() { + return array( + 'description' => 'Wine vintages', + 'fields' => array( + 'wineid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Wine ID', + ), + 'vintage' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Vintage (year)', + ), + ), + 'primary key' => array('wineid', 'vintage'), + ); +} + +function migrate_example_advanced_schema_variety_updates() { + return array( + 'description' => 'Variety updates', + 'fields' => array( + 'categoryid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'unsigned' => TRUE, + 'description' => 'Category ID', + ), + 'details' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + ), + 'primary key' => array('categoryid'), + ); +} + +function migrate_example_advanced_schema_category_wine() { + return array( + 'description' => 'Wine category assignments', + 'fields' => array( + 'wineid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Wine ID', + ), + 'categoryid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Category ID', + ), + ), + 'primary key' => array('categoryid', 'wineid'), + ); +} + +function migrate_example_advanced_schema_category_producer() { + return array( + 'description' => 'Producer category assignments', + 'fields' => array( + 'producerid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Producer ID', + ), + 'categoryid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Category ID', + ), + ), + 'primary key' => array('categoryid', 'producerid'), + ); +} + +function migrate_example_advanced_schema_comment() { + return array( + 'description' => 'Wine comments', + 'fields' => array( + 'commentid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Comment ID', + ), + 'wineid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Wine ID that is being commented upon', + ), + 'comment_parent' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'Parent comment ID in case of comment replies.', + ), + 'subject' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment subject', + ), + 'body' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment body', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment name (if anon)', + ), + 'mail' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment email (if anon)', + ), + 'accountid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'Account ID (if any).', + ), + 'commenthost' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'IP/domain of host posted from', + ), + 'userpage' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'User homepage', + ), + 'posted' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Date comment posted', + ), + 'lastchanged' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Date comment last changed', + ), + ), + 'primary key' => array('commentid'), + ); +} + +function migrate_example_advanced_schema_comment_updates() { + return array( + 'description' => 'Wine comment updates', + 'fields' => array( + 'commentid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Comment ID', + ), + 'subject' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Comment subject', + ), + ), + 'primary key' => array('commentid'), + ); +} + +function migrate_example_advanced_schema_account() { + return array( + 'description' => 'Wine accounts.', + 'fields' => array( + 'accountid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Account ID', + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Blocked_Allowed', + ), + 'posted' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Registration date', + ), + 'last_access' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Last access date', + ), + 'last_login' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Last login date', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Account name (for login)', + ), + 'sex' => array( + 'type' => 'char', + 'length' => 1, + 'not null' => FALSE, + 'description' => 'Gender', + ), + 'password' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Account password (raw)', + ), + 'mail' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Account email', + ), + 'original_mail' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Original account email', + ), + 'sig' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Signature for comments', + ), + 'imageid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'Image ID', + ), + 'positions' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Positions held', + ), + ), + 'primary key' => array('accountid'), + ); +} + +function migrate_example_advanced_schema_account_updates() { + return array( + 'description' => 'Wine account updates', + 'fields' => array( + 'accountid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Account ID', + ), + 'sex' => array( + 'type' => 'char', + 'length' => 1, + 'not null' => FALSE, + 'description' => 'Gender', + ), + ), + 'primary key' => array('accountid'), + ); +} + +function migrate_example_advanced_schema_blobs() { + return array( + 'description' => 'Wine blobs to be migrated to file entities', + 'fields' => array( + 'imageid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Image ID', + ), + 'imageblob' => array( + 'type' => 'blob', + 'size' => 'normal', + 'description' => 'binary image data', + ), + ), + 'primary key' => array('imageid'), + ); +} + +function migrate_example_advanced_schema_files() { + return array( + 'description' => 'Wine and account files', + 'fields' => array( + 'imageid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Image ID', + ), + 'url' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Image URL', + ), + 'image_alt' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Image alt', + ), + 'image_title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'Image title', + ), + 'wineid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'Wine node this is associated with', + ), + ), + 'primary key' => array('imageid'), + ); +} + +function migrate_example_advanced_schema_table_source() { + return array( + 'description' => 'Source data to go into a custom Drupal table', + 'fields' => array( + 'fooid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Primary key', + ), + 'field1' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'First field', + ), + 'field2' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Second field', + ), + ), + 'primary key' => array('fooid'), + ); +} + +function migrate_example_advanced_schema_table_dest() { + return array( + 'description' => 'Custom Drupal table to receive source data directly', + 'fields' => array( + 'recordid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Primary key', + ), + 'drupal_text' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'First field', + ), + 'drupal_int' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Second field', + ), + ), + 'primary key' => array('recordid'), + ); +} + +function migrate_example_advanced_data_wine() { + $fields = array('wineid', 'name', 'body', 'excerpt', 'accountid', + 'posted', 'last_changed', 'variety', 'region', 'rating'); + $query = db_insert('migrate_example_wine') + ->fields($fields); + $data = array( + array(1, 'Montes Classic Cabernet Sauvignon', 'Intense ruby-red color', 'Great!', 9, + strtotime('2010-01-02 03:04:05'), strtotime('2010-03-04 05:06:07'), 25, 17, 95), + array(2, 'Archeo Ruggero di Tasso Nero d\'Avola', 'Lots of berry character', 'Pair with red sauced dishes', 3, + strtotime('2010-09-03 18:23:58'), strtotime('2010-09-03 18:23:58'), 26, 2, 85), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_updates() { + $fields = array('wineid', 'rating'); + $query = db_insert('migrate_example_advanced_updates') + ->fields($fields); + $data = array( + array(1, 93), + array(2, NULL), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_producer() { + $fields = array('producerid', 'name', 'body', 'excerpt', 'accountid'); + $query = db_insert('migrate_example_advanced_producer') + ->fields($fields); + $data = array( + array(1, 'Montes', 'Fine Chilean winery', 'Great!', 9), + array(2, 'Archeo', 'Sicilia!', NULL, 3), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_account() { + $fields = array('accountid', 'status', 'posted', 'last_access', 'last_login', + 'name', 'sex', 'password', 'mail', 'original_mail', 'sig', 'imageid', 'positions'); + $query = db_insert('migrate_example_advanced_account') + ->fields($fields); + $data = array( + array(1, 1, '2010-03-30 10:31:05', '2010-04-30 18:25:24', '2010-04-30 14:01:02', + 'darren', 'M', 'dpass', 'ddarren@example.com', 'darren@example.com', + 'All about the Australians', NULL, '5'), + array(3, 0, '2007-03-15 10:31:05', '2007-06-10 04:11:38', '2007-06-10 04:11:38', + 'emily', 'F', 'insecure', 'emily@example.com', 'emily@example.com', + 'Sommelier to the stars', NULL, '18'), + array(9, 1, '2004-02-29 10:31:05', '2004-02-29 10:31:05', '2004-02-29 10:31:05', + 'fonzie', NULL, 'bike', 'thefonz@example.com', 'arthur@example.com', + 'Aaay!', 1, '5,18'), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_account_updates() { + $fields = array('accountid', 'sex'); + $query = db_insert('migrate_example_advanced_account_updates') + ->fields($fields); + $data = array( + array(1, NULL), + array(3, 'M'), + array(9, 'F'), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_comment() { + $fields = array('commentid', 'wineid', 'comment_parent', 'subject', 'body', + 'name', 'mail', 'accountid', 'commenthost', 'userpage', 'posted', 'lastchanged'); + $query = db_insert('migrate_example_advanced_comment') + ->fields($fields); + $data = array( + array(1, 1, NULL, 'im first', 'Tasty', 'grace', 'grace@example.com', 0, + '123.456.78.9', 'http:://grace.example.com/', + strtotime('2010-01-02 03:04:05'), strtotime('2010-04-05 06:07:08')), + array(2, 1, NULL, 'im second', 'Delicious', 'horace', 'horace@example.com', 0, + 'example.com', NULL, + strtotime('2010-02-02 03:04:05'), strtotime('2010-05-05 06:07:08')), + array(3, 1, NULL, 'im parent', 'Don\'t care for it', 'irene', 'irene@example.com', 0, + '254.0.2.5', 'http:://www.example.com/irene', + strtotime('2010-03-02 03:04:05'), strtotime('2010-03-02 03:04:05')), + array(4, 1, 3, 'im child', 'But it\'s so good!', 'emily', NULL, 3, + '58.29.126.1', 'http:://www.wine.com/', + strtotime('2010-01-02 03:04:05'), strtotime('2010-01-02 03:04:05')), + array(5, 1, 4, 'im grandchild', 'Right on, Emily!', 'thefonz@example.com', NULL, 9, + '123.456.78.9', NULL, + strtotime('2010-06-02 03:04:05'), strtotime('2010-06-02 03:04:05')), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_comment_updates() { + $fields = array('commentid', 'subject'); + $query = db_insert('migrate_example_advanced_comment_updates') + ->fields($fields); + $data = array( + array(1, 'I am first'), + array(2, 'I am second'), + array(3, 'I am parent'), + array(4, ''), + array(5, 'I am Spartacus'), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_categories() { + $fields = array('categoryid', 'type', 'name', 'category_parent', 'details', 'ordering'); + $query = db_insert('migrate_example_advanced_categories') + ->fields($fields); + $data = array( + array(1, 'variety', 'White wine', NULL, 'White wines are generally simpler and sweeter than red', 3), + array(3, 'variety', 'Red wine', NULL, 'Red wines are generally more complex and "dry" than white', 1), + array(8, 'variety', 'Riesling', 1, 'Associated with Germany', 2), + array(9, 'variety', 'Chardonnay', 1, 'One of the most popular whites', 1), + array(13, 'variety', 'Merlot', 3, 'Very drinkable', 4), + array(14, 'variety', 'Syrah', 3, 'A.k.a. shiraz', -3), + array(25, 'variety', 'Cabernet Sauvignon', 3, 'A basic', -5), + array(26, 'variety', "Nero d'Avola", 3, 'Sicilian specialty', 2), + array(2, 'region', 'Italy', NULL, 'Largest producer of wine', 5), + array(11, 'region', 'Tuscany', 2, NULL, 2), + array(18, 'region', 'Chianti', 11, NULL, -1), + array(19, 'region', 'Elba', 11, NULL, 5), + array(4, 'region', 'France', NULL, 'C\'est bon', 6), + array(5, 'region', 'Bordeaux', 4, NULL, 1), + array(6, 'region', 'Barsac', 5, NULL, 3), + array(7, 'region', 'Pomerol', 5, NULL, 2), + array(16, 'region', 'Chile', NULL, NULL, 3), + array(17, 'region', 'Colchagua Valley', 16, NULL, 1), + array(20, 'region', 'California', NULL, NULL, 5), + array(21, 'region', 'Redwood Valley', 20, NULL, 1), + array(10, 'best_with', 'Beef', NULL, NULL, 5), + array(12, 'best_with', 'Pork', NULL, NULL, -3), + array(15, 'best_with', 'Chicken', NULL, NULL, -5), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_vintages() { + $fields = array('wineid', 'vintage'); + $query = db_insert('migrate_example_advanced_vintages') + ->fields($fields); + $data = array( + array(1, 2006), + array(1, 2007), + array(2, 2001), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_variety_updates() { + $fields = array('categoryid', 'details'); + $query = db_insert('migrate_example_advanced_variety_updates') + ->fields($fields); + $data = array( + array(1, 'White wines are simpler and sweeter than red'), + array(3, 'Red wines are generally more complex and dry than white'), + array(8, 'Usually associated with Germany'), + array(9, NULL), + array(13, 'Common, very drinakable'), + array(14, 'AKA Shiraz'), + array(25, 'Basic'), + array(26, 'A specialty of Sicily'), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_category_wine() { + $fields = array('wineid', 'categoryid'); + $query = db_insert('migrate_example_advanced_category_wine') + ->fields($fields); + $data = array( + array(1, 12), + array(1, 15), + array(2, 10), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_category_producer() { + $fields = array('producerid', 'categoryid'); + $query = db_insert('migrate_example_advanced_category_producer') + ->fields($fields); + $data = array( + array(1, 17), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_files() { + $fields = array('imageid', 'url', 'image_alt', 'image_title', 'wineid'); + $query = db_insert('migrate_example_advanced_files') + ->fields($fields); + $data = array( + array(1, 'http://placekitten.com/200/200', NULL, NULL, NULL), + array(2, 'http://cyrve.com/files/penguin.jpeg', 'Penguin alt', 'Penguin title', 1), + array(3, 'http://cyrve.com/files/rioja.jpeg', 'Rioja alt', 'Rioja title', 2), + array(4, 'http://cyrve.com/files/boutisse_0.jpeg', 'Boutisse alt', 'Boutisse title', 2), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_blobs() { + $blob = file_get_contents('core/misc/druplicon.png'); + $fields = array('imageid', 'imageblob'); + $query = db_insert('migrate_example_advanced_blobs') + ->fields($fields); + $data = array( + array(1, $blob), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} + +function migrate_example_advanced_data_table_source() { + $fields = array('fooid', 'field1', 'field2'); + $query = db_insert('migrate_example_advanced_table_source') + ->fields($fields); + $data = array( + array(3, 'Some sample data', 58), + array(15, 'Whatever', 2), + array(646, 'More sample data', 34989), + ); + foreach ($data as $row) { + $query->values(array_combine($fields, $row)); + } + $query->execute(); +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/PositionResource.php b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/PositionResource.php new file mode 100644 index 000000000..20fe34aad --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/PositionResource.php @@ -0,0 +1,44 @@ + 'wine_taster', 'name' => 'Wine Taster']; + $position2 = ['sourceid' => 'vintner', 'name' => 'Vintner']; + $data = ['position' => [$position1, $position2]]; + + $response = new ResourceResponse($data, 200); + return $response; + } + + /** + * {@inheritdoc} + */ + public function permissions() { + // Remove permissions so the resource is available to all. + return []; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/VarietyItems.php b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/VarietyItems.php new file mode 100644 index 000000000..a14986036 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/VarietyItems.php @@ -0,0 +1,72 @@ + [ + 'name' => 'Retsina', + 'parent' => 1, // categoryid for 'white'. + 'details' => 'Greek', + ], + 'trebbiano' => [ + 'name' => 'Trebbiano', + 'parent' => 1, // categoryid for 'white'. + 'details' => 'Italian', + ], + 'valpolicella' => [ + 'name' => 'Valpolicella', + 'parent' => 3, // categoryid for 'red'. + 'details' => 'Italian Venoto region', + ], + 'bardolino' => [ + 'name' => 'Bardolino', + 'parent' => 3, // categoryid for 'red'. + 'details' => 'Italian Venoto region', + ], + ]; + if (isset($varieties[$variety])) { + $data = ['variety' => $varieties[$variety]]; + } + else { + $data = []; + } + + $response = new ResourceResponse($data, 200); + return $response; + } + + /** + * {@inheritdoc} + */ + public function permissions() { + // Remove permissions so the resource is available to all. + return []; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/VarietyList.php b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/VarietyList.php new file mode 100644 index 000000000..bb4bb60cb --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/VarietyList.php @@ -0,0 +1,42 @@ + 'Amarone', + 'parent' => 3, // categoryid for 'red'. + 'details' => 'Italian Venoto region', + 'attributes' => [ + 'rich', + 'aromatic', + ], + ]; + $data['variety'][] = [ + 'name' => 'Barbaresco', + 'parent' => 3, // categoryid for 'red'. + 'details' => 'Italian Piedmont region', + 'attributes' => [ + 'smoky', + 'earthy', + ], + ]; + } + if (strtolower($type) != 'red') { + $data['variety'][] = [ + 'name' => 'Kir', + 'parent' => 1, // categoryid for 'white'. + 'details' => 'French Burgundy region', + 'attributes' => [], + ]; + $data['variety'][] = [ + 'name' => 'Pinot Grigio', + 'parent' => 1, // categoryid for 'white'. + 'details' => 'From the northeast of Italy', + 'attributes' => [ + 'fruity', + 'medium-bodied', + 'slightly sweet', + ], + ]; + } + + $response = new ResourceResponse($data, 200); + return $response; + } + + /** + * {@inheritdoc} + */ + public function permissions() { + // Remove permissions so the resource is available to all. + return []; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/src/Plugin/migrate/source/WineTerm.php b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/src/Plugin/migrate/source/WineTerm.php new file mode 100644 index 000000000..4bc143fcd --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/src/Plugin/migrate/source/WineTerm.php @@ -0,0 +1,50 @@ +select('migrate_example_advanced_categories', 'wc') + ->fields('wc', ['categoryid', 'type', 'name', 'details', 'category_parent', 'ordering']) + // This sort assures that parents are saved before children. + ->orderBy('category_parent', 'ASC'); + } + + /** + * {@inheritdoc} + */ + public function fields() { + $fields = [ + 'categoryid' => $this->t('Unique ID of the category'), + 'type' => $this->t('Category type corresponding to Drupal vocabularies'), + 'name' => $this->t('Category name'), + 'details' => $this->t('Description of the category'), + 'category_parent' => $this->t('ID of the parent category'), + 'ordering' => $this->t('Order in which to display this category'), + ]; + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + return ['categoryid' => ['type' => 'integer']]; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.info.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.info.yml new file mode 100644 index 000000000..6bd94f71f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.info.yml @@ -0,0 +1,13 @@ +type: module +name: Migrate Plus +description: 'Enhancements to core migration support' +package: Migration +# core: 8.x +dependencies: + - drupal:migrate (>=8.3) + +# Information added by Drupal.org packaging script on 2017-05-10 +version: '8.x-4.0-beta1' +core: '8.x' +project: 'migrate_plus' +datestamp: 1494450189 diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.install b/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.install new file mode 100644 index 000000000..815b9876e --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.install @@ -0,0 +1,38 @@ +select('config', 'c') + ->fields('c', ['name', 'data']) + ->condition('name', 'migrate.migration.%', 'LIKE') + ->execute(); + foreach ($migration_config as $migration) { + $data = unserialize($migration->data); + if (isset($data['third_party_settings']['migrate_plus']['migration_group'])) { + $data['migration_group'] = $data['third_party_settings']['migrate_plus']['migration_group']; + unset($data['third_party_settings']['migrate_plus']); + if (empty($data['third_party_settings'])) { + unset($data['third_party_settings']); + } + $new_name = str_replace('migrate.migration.', 'migrate_plus.migration.', $migration->name); + $database->update('config') + ->fields([ + 'name' => $new_name, + 'data' => serialize($data), + ]) + ->condition('name', $migration->name) + ->execute(); + } + } +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.module b/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.module new file mode 100644 index 000000000..0dbddc473 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.module @@ -0,0 +1,87 @@ + $migration) { + // Add the default class where empty. + if (empty($migration['class'])) { + $migrations[$id]['class'] = 'Drupal\migrate\Plugin\Migration'; + } + + // For derived configuration entity-based migrations, strip the deriver + // prefix so we can reference migrations by the IDs they specify (i.e., + // the migration that specifies "id: temp" can be referenced as "temp" + // rather than "migration_config_deriver:temp"). + $prefix = 'migration_config_deriver:'; + if (strpos($id, $prefix) === 0) { + $new_id = substr($id, strlen($prefix)); + $migrations[$new_id] = $migrations[$id]; + unset($migrations[$id]); + $id = $new_id; + } + + // Integrate shared group configuration into the migration. + if (empty($migration['migration_group'])) { + $migration['migration_group'] = 'default'; + } + $group = MigrationGroup::load($migration['migration_group']); + if (empty($group)) { + // If the specified group does not exist, create it. Provide a little more + // for the 'default' group. + $group_properties = []; + $group_properties['id'] = $migration['migration_group']; + if ($migration['migration_group'] == 'default') { + $group_properties['label'] = 'Default'; + $group_properties['description'] = 'A container for any migrations not explicitly assigned to a group.'; + } + else { + $group_properties['label'] = $group_properties['id']; + $group_properties['description'] = ''; + } + $group = MigrationGroup::create($group_properties); + $group->save(); + } + $shared_configuration = $group->get('shared_configuration'); + if (empty($shared_configuration)) { + continue; + } + foreach ($shared_configuration as $key => $group_value) { + $migration_value = $migration[$key]; + // Where both the migration and the group provide arrays, replace + // recursively (so each key collision is resolved in favor of the + // migration). + if (is_array($migration_value) && is_array($group_value)) { + $merged_values = array_replace_recursive($group_value, $migration_value); + $migrations[$id][$key] = $merged_values; + } + // Where the group provides a value the migration doesn't, use the group + // value. + elseif (is_null($migration_value)) { + $migrations[$id][$key] = $group_value; + } + // Otherwise, the existing migration value overrides the group value. + } + } +} + +/** + * Implements hook_migrate_prepare_row(). + */ +function migrate_plus_migrate_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) { + \Drupal::service('event_dispatcher')->dispatch(MigrateEvents::PREPARE_ROW, new MigratePrepareRowEvent($row, $source, $migration)); +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.services.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.services.yml new file mode 100644 index 000000000..2ca3363c1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrate_plus.services.yml @@ -0,0 +1,10 @@ +services: + plugin.manager.migrate_plus.authentication: + class: Drupal\migrate_plus\AuthenticationPluginManager + parent: default_plugin_manager + plugin.manager.migrate_plus.data_fetcher: + class: Drupal\migrate_plus\DataFetcherPluginManager + parent: default_plugin_manager + plugin.manager.migrate_plus.data_parser: + class: Drupal\migrate_plus\DataParserPluginManager + parent: default_plugin_manager diff --git a/sites/all/modules/contrib/migrate/migrate_plus/migrations/migration_config_deriver.yml b/sites/all/modules/contrib/migrate/migrate_plus/migrations/migration_config_deriver.yml new file mode 100644 index 000000000..9fc597e7d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/migrations/migration_config_deriver.yml @@ -0,0 +1,6 @@ +id: migration_config_deriver +deriver: Drupal\migrate_plus\Plugin\MigrationConfigDeriver +# @todo: Remove if/when https://www.drupal.org/node/2797421 is fixed. +# Unused source configuration must be added to prevent errors. +source: + plugin: embedded_data diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Annotation/Authentication.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Annotation/Authentication.php new file mode 100644 index 000000000..910adb04c --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Annotation/Authentication.php @@ -0,0 +1,37 @@ +alterInfo('authentication_info'); + $this->setCacheBackend($cache_backend, 'migrate_plus_plugins_authentication'); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/DataFetcherPluginBase.php b/sites/all/modules/contrib/migrate/migrate_plus/src/DataFetcherPluginBase.php new file mode 100644 index 000000000..9e413fe10 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/DataFetcherPluginBase.php @@ -0,0 +1,32 @@ +alterInfo('data_fetcher_info'); + $this->setCacheBackend($cache_backend, 'migrate_plus_plugins_data_fetcher'); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/DataParserPluginBase.php b/sites/all/modules/contrib/migrate/migrate_plus/src/DataParserPluginBase.php new file mode 100644 index 000000000..a1e4c2dd3 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/DataParserPluginBase.php @@ -0,0 +1,220 @@ +urls = $configuration['urls']; + $this->itemSelector = $configuration['item_selector']; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition); + } + + /** + * Returns the initialized data fetcher plugin. + * + * @return \Drupal\migrate_plus\DataFetcherPluginInterface + * The data fetcher plugin. + */ + public function getDataFetcherPlugin() { + if (!isset($this->dataFetcherPlugin)) { + $this->dataFetcherPlugin = \Drupal::service('plugin.manager.migrate_plus.data_fetcher')->createInstance($this->configuration['data_fetcher_plugin'], $this->configuration); + } + return $this->dataFetcherPlugin; + } + + /** + * {@inheritdoc} + */ + public function rewind() { + $this->activeUrl = NULL; + $this->next(); + } + + /** + * Implementation of Iterator::next(). + */ + public function next() { + $this->currentItem = $this->currentId = NULL; + if (is_null($this->activeUrl)) { + if (!$this->nextSource()) { + // No data to import. + return; + } + } + // At this point, we have a valid open source url, try to fetch a row from + // it. + $this->fetchNextRow(); + // If there was no valid row there, try the next url (if any). + if (is_null($this->currentItem)) { + while ($this->nextSource()) { + $this->fetchNextRow(); + if ($this->valid()) { + break; + } + } + } + if ($this->valid()) { + foreach ($this->configuration['ids'] as $id_field_name => $id_info) { + $this->currentId[$id_field_name] = $this->currentItem[$id_field_name]; + } + } + } + + /** + * Opens the specified URL. + * + * @param $url + * URL to open. + * + * @return bool + * TRUE if the URL was successfully opened, FALSE otherwise. + */ + abstract protected function openSourceUrl($url); + + /** + * Retrieves the next row of data from the open source URL, populating + * currentItem. + */ + abstract protected function fetchNextRow(); + + /** + * Advances the data parser to the next source url. + * + * @return bool + * TRUE if a valid source URL was opened + */ + protected function nextSource() { + while ($this->activeUrl === NULL || (count($this->urls) - 1) > $this->activeUrl) { + if (is_null($this->activeUrl)) { + $this->activeUrl = 0; + } + else { + // Increment the activeUrl so we try to load the next source. + $this->activeUrl = $this->activeUrl + 1; + if ($this->activeUrl >= count($this->urls)) { + return FALSE; + } + } + + if ($this->openSourceUrl($this->urls[$this->activeUrl])) { + // We have a valid source. + return TRUE; + } + } + + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function current() { + return $this->currentItem; + } + + /** + * {@inheritdoc} + */ + public function key() { + return $this->currentId; + } + + /** + * {@inheritdoc} + */ + public function valid() { + return !empty($this->currentItem); + } + + /** + * {@inheritdoc} + */ + public function count() { + $count = 0; + foreach ($this as $item) { + $count++; + } + return $count; + } + + /** + * Return the selectors used to populate each configured field. + * + * @return string[] + * Array of selectors, keyed by field name. + */ + protected function fieldSelectors() { + $fields = []; + foreach ($this->configuration['fields'] as $field_info) { + if (isset($field_info['selector'])) { + $fields[$field_info['name']] = $field_info['selector']; + } + } + return $fields; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/DataParserPluginInterface.php b/sites/all/modules/contrib/migrate/migrate_plus/src/DataParserPluginInterface.php new file mode 100644 index 000000000..838ca9d36 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/DataParserPluginInterface.php @@ -0,0 +1,15 @@ +alterInfo('data_parser_info'); + $this->setCacheBackend($cache_backend, 'migrate_plus_plugins_data_parser'); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/Migration.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/Migration.php new file mode 100644 index 000000000..6359825cb --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/Migration.php @@ -0,0 +1,83 @@ +createInstance($plugin_id); + $entity_array['id'] = $new_plugin_id; + $entity_array['migration_tags'] = $migration_plugin->get('migration_tags'); + $entity_array['label'] = $migration_plugin->label(); + $entity_array['source'] = $migration_plugin->getSourceConfiguration(); + $entity_array['destination'] = $migration_plugin->getDestinationConfiguration(); + $entity_array['process'] = $migration_plugin->getProcess(); + $entity_array['migration_dependencies'] = $migration_plugin->getMigrationDependencies(); + $migration_entity = static::create($entity_array); + return $migration_entity; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/MigrationGroup.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/MigrationGroup.php new file mode 100644 index 000000000..519dbe241 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/MigrationGroup.php @@ -0,0 +1,77 @@ +condition('migration_group', $this->id()); + $names = $query->execute(); + + // Order the migrations according to their dependencies. + /** @var MigrationInterface[] $migrations */ + $migrations = \Drupal::entityTypeManager()->getStorage('migration')->loadMultiple($names); + + // Delete in reverse order, so dependencies are never violated. + $migrations = array_reverse($migrations); + + foreach ($migrations as $migration) { + $migration->delete(); + } + + // Finally, delete the group itself. + parent::delete(); + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + // Make sure we save any explicit module dependencies. + if ($provider = $this->get('module')) { + $this->addDependency('module', $provider); + } + return $this->dependencies; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/MigrationGroupInterface.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/MigrationGroupInterface.php new file mode 100644 index 000000000..9dde2f2e4 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Entity/MigrationGroupInterface.php @@ -0,0 +1,12 @@ +row = $row; + $this->source = $source; + $this->migration = $migration; + } + + /** + * Gets the row object. + * + * @return \Drupal\migrate\Row + * The row object about to be imported. + */ + public function getRow() { + return $this->row; + } + + /** + * Gets the source plugin. + * + * @return \Drupal\migrate\Plugin\MigrateSourceInterface $source + * The source plugin firing the event. + */ + public function getSource() { + return $this->source; + } + + /** + * Gets the migration plugin. + * + * @return \Drupal\migrate\Plugin\MigrationInterface + * The migration entity being imported. + */ + public function getMigration() { + return $this->migration; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/MigrationConfigDeriver.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/MigrationConfigDeriver.php new file mode 100644 index 000000000..013544497 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/MigrationConfigDeriver.php @@ -0,0 +1,28 @@ +derivatives = []; + $migrations = Migration::loadMultiple(); + /** @var \Drupal\migrate_plus\Entity\MigrationInterface $migration */ + foreach ($migrations as $id => $migration) { + $this->derivatives[$id] = $migration->toArray(); + } + return $this->derivatives; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/destination/Table.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/destination/Table.php new file mode 100755 index 000000000..64a6e8889 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/destination/Table.php @@ -0,0 +1,128 @@ +dbConnection = $connection; + $this->tableName = $configuration['table_name']; + $this->idFields = $configuration['id_fields']; + $this->fields = isset($configuration['fields']) ? $configuration['fields'] : []; + $this->supportsRollback = TRUE; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { + $db_key = !empty($configuration['database_key']) ? $configuration['database_key'] : NULL; + + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $migration, + Database::getConnection('default', $db_key) + ); + } + + /** + * {@inheritdoc} + */ + public function getIds() { + if (empty($this->idFields)) { + throw new MigrateException('Id fields are required for a table destination'); + } + return $this->idFields; + } + + /** + * {@inheritdoc} + */ + public function fields(MigrationInterface $migration = NULL) { + return $this->fields; + } + + /** + * {@inheritdoc} + */ + public function import(Row $row, array $old_destination_id_values = []) { + $id = $row->getSourceIdValues(); + if (count($id) != count($this->idFields)) { + throw new MigrateSkipProcessException('All the id fields are required for a table migration.'); + } + + $values = $row->getDestination(); + + if ($this->fields) { + $values = array_intersect_key($values, $this->fields); + } + + $status = $this->dbConnection->merge($this->tableName) + ->key($id) + ->fields($values) + ->execute(); + + return $status ? $id : NULL; + } + + /** + * {@inheritdoc} + */ + public function rollback(array $destination_identifier) { + $delete = $this->dbConnection->delete($this->tableName); + foreach ($destination_identifier as $field => $value) { + $delete->condition($field, $value); + } + $delete->execute(); + } +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/EntityGenerate.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/EntityGenerate.php new file mode 100644 index 000000000..642ce0287 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/EntityGenerate.php @@ -0,0 +1,101 @@ +generateEntity($value); + } + + return $result; + } + + /** + * Generates an entity for a given value. + * + * @param string $value + * Value to use in creation of the entity. + * + * @return int|string + * The entity id of the generated entity. + */ + protected function generateEntity($value) { + if (!empty($value)) { + $entity = $this->entityManager + ->getStorage($this->lookupEntityType) + ->create($this->entity($value)); + $entity->save(); + + return $entity->id(); + } + } + + /** + * Fabricate an entity. + * + * This is intended to be extended by implementing classes to provide for more + * dynamic default values, rather than just static ones. + * + * @param $value + * Primary value to use in creation of the entity. + * + * @return array + * Entity value array. + */ + protected function entity($value) { + $entity_values = [$this->lookupValueKey => $value]; + + if ($this->lookupBundleKey) { + $entity_values[$this->lookupBundleKey] = $this->lookupBundle; + } + + // Gather any static default values for properties/fields. + if (isset($this->configuration['default_values']) && is_array($this->configuration['default_values'])) { + foreach ($this->configuration['default_values'] as $key => $value) { + $entity_values[$key] = $value; + } + } + + return $entity_values; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/EntityLookup.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/EntityLookup.php new file mode 100644 index 000000000..abd341426 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/EntityLookup.php @@ -0,0 +1,249 @@ +migration = $migration; + $this->entityManager = $entityManager; + $this->selectionPluginManager = $selectionPluginManager; + $pluginIdParts = explode(':', $this->migration->getDestinationPlugin()->getPluginId()); + $this->destinationEntityType = empty($pluginIdParts[1]) ?: $pluginIdParts[1]; + $this->destinationBundleKey = !$this->destinationEntityType ?: $this->entityManager->getDefinition($this->destinationEntityType)->getKey('bundle'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration = NULL) { + return new static( + $configuration, + $pluginId, + $pluginDefinition, + $migration, + $container->get('entity.manager'), + $container->get('plugin.manager.entity_reference_selection') + ); + } + + /** + * {@inheritdoc} + */ + public function transform($value, MigrateExecutableInterface $migrateExecutable, Row $row, $destinationProperty) { + // In case of subfields ('field_reference/target_id'), extract the field + // name only. + $parts = explode('/', $destinationProperty); + $destinationProperty = reset($parts); + $this->determineLookupProperties($destinationProperty); + + $this->destinationProperty = isset($this->configuration['destination_field']) ? $this->configuration['destination_field'] : NULL; + + return $this->query($value); + } + + /** + * Determine the lookup properties from config or target field configuration. + * + * @param string $destinationProperty + * The destination property currently worked on. This is only used together + * with the $row above. + */ + protected function determineLookupProperties($destinationProperty) { + if (!empty($this->configuration['value_key'])) { + $this->lookupValueKey = $this->configuration['value_key']; + } + if (!empty($this->configuration['bundle_key'])) { + $this->lookupBundleKey = $this->configuration['bundle_key']; + } + if (!empty($this->configuration['bundle'])) { + $this->lookupBundle = $this->configuration['bundle']; + } + if (!empty($this->configuration['entity_type'])) { + $this->lookupEntityType = $this->configuration['entity_type']; + } + + if (empty($this->lookupValueKey) || empty($this->lookupBundleKey) || empty($this->lookupBundle) || empty($this->lookupEntityType)) { + // See if we can introspect the lookup properties from the destination field. + if (!empty($this->migration->getProcess()[$this->destinationBundleKey][0]['default_value'])) { + $destinationEntityBundle = $this->migration->getProcess()[$this->destinationBundleKey][0]['default_value']; + $fieldConfig = $this->entityManager->getFieldDefinitions($this->destinationEntityType, $destinationEntityBundle)[$destinationProperty]->getConfig($destinationEntityBundle); + switch ($fieldConfig->getType()) { + case 'entity_reference': + if (empty($this->lookupBundle)) { + $handlerSettings = $fieldConfig->getSetting('handler_settings'); + $bundles = array_filter((array) $handlerSettings['target_bundles']); + if (count($bundles) == 1) { + $this->lookupBundle = reset($bundles); + } + // This was added in 8.1.x is not supported in 8.0.x. + elseif (!empty($handlerSettings['auto_create']) && !empty($handlerSettings['auto_create_bundle'])) { + $this->lookupBundle = reset($handlerSettings['auto_create_bundle']); + } + } + + // Make an assumption that if the selection handler can target more than + // one type of entity that we will use the first entity type. + $this->lookupEntityType = $this->lookupEntityType ?: reset($this->selectionPluginManager->createInstance($fieldConfig->getSetting('handler'))->getPluginDefinition()['entity_types']); + $this->lookupValueKey = $this->lookupValueKey ?: $this->entityManager->getDefinition($this->lookupEntityType)->getKey('label'); + $this->lookupBundleKey = $this->lookupBundleKey ?: $this->entityManager->getDefinition($this->lookupEntityType)->getKey('bundle'); + break; + + case 'file': + case 'image': + $this->lookupEntityType = 'file'; + $this->lookupValueKey = $this->lookupValueKey ?: 'uri'; + break; + + default: + throw new MigrateException('Destination field type ' . + $fieldConfig->getType(). 'is not a recognized reference type.'); + } + } + } + + // If there aren't enough lookup properties available by now, then bail. + if (empty($this->lookupValueKey)) { + throw new MigrateException('The entity_lookup plugin requires a value_key, none located.'); + } + if (!empty($this->lookupBundleKey) && empty($this->lookupBundle)) { + throw new MigrateException('The entity_lookup plugin found no bundle but destination entity requires one.'); + } + if (empty($this->lookupEntityType)) { + throw new MigrateException('The entity_lookup plugin requires a entity_type, none located.'); + } + } + + /** + * Checks for the existence of some value. + * + * @param $value + * The value to query. + * + * @return mixed|null + * Entity id if the queried entity exists. Otherwise NULL. + */ + protected function query($value) { + // Entity queries typically are case-insensitive. Therefore, we need to + // handle case sensitive filtering as a post-query step. By default, it + // filters case insensitive. Change to true if that is not the desired + // outcome. + $ignoreCase = !empty($this->configuration['ignore_case']) ?: FALSE; + + $multiple = is_array($value); + + $query = $this->entityManager->getStorage($this->lookupEntityType) + ->getQuery() + ->condition($this->lookupValueKey, $value, $multiple ? 'IN' : NULL); + + if ($this->lookupBundleKey) { + $query->condition($this->lookupBundleKey, $this->lookupBundle); + } + $results = $query->execute(); + + if (empty($results)) { + return NULL; + } + + // By default do a case-sensitive comparison. + if (!$ignoreCase) { + // Returns the entity's identifier. + foreach ($results as $k => $identifier) { + $result_value = $this->entityManager->getStorage($this->lookupEntityType)->load($identifier)->{$this->lookupValueKey}->value; + if (($multiple && !in_array($result_value, $value, TRUE)) || (!$multiple && $result_value !== $value)) { + unset($results[$k]); + } + } + } + + if ($multiple && !empty($this->destinationProperty)) { + array_walk($results, function (&$value) { + $value = [$this->destinationProperty => $value]; + }); + } + + return $multiple ? array_values($results) : reset($results); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/FileBlob.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/FileBlob.php new file mode 100644 index 000000000..cd838cb87 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/FileBlob.php @@ -0,0 +1,155 @@ + FALSE, + ); + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->fileSystem = $file_system; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('file_system') + ); + } + + /** + * {@inheritdoc} + */ + public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) { + // If we're stubbing a file entity, return a URI of NULL so it will get + // stubbed by the general process. + if ($row->isStub()) { + return NULL; + } + list($destination, $blob) = $value; + + // Determine if we going to overwrite existing files or not touch them. + $replace = $this->getOverwriteMode(); + + // Attempt to save the file to avoid calling file_prepare_directory() any + // more than absolutely necessary. + if ($this->putFile($destination, $blob, $replace)) { + return $destination; + } + $dir = $this->getDirectory($destination); + if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) { + throw new MigrateSkipProcessException("Could not create directory '$dir'"); + } + if ($this->putFile($destination, $blob, $replace)) { + return $destination; + } + throw new MigrateSkipProcessException("Blob data could not be copied to $destination."); + } + + /** + * Try to save the file. + * + * @param string $destination + * The destination path or URI. + * @param string $blob + * The base64 encoded file contents. + * @param int $replace + * (optional) FILE_EXISTS_REPLACE (default) or FILE_EXISTS_ERROR, depending + * on the configuration. + * + * @return bool|string + * File path on success, FALSE on failure. + */ + protected function putFile($destination, $blob, $replace = FILE_EXISTS_REPLACE) { + if ($path = file_destination($destination, $replace)) { + if (file_put_contents($path, $blob)) { + return $path; + } + else { + return FALSE; + } + } + + // File was already copied. + return $destination; + } + + /** + * Determines how to handle file conflicts. + * + * @return int + * Either FILE_EXISTS_REPLACE (default) or FILE_EXISTS_ERROR, depending on + * the configuration. + */ + protected function getOverwriteMode() { + if (!empty($this->configuration['reuse'])) { + return FILE_EXISTS_ERROR; + } + + return FILE_EXISTS_REPLACE; + } + + /** + * Returns the directory component of a URI or path. + * + * For URIs like public://foo.txt, the full physical path of public:// + * will be returned, since a scheme by itself will trip up certain file + * API functions (such as file_prepare_directory()). + * + * @param string $uri + * The URI or path. + * + * @return string|false + * The directory component of the path or URI, or FALSE if it could not + * be determined. + */ + protected function getDirectory($uri) { + $dir = $this->fileSystem->dirname($uri); + if (substr($dir, -3) == '://') { + return $this->fileSystem->realpath($dir); + } + return $dir; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/Merge.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/Merge.php new file mode 100644 index 000000000..e646ca739 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/process/Merge.php @@ -0,0 +1,68 @@ +configuration['value']) && !array_key_exists('value', $this->configuration)) { + throw new MigrateException('Skip on value plugin is missing value configuration.'); + } + + if (is_array($this->configuration['value'])) { + foreach ($this->configuration['value'] as $skipValue) { + if ($this->compareValue($value, $skipValue, !isset($this->configuration['not_equals']))) { + throw new MigrateSkipRowException(); + } + } + } + elseif ($this->compareValue($value, $this->configuration['value'], !isset($this->configuration['not_equals']))) { + throw new MigrateSkipRowException(); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function process($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) { + if (empty($this->configuration['value']) && !array_key_exists('value', $this->configuration)) { + throw new MigrateException('Skip on value plugin is missing value configuration.'); + } + + if (is_array($this->configuration['value'])) { + foreach ($this->configuration['value'] as $skipValue) { + if ($this->compareValue($value, $skipValue, !isset($this->configuration['not_equals']))) { + throw new MigrateSkipProcessException(); + } + } + } + elseif ($this->compareValue($value, $this->configuration['value'], !isset($this->configuration['not_equals']))) { + throw new MigrateSkipProcessException(); + } + + return $value; + } + + /** + * Compare values to see if they are equal. + * + * @param $value + * Actual value + * @param $skipValue + * Value to compare against. + * @param $equal + * Compare as equal or not equal. + * + * @return bool + * True if the compare successfully, FALSE otherwise. + */ + protected function compareValue($value, $skipValue, $equal = TRUE) { + if ($equal) { + return (string) $value == (string) $skipValue; + } + + return (string) $value != (string) $skipValue; + + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/source/SourcePluginExtension.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/source/SourcePluginExtension.php new file mode 100644 index 000000000..19ca6a9a2 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/source/SourcePluginExtension.php @@ -0,0 +1,60 @@ +fields = $configuration['fields']; + $this->ids = $configuration['ids']; + } + + /** + * {@inheritdoc} + */ + public function fields() { + $fields = []; + foreach ($this->fields as $field_info) { + $fields[$field_info['name']] = isset($field_info['label']) ? $field_info['label'] : $field_info['name']; + } + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + return $this->ids; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/source/Url.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/source/Url.php new file mode 100644 index 000000000..92f6d449d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate/source/Url.php @@ -0,0 +1,139 @@ +sourceUrls = $configuration['urls']; + + // Set a default Accept header. +/* $this->headers = array_merge(['Accept' => 'application/json'], + $configuration['headers'] ?: []);*/ + + // See if this is a paged response with next links. If so, add to the source_urls array. +/* foreach ( (array) $configuration['urls'] as $url) { + $this->sourceUrls += $this->getNextLinks($url); + }*/ + } + + /** + * Return a string representing the source URLs. + * + * @return string + * Comma-separated list of URLs being imported. + */ + public function __toString() { + // This could cause a problem when using a lot of urls, may need to hash. + $urls = implode(', ', $this->sourceUrls); + return $urls; + } + + /** + * Returns the initialized data parser plugin. + * + * @return \Drupal\migrate_plus\DataParserPluginInterface + * The data parser plugin. + */ + public function getDataParserPlugin() { + if (!isset($this->dataParserPlugin)) { + $this->dataParserPlugin = \Drupal::service('plugin.manager.migrate_plus.data_parser')->createInstance($this->configuration['data_parser_plugin'], $this->configuration); + } + return $this->dataParserPlugin; + } + + /** + * Creates and returns a filtered Iterator over the documents. + * + * @return \Iterator + * An iterator over the documents providing source rows that match the + * configured item_selector. + */ + protected function initializeIterator() { + return $this->getDataParserPlugin(); + } + + /** + * Collect an array of next links from a paged response. + */ +/* protected function getNextLinks($url) { + $urls = array(); + $more = TRUE; + while ($more == TRUE) { + $response = $this->dataParserPlugin->getDataFetcher()->getResponse($url); + if ($url = $this->getNextFromHeaders($response)) { + $urls[] = $url; + } + elseif ($url = $this->getNextFromLinks($response)) { + $urls[] = $url; + } + else { + $more = FALSE; + } + } + return $urls; + } +*/ + /** + * See if the next link is in a 'links' group in the response. + * + * @param \Psr\Http\Message\ResponseInterface $response + */ +/* protected function getNextFromLinks(ResponseInterface $response) { + $body = json_decode($response->getBody(), TRUE); + if (!empty($body['links']) && array_key_exists('next', $body['links'])) { + return $body['links']['next']; + } + return FALSE; + } +*/ + /** + * See if the next link is in the header. + * + * @param \Psr\Http\Message\ResponseInterface $response + */ +/* protected function getNextFromHeaders(ResponseInterface $response) { + $headers = $response->getHeader('Link'); + foreach ($headers as $header) { + $matches = array(); + preg_match('/^<(.*)>; rel="next"$/', $header, $matches); + if (!empty($matches) && !empty($matches[1])) { + return $matches[1]; + } + } + return FALSE; + } +*/ +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/Basic.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/Basic.php new file mode 100755 index 000000000..f1ca69ab6 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/Basic.php @@ -0,0 +1,30 @@ + [ + $this->configuration['username'], + $this->configuration['password'], + ], + ]; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/Digest.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/Digest.php new file mode 100755 index 000000000..246653430 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/Digest.php @@ -0,0 +1,31 @@ + [ + $this->configuration['username'], + $this->configuration['password'], + 'digest', + ], + ]; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/OAuth2.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/OAuth2.php new file mode 100755 index 000000000..7415e72d1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/authentication/OAuth2.php @@ -0,0 +1,69 @@ + $handlerStack, + 'base_uri' => $this->configuration['base_uri'], + 'auth' => 'oauth2', + ]); + + switch ($this->configuration['grant_type']) { + case 'authorization_code': + $grant_type = new AuthorizationCode($client, $this->configuration); + break; + case 'client_credentials': + $grant_type = new ClientCredentials($client, $this->configuration); + break; + case 'urn:ietf:params:oauth:grant-type:jwt-bearer': + $grant_type = new JwtBearer($client, $this->configuration); + break; + case 'password': + $grant_type = new PasswordCredentials($client, $this->configuration); + break; + case 'refresh_token': + $grant_type = new RefreshToken($client, $this->configuration); + break; + default: + throw new MigrateException("Unrecognized grant_type {$this->configuration['grant_type']}."); + break; + } + $middleware = new OAuthMiddleware($client, $grant_type); + + return [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $middleware->getAccessToken()->getToken(), + ], + ]; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_fetcher/File.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_fetcher/File.php new file mode 100644 index 000000000..459c454bc --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_fetcher/File.php @@ -0,0 +1,52 @@ +getResponse($url); + return $response; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_fetcher/Http.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_fetcher/Http.php new file mode 100755 index 000000000..7402126a9 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_fetcher/Http.php @@ -0,0 +1,104 @@ +httpClient = \Drupal::httpClient(); + } + + /** + * Returns the initialized authentication plugin. + * + * @return \Drupal\migrate_plus\AuthenticationPluginInterface + * The authentication plugin. + */ + public function getAuthenticationPlugin() { + if (!isset($this->authenticationPlugin)) { + $this->authenticationPlugin = \Drupal::service('plugin.manager.migrate_plus.authentication')->createInstance($this->configuration['authentication']['plugin'], $this->configuration['authentication']); + } + return $this->authenticationPlugin; + } + + /** + * {@inheritdoc} + */ + public function setRequestHeaders(array $headers) { + $this->headers = $headers; + } + + /** + * {@inheritdoc} + */ + public function getRequestHeaders() { + return !empty($this->headers) ? $this->headers : []; + } + + /** + * {@inheritdoc} + */ + public function getResponse($url) { + try { + $options = ['headers' => $this->getRequestHeaders()]; + if (!empty($this->configuration['authentication'])) { + $options = array_merge($options, $this->getAuthenticationPlugin()->getAuthenticationOptions()); + } + $response = $this->httpClient->get($url, $options); + if (empty($response)) { + throw new MigrateException('No response at ' . $url . '.'); + } + } + catch (RequestException $e) { + throw new MigrateException('Error message: ' . $e->getMessage() . ' at ' . $url .'.'); + } + return $response; + } + + /** + * {@inheritdoc} + */ + public function getResponseContent($url) { + $response = $this->getResponse($url); + return $response->getBody(); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Json.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Json.php new file mode 100755 index 000000000..c94444bc8 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Json.php @@ -0,0 +1,133 @@ +getDataFetcherPlugin()->getResponseContent($url); + + // Convert objects to associative arrays. + $source_data = json_decode($response, TRUE); + + // If json_decode() has returned NULL, it might be that the data isn't + // valid utf8 - see http://php.net/manual/en/function.json-decode.php#86997. + if (is_null($source_data)) { + $utf8response = utf8_encode($response); + $source_data = json_decode($utf8response); + } + + // Backwards-compatibility for depth selection. + if (is_int($this->itemSelector)) { + return $this->selectByDepth($source_data); + } + + // Otherwise, we're using xpath-like selectors. + $selectors = explode('/', trim($this->itemSelector, '/')); + foreach ($selectors as $selector) { + if (!empty($selector)) { + $source_data = $source_data[$selector]; + } + } + return $source_data; + } + + /** + * Get the source data for reading. + * + * @param array $raw_data + * Raw data from the JSON feed. + * + * @return array + * Selected items at the requested depth of the JSON feed. + */ + protected function selectByDepth(array $raw_data) { + // Return the results in a recursive iterator that can traverse + // multidimensional arrays. + $iterator = new \RecursiveIteratorIterator( + new \RecursiveArrayIterator($raw_data), + \RecursiveIteratorIterator::SELF_FIRST); + $items = []; + // Backwards-compatibility - an integer item_selector is interpreted as a + // depth. When there is an array of items at the expected depth, pull that + // array out as a distinct item. + $identifierDepth = $this->itemSelector; + $iterator->rewind(); + while ($iterator->valid()) { + $item = $iterator->current(); + if (is_array($item) && $iterator->getDepth() == $identifierDepth) { + $items[] = $item; + } + $iterator->next(); + } + return $items; + } + + /** + * {@inheritdoc} + */ + protected function openSourceUrl($url) { + // (Re)open the provided URL. + $source_data = $this->getSourceData($url); + $this->iterator = new \ArrayIterator($source_data); + return TRUE; + } + + /** + * {@inheritdoc} + */ + protected function fetchNextRow() { + $current = $this->iterator->current(); + if ($current) { + foreach ($this->fieldSelectors() as $field_name => $selector) { + $field_data = $current; + $field_selectors = explode('/', trim($selector, '/')); + foreach ($field_selectors as $field_selector) { + $field_data = $field_data[$field_selector]; + } + $this->currentItem[$field_name] = $field_data; + } + if (!empty($this->configuration['include_raw_data'])) { + $this->currentItem['raw'] = $current; + } + $this->iterator->next(); + } + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/SimpleXml.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/SimpleXml.php new file mode 100644 index 000000000..a33b6a086 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/SimpleXml.php @@ -0,0 +1,87 @@ +getDataFetcherPlugin()->getResponseContent($url); + $xml = simplexml_load_string($xml_data); + $this->registerNamespaces($xml); + $xpath = $this->configuration['item_selector']; + $this->matches = $xml->xpath($xpath); + foreach (libxml_get_errors() as $error) { + $error_string = self::parseLibXmlError($error); + throw new MigrateException($error_string); + } + return TRUE; + } + + /** + * {@inheritdoc} + */ + protected function fetchNextRow() { + $target_element = array_shift($this->matches); + + // If we've found the desired element, populate the currentItem and + // currentId with its data. + if ($target_element !== FALSE && !is_null($target_element)) { + foreach ($this->fieldSelectors() as $field_name => $xpath) { + foreach ($target_element->xpath($xpath) as $value) { + if ($value->children() && !trim((string) $value)) { + $this->currentItem[$field_name] = $value; + } + else { + $this->currentItem[$field_name][] = (string) $value; + } + } + } + // Reduce single-value results to scalars. + foreach ($this->currentItem as $field_name => $values) { + if (count($values) == 1) { + $this->currentItem[$field_name] = reset($values); + } + } + } + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Soap.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Soap.php new file mode 100755 index 000000000..60db90204 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Soap.php @@ -0,0 +1,123 @@ +function = $configuration['function']; + $this->parameters = $configuration['parameters']; + $this->responseType = $configuration['response_type']; + } + + /** + * {@inheritdoc} + * + * @throws \SoapFault + * If there's an error in a SOAP call. + * @throws \Drupal\migrate\MigrateException + * If we can't resolve the SOAP function or its response property. + */ + protected function openSourceUrl($url) { + // Will throw SoapFault if there's + $client = new \SoapClient($url); + // Determine the response property name. + $function_found = FALSE; + foreach ($client->__getFunctions() as $function_signature) { + // E.g., "GetWeatherResponse GetWeather(GetWeather $parameters)". + $response_type = strtok($function_signature, ' '); + $function_name = strtok('('); + if (strcasecmp($function_name, $this->function) === 0) { + $function_found = TRUE; + foreach ($client->__getTypes() as $type_info) { + // E.g., "struct GetWeatherResponse {\n string GetWeatherResult;\n}". + if (preg_match('|struct (.*?) {\s*[a-z]+ (.*?);|is', $type_info, $matches)) { + if ($matches[1] == $response_type) { + $response_property = $matches[2]; + } + } + } + break; + } + } + if (!$function_found) { + throw new MigrateException("SOAP function {$this->function} not found."); + } + elseif (!isset($response_property)) { + throw new MigrateException("Response property not found for SOAP function {$this->function}."); + } + $response = $client->{$this->function}($this->parameters); + $response_value = $response->$response_property; + switch ($this->responseType) { + case 'xml': + $xml = simplexml_load_string($response_value); + $this->iterator = new \ArrayIterator($xml->xpath($this->itemSelector)); + break; + case 'object': + $this->iterator = new \ArrayIterator($response_value->{$this->itemSelector}); + break; + case 'array': + $this->iterator = new \ArrayIterator($response_value[$this->itemSelector]); + break; + } + return TRUE; + } + + /** + * {@inheritdoc} + */ + protected function fetchNextRow() { + $current = $this->iterator->current(); + if ($current) { + foreach ($this->fieldSelectors() as $field_name => $selector) { + $this->currentItem[$field_name] = $current->$selector; + } + $this->iterator->next(); + } + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Xml.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Xml.php new file mode 100644 index 000000000..6c0806927 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/Xml.php @@ -0,0 +1,321 @@ + 'file', 1 => 'article']. + * + * @var array + */ + protected $elementsToMatch = []; + + /** + * An optional xpath predicate. + * + * Restricts the matching elements based on values in their children. Parsed + * from the element query at construct time. + * + * @var string + */ + protected $xpathPredicate = NULL; + + /** + * Array representing the path to the current element as we traverse the XML. + * + * For example, if in an XML string like '
...
' + * we are positioned within the article element, currentPath will be + * [0 => 'file', 1 => 'article']. + * + * @var array + */ + protected $currentPath = []; + + /** + * Retains all elements with a given name to support extraction from parents. + * + * This is a hack to support field extraction of values in parents + * of the 'context node' - ie, if $this->fields() has something like '..\nid'. + * Since we are using a streaming xml processor, it is too late to snoop + * around parent elements again once we've located an element of interest. So, + * grab elements with matching names and their depths, and refer back to it + * when building the source row. + * + * @var array + */ + protected $parentXpathCache = []; + + /** + * Hash of the element names that should be captured into $parentXpathCache. + * + * @var array + */ + protected $parentElementsOfInterest = []; + + /** + * Element name matching mode. + * + * When matching element names, whether to compare to the namespace-prefixed + * name, or the local name. + * + * @var bool + */ + protected $prefixedName = FALSE; + + /** + * {@inheritdoc} + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + + $this->reader = new \XMLReader(); + + // Suppress errors during parsing, so we can pick them up after. + libxml_use_internal_errors(TRUE); + + // Parse the element query. First capture group is the element path, second + // (if present) is the attribute. + preg_match_all('|^/([^\[]+)\[?(.*?)]?$|', $configuration['item_selector'], $matches); + $element_path = $matches[1][0]; + $this->elementsToMatch = explode('/', $element_path); + $predicate = $matches[2][0]; + if ($predicate) { + $this->xpathPredicate = $predicate; + } + + // If the element path contains any colons, it must be specifying + // namespaces, so we need to compare using the prefixed element + // name in next(). + if (strpos($element_path, ':')) { + $this->prefixedName = TRUE; + } + + foreach ($this->fieldSelectors() as $field_name => $xpath) { + $prefix = substr($xpath, 0, 3); + if ($prefix === '../') { + $this->parentElementsOfInterest[] = str_replace('../', '', $xpath); + } + elseif ($prefix === '..\\') { + $this->parentElementsOfInterest[] = str_replace('..\\', '', $xpath); + } + } + } + + /** + * Builds a \SimpleXmlElement rooted at the iterator's current location. + * + * The resulting SimpleXmlElement also contains any child nodes of the current + * element. + * + * @return \SimpleXmlElement|false + * A \SimpleXmlElement when the document is parseable, or false if a + * parsing error occurred. + * + * @throws MigrateException + */ + protected function getSimpleXml() { + $node = $this->reader->expand(); + if ($node) { + // We must associate the DOMNode with a DOMDocument to be able to import + // it into SimpleXML. Despite appearances, this is almost twice as fast as + // simplexml_load_string($this->readOuterXML()); + $dom = new \DOMDocument(); + $node = $dom->importNode($node, TRUE); + $dom->appendChild($node); + $sxml_elem = simplexml_import_dom($node); + $this->registerNamespaces($sxml_elem); + return $sxml_elem; + } + else { + foreach (libxml_get_errors() as $error) { + $error_string = self::parseLibXmlError($error); + throw new MigrateException($error_string); + } + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function rewind() { + // Reset our path tracker. + $this->currentPath = []; + parent::rewind(); + } + + /** + * {@inheritdoc} + */ + protected function openSourceUrl($url) { + // (Re)open the provided URL. + $this->reader->close(); + + // Clear XML error buffer. Other Drupal code that executed during the + // migration may have polluted the error buffer and could create false + // positives in our error check below. We are only concerned with errors + // that occur from attempting to load the XML string into an object here. + libxml_clear_errors(); + + return $this->reader->open($url, NULL, \LIBXML_NOWARNING); + } + + /** + * {@inheritdoc} + */ + protected function fetchNextRow() { + $target_element = NULL; + + // Loop over each node in the XML file, looking for elements at a path + // matching the input query string (represented in $this->elementsToMatch). + while ($this->reader->read()) { + if ($this->reader->nodeType == \XMLReader::ELEMENT) { + if ($this->prefixedName) { + $this->currentPath[$this->reader->depth] = $this->reader->name; + if (in_array($this->reader->name, $this->parentElementsOfInterest)) { + $this->parentXpathCache[$this->reader->depth][$this->reader->name][] = $this->getSimpleXml(); + } + } + else { + $this->currentPath[$this->reader->depth] = $this->reader->localName; + if (in_array($this->reader->localName, $this->parentElementsOfInterest)) { + $this->parentXpathCache[$this->reader->depth][$this->reader->name][] = $this->getSimpleXml(); + } + } + if ($this->currentPath == $this->elementsToMatch) { + // We're positioned to the right element path - build the SimpleXML + // object to enable proper xpath predicate evaluation. + $target_element = $this->getSimpleXml(); + if ($target_element !== FALSE) { + if (empty($this->xpathPredicate) || $this->predicateMatches($target_element)) { + break; + } + } + } + } + elseif ($this->reader->nodeType == \XMLReader::END_ELEMENT) { + // Remove this element and any deeper ones from the current path. + foreach ($this->currentPath as $depth => $name) { + if ($depth >= $this->reader->depth) { + unset($this->currentPath[$depth]); + } + } + foreach ($this->parentXpathCache as $depth => $elements) { + if ($depth > $this->reader->depth) { + unset($this->parentXpathCache[$depth]); + } + } + } + } + + // If we've found the desired element, populate the currentItem and + // currentId with its data. + if ($target_element !== FALSE && !is_null($target_element)) { + foreach ($this->fieldSelectors() as $field_name => $xpath) { + $prefix = substr($xpath, 0, 3); + if (in_array($prefix, ['../', '..\\'])) { + $name = str_replace($prefix, '', $xpath); + $up = substr_count($xpath, $prefix); + $values = $this->getAncestorElements($up, $name); + } + else { + $values = $target_element->xpath($xpath); + } + foreach ($values as $value) { + // If the SimpleXMLElement doesn't render to a string of any sort, + // and has children then return the whole object for the process + // plugin or other row manipulation. + if ($value->children() && !trim((string) $value)) { + $this->currentItem[$field_name] = $value; + } + else { + $this->currentItem[$field_name][] = (string) $value; + } + } + } + // Reduce single-value results to scalars. + foreach ($this->currentItem as $field_name => $values) { + if (count($values) == 1) { + $this->currentItem[$field_name] = reset($values); + } + } + } + } + + /** + * Tests whether the iterator's xpath predicate matches the provided element. + * + * Has some limitations esp. in that it is easy to write predicates that + * reference things outside this SimpleXmlElement's tree, but "simpler" + * predicates should work as expected. + * + * @param \SimpleXMLElement $elem + * The element to test. + * + * @return bool + * True if the element matches the predicate, false if not. + */ + protected function predicateMatches(\SimpleXMLElement $elem) { + return !empty($elem->xpath('/*[' . $this->xpathPredicate . ']')); + } + + /** + * Gets an ancestor SimpleXMLElement, if the element name was registered. + * + * Gets the SimpleXMLElement some number of levels above the iterator + * having the given name, but only for element names that this + * Xml data parser was told to retain for future reference through the + * constructor's $parent_elements_of_interest. + * + * @param int $levels_up + * The number of levels back towards the root of the DOM tree to ascend + * before searching for the named element. + * @param string $name + * The name of the desired element. + * + * @return \SimpleXMLElement|false + * The element matching the level and name requirements, or false if it is + * not present or was not retained. + */ + public function getAncestorElements($levels_up, $name) { + if ($levels_up > 0) { + $levels_up *= -1; + } + $ancestor_depth = $this->reader->depth + $levels_up + 1; + if ($ancestor_depth < 0) { + return FALSE; + } + + if (array_key_exists($ancestor_depth, $this->parentXpathCache) && array_key_exists($name, $this->parentXpathCache[$ancestor_depth])) { + return $this->parentXpathCache[$ancestor_depth][$name]; + } + else { + return FALSE; + } + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/XmlTrait.php b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/XmlTrait.php new file mode 100644 index 000000000..e1a8aafb0 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/src/Plugin/migrate_plus/data_parser/XmlTrait.php @@ -0,0 +1,65 @@ +configuration['namespaces']) && is_array($this->configuration['namespaces'])) { + foreach ($this->configuration['namespaces'] as $prefix => $ns) { + $xml->registerXPathNamespace($prefix, $ns); + } + } + } + + /** + * Parses a LibXMLError to a error message string. + * + * @param \LibXMLError $error + * Error thrown by the XML. + * + * @return string + * Error message + */ + public static function parseLibXmlError(\LibXMLError $error) { + $error_code_name = 'Unknown Error'; + switch ($error->level) { + case LIBXML_ERR_WARNING: + $error_code_name = t('Warning'); + break; + + case LIBXML_ERR_ERROR: + $error_code_name = t('Error'); + break; + + case LIBXML_ERR_FATAL: + $error_code_name = t('Fatal Error'); + break; + } + + return t( + "@libxmlerrorcodename @libxmlerrorcode: @libxmlerrormessage\n" . + "Line: @libxmlerrorline\n" . + "Column: @libxmlerrorcolumn\n" . + "File: @libxmlerrorfile", + [ + '@libxmlerrorcodename' => $error_code_name, + '@libxmlerrorcode' => $error->code, + '@libxmlerrormessage' => trim($error->message), + '@libxmlerrorline' => $error->line, + '@libxmlerrorcolumn' => $error->column, + '@libxmlerrorfile' => (($error->file)) ? $error->file : NULL, + ] + ); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrateTableTest.php b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrateTableTest.php new file mode 100755 index 000000000..56c8b7ada --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrateTableTest.php @@ -0,0 +1,136 @@ +connection = Database::getConnection(); + + $this->connection->schema()->createTable(static::TABLE_NAME, [ + 'description' => 'Test table', + 'fields' => [ + 'data' => [ + 'type' => 'varchar', + 'length' => '32', + 'not null' => TRUE, + ], + 'data2' => [ + 'type' => 'varchar', + 'length' => '32', + 'not null' => TRUE, + ], + 'data3' => [ + 'type' => 'varchar', + 'length' => '32', + 'not null' => TRUE, + ], + ], + 'primary key' => ['data'], + ]); + } + + protected function tearDown() { + $this->connection->schema()->dropTable(static::TABLE_NAME); + parent::tearDown(); + } + + protected function getTableDestinationMigration() { + // Create a minimally valid migration with some source data. + $definition = [ + 'id' => 'migration_table_test', + 'migration_tags' => ['Testing'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => [ + ['data' => 'dummy value', 'data2' => 'dummy2 value', 'data3' => 'dummy3 value'], + ['data' => 'dummy value2', 'data2' => 'dummy2 value2', 'data3' => 'dummy3 value2'], + ['data' => 'dummy value3', 'data2' => 'dummy2 value3', 'data3' => 'dummy3 value3'], + ], + 'ids' => [ + 'data' => ['type' => 'string'], + ], + ], + 'destination' => [ + 'plugin' => 'table', + 'table_name' => static::TABLE_NAME, + 'id_fields' => ['data' => ['type' => 'string']], + ], + 'process' => [ + 'data' => 'data', + 'data2' => 'data2', + 'data3' => 'data3', + ], + ]; + return $definition; + } + + /** + * Tests table destination. + */ + public function testTableDestination() { + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($this->getTableDestinationMigration()); + + $executable = new MigrateExecutable($migration, $this); + $executable->import(); + + $values = $this->connection->select(static::TABLE_NAME) + ->fields(static::TABLE_NAME) + ->execute() + ->fetchAllAssoc('data'); + + $this->assertEquals('dummy value', $values['dummy value']->data); + $this->assertEquals('dummy2 value', $values['dummy value']->data2); + $this->assertEquals('dummy2 value2', $values['dummy value2']->data2); + $this->assertEquals('dummy3 value3', $values['dummy value3']->data3); + $this->assertEquals(3, count($values)); + } + + public function testTableRollback() { + $this->testTableDestination(); + + /** @var MigrationInterface $migration */ + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($this->getTableDestinationMigration()); + $executable = new MigrateExecutable($migration, $this); + $executable->import(); + + $values = $this->connection->select(static::TABLE_NAME) + ->fields(static::TABLE_NAME) + ->execute() + ->fetchAllAssoc('data'); + + $this->assertEquals('dummy value', $values['dummy value']->data); + $this->assertEquals(3, count($values)); + + // Now rollback. + $executable->rollback(); + $values = $this->connection->select(static::TABLE_NAME) + ->fields(static::TABLE_NAME) + ->execute() + ->fetchAllAssoc('data'); + + $this->assertEquals(0, count($values)); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrationConfigEntityTest.php b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrationConfigEntityTest.php new file mode 100644 index 000000000..386363a36 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrationConfigEntityTest.php @@ -0,0 +1,52 @@ +pluginManager = \Drupal::service('plugin.manager.migration'); + } + + public function testCacheInvalidation() { + $config = Migration::create([ + 'id' => 'test', + 'label' => 'Label A', + 'migration_tags' => [], + 'source' => [], + 'destination' => [], + 'migration_dependencies' => [], + ]); + $config->save(); + + $this->assertTrue($this->pluginManager->getDefinition('test')); + $this->assertSame('Label A', $this->pluginManager->getDefinition('test')['label']); + + // Clear static cache in the plugin manager, the cache tag take care of the + // persistent cache. + $this->pluginManager->useCaches(FALSE); + $this->pluginManager->useCaches(TRUE); + + $config->set('label', 'Label B'); + $config->save(); + + $this->assertSame('Label B', $this->pluginManager->getDefinition('test')['label']); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrationGroupTest.php b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrationGroupTest.php new file mode 100644 index 000000000..4cdd55253 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/MigrationGroupTest.php @@ -0,0 +1,121 @@ + $group_id, + 'shared_configuration' => [ + 'migration_tags' => ['Drupal 6'], // In migration, so will be overridden. + 'source' => [ + 'constants' => [ + 'type' => 'image', // Not in migration, so will be added. + 'cardinality' => '1', // In migration, so will be overridden. + ], + ], + 'destination' => ['plugin' => 'field_storage_config'], // Not in migration, so will be added. + ], + ]; + $this->container->get('entity_type.manager')->getStorage('migration_group') + ->create($group_configuration)->save(); + + /** @var \Drupal\migrate_plus\Entity\MigrationInterface $migration */ + $migration = $this->container->get('entity_type.manager') + ->getStorage('migration')->create([ + 'id' => 'specific_migration', + 'load' => [], + 'migration_group' => $group_id, + 'label' => 'Unaffected by the group', + 'migration_tags' => ['Drupal 7'], // Overrides group. + 'destination' => [], + 'source' => [], + 'process' => [], + 'migration_dependencies' => [], + ]); + $migration->set('source', [ + 'plugin' => 'empty', // Not in group, persists. + 'constants' => [ + 'entity_type' => 'user', // Not in group, persists. + 'cardinality' => '3', // Overrides group. + ], + ]); + $migration->save(); + + $expected_config = [ + 'migration_group' => $group_id, + 'label' => 'Unaffected by the group', + 'migration_tags' => ['Drupal 7'], + 'source' => [ + 'plugin' => 'empty', + 'constants' => [ + 'entity_type' => 'user', + 'type' => 'image', + 'cardinality' => '3', + ], + ], + 'destination' => ['plugin' => 'field_storage_config'], + ]; + /** @var \Drupal\migrate\Plugin\MigrationInterface $loaded_migration */ + $loaded_migration = $this->container->get('plugin.manager.migration') + ->createInstance('specific_migration'); + foreach ($expected_config as $key => $expected_value) { + $actual_value = $loaded_migration->get($key); + $this->assertEquals($expected_value, $actual_value); + } + } + + /** + * Test that deleting a group deletes its migrations. + */ + public function testDelete() { + /** @var MigrationGroupInterface $migration_group */ + $group_configuration = [ + 'id' => 'test_group', + ]; + $migration_group = $this->container->get('entity_type.manager') + ->getStorage('migration_group')->create($group_configuration); + $migration_group->save(); + + /** @var \Drupal\migrate_plus\Entity\MigrationInterface $migration */ + $migration = $this->container->get('entity_type.manager') + ->getStorage('migration')->create([ + 'id' => 'specific_migration', + 'migration_group' => 'test_group', + 'migration_tags' => [], + 'load' => [], + 'destination' => [], + 'source' => [], + 'migration_dependencies' => [], + ]); + $migration->save(); + + /** @var \Drupal\migrate_plus\Entity\MigrationGroupInterface $loaded_migration_group */ + $loaded_migration_group = MigrationGroup::load('test_group'); + $loaded_migration_group->delete(); + + /** @var \Drupal\migrate_plus\Entity\MigrationInterface $loaded_migration */ + $loaded_migration = Migration::load('specific_migration'); + $this->assertNull($loaded_migration); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/Plugin/migrate/process/EntityGenerateTest.php b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/Plugin/migrate/process/EntityGenerateTest.php new file mode 100644 index 000000000..c7a340a5f --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Kernel/Plugin/migrate/process/EntityGenerateTest.php @@ -0,0 +1,328 @@ + $this->bundle, + 'name' => 'Page', + ]; + $node_type = NodeType::create($values); + $node_type->save(); + + $this->installEntitySchema('node'); + $this->installEntitySchema('taxonomy_term'); + $this->installEntitySchema('taxonomy_vocabulary'); + $this->installEntitySchema('user'); + $this->installSchema('system', ['sequences']); + $this->installSchema('user', 'users_data'); + $this->installConfig($this->modules); + + // Create a vocabulary. + $vocabulary = Vocabulary::create([ + 'name' => $this->vocabulary, + 'description' => $this->vocabulary, + 'vid' => $this->vocabulary, + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ]); + $vocabulary->save(); + + // Create a field. + $this->createEntityReferenceField( + 'node', + $this->bundle, + $this->fieldName, + 'Term reference', + 'taxonomy_term', + 'default', + ['target_bundles' => [$this->vocabulary]], + FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED + ); + + $this->migrationPluginManager = \Drupal::service('plugin.manager.migration'); + } + + /** + * Tests generating an entity. + * + * @dataProvider transformDataProvider + * + * @covers ::transform + */ + public function testTransform(array $definition, array $expected, array $preSeed = []) { + // Pre seed some test data. + foreach ($preSeed as $storageName => $values) { + /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */ + $storage = $this->container + ->get('entity_type.manager') + ->getStorage($storageName); + $entity = $storage->create($values); + $entity->save(); + } + + /** @var Migration $migration */ + $migration = $this->migrationPluginManager->createStubMigration($definition); + /** @var EntityStorageBase $storage */ + $storage = $this->readAttribute($migration->getDestinationPlugin(), 'storage'); + $migrationExecutable = (new MigrateExecutable($migration, $this)); + $migrationExecutable->import(); + + foreach ($expected as $row) { + $entity = $storage->load($row['id']); + $properties = array_diff_key($row, array_flip(['id'])); + foreach ($properties as $property => $value) { + if (is_array($value)) { + foreach ($value as $key => $expectedValue) { + if (empty($expectedValue)) { + $this->assertEmpty($entity->{$property}->getValue(), "Expected value is empty but field $property is not empty."); + } + elseif ($entity->{$property}->getValue()) { + $this->assertEquals($expectedValue, $entity->{$property}[0]->entity->$key->value); + } + else { + $this->fail("Expected value: $expectedValue does not exist in $property."); + } + } + } + else { + $this->assertNotEmpty($entity, 'Entity with label ' . $row[$property] .' is empty'); + $this->assertEquals($row[$property], $entity->label()); + } + } + } + } + + /** + * Provides multiple migration definitions for "transform" test. + */ + public function transformDataProvider() { + return [ + 'no arguments' => [ + 'definition' => [ + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => [ + [ + 'id' => 1, + 'title' => 'content item 1', + 'term' => 'Apples', + ], + [ + 'id' => 2, + 'title' => 'content item 2', + 'term' => 'Bananas', + ], + [ + 'id' => 3, + 'title' => 'content item 3', + 'term' => 'Grapes', + ], + ], + 'ids' => [ + 'id' => ['type' => 'integer'], + ], + ], + 'process' => [ + 'id' => 'id', + 'type' => [ + 'plugin' => 'default_value', + 'default_value' => $this->bundle, + ], + 'title' => 'title', + $this->fieldName => [ + 'plugin' => 'entity_generate', + 'source' => 'term', + ], + ], + 'destination' => [ + 'plugin' => 'entity:node', + ], + ], + 'expected' => [ + 'row 1' => [ + 'id' => 1, + 'title' => 'content item 1', + $this->fieldName => [ + 'tid' => 2, + 'name' => 'Apples', + ], + ], + 'row 2' => [ + 'id' => 2, + 'title' => 'content item 2', + $this->fieldName => [ + 'tid' => 3, + 'name' => 'Bananas', + ], + ], + 'row 3' => [ + 'id' => 3, + 'title' => 'content item 3', + $this->fieldName => [ + 'tid' => 1, + 'name' => 'Grapes', + ], + ], + ], + 'pre seed' => [ + 'taxonomy_term' => [ + 'name' => 'Grapes', + 'vid' => $this->vocabulary, + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ], + ], + ], + 'no arguments_lookup_only' => [ + 'definition' => [ + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => [ + [ + 'id' => 1, + 'title' => 'content item 1', + 'term' => 'Apples', + ], + [ + 'id' => 2, + 'title' => 'content item 2', + 'term' => 'Bananas', + ], + [ + 'id' => 3, + 'title' => 'content item 3', + 'term' => 'Grapes', + ], + ], + 'ids' => [ + 'id' => ['type' => 'integer'], + ], + ], + 'process' => [ + 'id' => 'id', + 'type' => [ + 'plugin' => 'default_value', + 'default_value' => $this->bundle, + ], + 'title' => 'title', + $this->fieldName => [ + 'plugin' => 'entity_lookup', + 'source' => 'term', + ], + ], + 'destination' => [ + 'plugin' => 'entity:node', + ], + ], + 'expected' => [ + 'row 1' => [ + 'id' => 1, + 'title' => 'content item 1', + $this->fieldName => [ + 'tid' => NULL, + 'name' => NULL, + ], + ], + 'row 2' => [ + 'id' => 2, + 'title' => 'content item 2', + $this->fieldName => [ + 'tid' => NULL, + 'name' => NULL, + ], + ], + 'row 3' => [ + 'id' => 3, + 'title' => 'content item 3', + $this->fieldName => [ + 'tid' => 1, + 'name' => 'Grapes', + ], + ], + ], + 'pre seed' => [ + 'taxonomy_term' => [ + 'name' => 'Grapes', + 'vid' => $this->vocabulary, + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function display($message, $type = 'status') { + $this->assertTrue($type == 'status', $message); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Unit/process/SkipOnValueTest.php b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Unit/process/SkipOnValueTest.php new file mode 100644 index 000000000..aebcf6494 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_plus/tests/src/Unit/process/SkipOnValueTest.php @@ -0,0 +1,112 @@ +transform('86', $this->migrateExecutable, $this->row, 'destinationproperty'); + } + + /** + * @covers ::process + * @expectedException \Drupal\migrate\MigrateSkipProcessException + */ + public function testProcessSkipsOnMultipleValue() { + $configuration['method'] = 'process'; + $configuration['value'] = [1, 1, 2, 3, 5, 8]; + (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform('5', $this->migrateExecutable, $this->row, 'destinationproperty'); + } + + /** + * @covers ::process + */ + public function testProcessBypassesOnNonValue() { + $configuration['method'] = 'process'; + $configuration['value'] = 'sourcevalue'; + $configuration['not_equals'] = TRUE; + $value = (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform('sourcevalue', $this->migrateExecutable, $this->row, 'destinationproperty'); + $this->assertEquals($value, 'sourcevalue'); + $configuration['value'] = 86; + $value = (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform('86', $this->migrateExecutable, $this->row, 'destinationproperty'); + $this->assertEquals($value, '86'); + } + + /** + * @covers ::process + */ + public function testProcessSkipsOnMultipleNonValue() { + $configuration['method'] = 'process'; + $configuration['value'] = [1, 1, 2, 3, 5, 8]; + $value = (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform(4, $this->migrateExecutable, $this->row, 'destinationproperty'); + $this->assertEquals($value, '4'); + } + + /** + * @covers ::row + * @expectedException \Drupal\migrate\MigrateSkipRowException + */ + public function testRowSkipsOnValue() { + $configuration['method'] = 'row'; + $configuration['value'] = 86; + (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform('86', $this->migrateExecutable, $this->row, 'destinationproperty'); + } + + /** + * @covers ::row + */ + public function testRowBypassesOnNonValue() { + $configuration['method'] = 'row'; + $configuration['value'] = 'sourcevalue'; + $configuration['not_equals'] = TRUE; + $value = (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform('sourcevalue', $this->migrateExecutable, $this->row, 'destinationproperty'); + $this->assertEquals($value, 'sourcevalue'); + $configuration['value'] = 86; + $value = (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform('86', $this->migrateExecutable, $this->row, 'destinationproperty'); + $this->assertEquals($value, 86); + } + + /** + * @covers ::row + * @expectedException \Drupal\migrate\MigrateException + */ + public function testRequiredRowConfiguration() { + $configuration['method'] = 'row'; + (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform('sourcevalue', $this->migrateExecutable, $this->row, 'destinationproperty'); + } + + /** + * @covers ::process + * @expectedException \Drupal\migrate\MigrateException + */ + public function testRequiredProcessConfiguration() { + $configuration['method'] = 'process'; + (new SkipOnValue($configuration, 'skip_on_value', [])) + ->transform('sourcevalue', $this->migrateExecutable, $this->row, 'destinationproperty'); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/LICENSE.txt b/sites/all/modules/contrib/migrate/migrate_tools/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/LICENSE.txt @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/sites/all/modules/contrib/migrate/migrate_tools/README.txt b/sites/all/modules/contrib/migrate/migrate_tools/README.txt new file mode 100644 index 000000000..5f46ba08e --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/README.txt @@ -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. diff --git a/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.drush.inc b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.drush.inc new file mode 100644 index 000000000..f982987ac --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.drush.inc @@ -0,0 +1,513 @@ + '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 : []; +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.info.yml b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.info.yml new file mode 100644 index 000000000..9f35d9c2d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.info.yml @@ -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 diff --git a/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.action.yml b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.action.yml new file mode 100644 index 000000000..869297cf7 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.action.yml @@ -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 diff --git a/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.menu.yml b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.menu.yml new file mode 100644 index 000000000..d55ba56c1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.menu.yml @@ -0,0 +1,5 @@ +migrate_tools.menu: + title: Migrations + parent: system.admin_structure + description: Manage migration processes. + route_name: entity.migration_group.list diff --git a/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.task.yml b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.task.yml new file mode 100644 index 000000000..415d1780a --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.links.task.yml @@ -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 diff --git a/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.module b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.module new file mode 100644 index 000000000..5a93e8434 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.module @@ -0,0 +1,33 @@ +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'); +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.permissions.yml b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.permissions.yml new file mode 100644 index 000000000..b5301e5c1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.permissions.yml @@ -0,0 +1,3 @@ +'administer migrations': + title: 'Administer migrations' + description: Create, edit, and manage migration processed. diff --git a/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.routing.yml b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.routing.yml new file mode 100644 index 000000000..b12735a28 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/migrate_tools.routing.yml @@ -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' diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MessageController.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MessageController.php new file mode 100644 index 000000000..54a194fb8 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MessageController.php @@ -0,0 +1,144 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationController.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationController.php new file mode 100644 index 000000000..09ae154ad --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationController.php @@ -0,0 +1,262 @@ +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('

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.

'), + '#attributes' => [ + 'id' => 'migration-detail-source', + ], + ]; + + $source = $migration->getSourcePlugin(); + $build['source']['query'] = [ + '#type' => 'item', + '#title' => $this->t('Query'), + '#markup' => '
' . Xss::filterAdmin($source) . '
', + ]; + $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('

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.

'), + '#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; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationGroupListBuilder.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationGroupListBuilder.php new file mode 100644 index 000000000..6757c22b1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationGroupListBuilder.php @@ -0,0 +1,68 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationListBuilder.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationListBuilder.php new file mode 100644 index 000000000..16ee84f32 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Controller/MigrationListBuilder.php @@ -0,0 +1,211 @@ +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); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/DrushLogMigrateMessage.php b/sites/all/modules/contrib/migrate/migrate_tools/src/DrushLogMigrateMessage.php new file mode 100644 index 000000000..9ed1d9fb0 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/DrushLogMigrateMessage.php @@ -0,0 +1,23 @@ +t('Create Migration'); + unset($actions['submit']); + return $actions; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationDeleteForm.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationDeleteForm.php new file mode 100644 index 000000000..b28fa9be3 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationDeleteForm.php @@ -0,0 +1,71 @@ +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()); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationEditForm.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationEditForm.php new file mode 100644 index 000000000..144c74739 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationEditForm.php @@ -0,0 +1,51 @@ +getRouteParameters() + array('migration_group' => $migration_group); + $url->setRouteParameters($route_parameters); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationFormBase.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationFormBase.php new file mode 100644 index 000000000..8eb8841cb --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationFormBase.php @@ -0,0 +1,195 @@ +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 :url', [ + ':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'))); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupAddForm.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupAddForm.php new file mode 100644 index 000000000..3efbf8c88 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupAddForm.php @@ -0,0 +1,37 @@ +t('Create Migration Group'); + return $actions; + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupDeleteForm.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupDeleteForm.php new file mode 100644 index 000000000..ac047f83d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupDeleteForm.php @@ -0,0 +1,71 @@ +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()); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupEditForm.php b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupEditForm.php new file mode 100644 index 000000000..ee52359cd --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/Form/MigrationGroupEditForm.php @@ -0,0 +1,37 @@ +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'); + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_tools/src/MigrateExecutable.php b/sites/all/modules/contrib/migrate/migrate_tools/src/MigrateExecutable.php new file mode 100644 index 000000000..e7437a769 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_tools/src/MigrateExecutable.php @@ -0,0 +1,365 @@ + 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); + } + + } + +} diff --git a/sites/all/modules/contrib/migrate/migrate_upgrade/LICENSE.txt b/sites/all/modules/contrib/migrate/migrate_upgrade/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_upgrade/LICENSE.txt @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/sites/all/modules/contrib/migrate/migrate_upgrade/README.txt b/sites/all/modules/contrib/migrate/migrate_upgrade/README.txt new file mode 100644 index 000000000..cf5ee51cf --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_upgrade/README.txt @@ -0,0 +1,87 @@ +The migrate_upgrade module provides drush support for performing upgrades from +previous versions of Drupal to Drupal 8. It implements two drush commands: + +* migrate-upgrade - performs a complete import of the source site's configuration +and content into the target Drupal 8 site. Optionally, with the --configure-only +flag, it may create migration configurations for such an import without actually +running them, to permit customization of the import process. + +* migrate-upgrade-rollback - removes content and certain configuration +previously imported either by the migrate-upgrade command or by the core +upgrade UI. + +migrate-upgrade +=============== +The upgrade command requires a Drupal 6-style database URL of the source site's +database, and the location of the source site's public files. + +drush migrate-upgrade --legacy-db-url=mysql://user:pw@127.0.0.1/d6db --legacy-root=http://example.com + +The --legacy-root option may be either the domain of your existing Drupal site +(with the public files pulled by HTTP), or a local file directory into which you +have copied the files directory from your source site. + +If your source site used a database prefix for all tables, you may specify the +prefix with --legacy-db-prefix. Migration from sites with partial or mixed +prefixing is not supported. Note that if the source site is stored in a Postgres +schema, you must set the prefix to the schema with a period appended (e.g., +--legacy-db-prefix=drupal.). + +The migrate-upgrade command, like the core upgrade UI, is designed to be run on +a freshly installed and empty Drupal 8 site (where the only site configuration +that has been done is enabling any modules for which you wish to migrate data). + +migrate-upgrade-rollback +======================== + +The rollback command has no arguments or options: + +drush migrate-upgrade-rollback + +If it detects that an upgrade has been performed, either by migrate-upgrade or +by the core UI, it removes all content imported via the migration process (it +identifies the upgrade by the presence of the migrate_drupal_ui.performed state +key). In addition, any configuration entites created by the migration process +(such as content type and field definitions) are also removed. Because simple +configuration settings (such as the site title) are generally modified rather +than created by the upgrade process, and the original values are not preserved, +those changes are not rolled back. To completely return to the previous state, +you need to restore the site from backup, or reinstall a fresh empty site. + +migrate-upgrade --configure-only +================================ +At the time of this release, tools have not yet been developed (along the lines +of the migrate_d2d_ui module under Drupal 7) for customizing Drupal-to-Drupal +migrations in Drupal 8. For now, the best option short of doing custom +development is to use the --configure-only option on migrate-upgrade to replace +the actual execution of the migrations with export of their configuration to +configuration entities, which can then be modified as needed for a particular +migration scenario. A suggested workflow: + +1. Install a fresh empty D8 site, enabling all modules for which you wish to + migrate data. +2. Run the drush migrate-upgrade command with the --configure-only option. This + generates migration configuration entities in the D8 database (config table). +3. Create a custom module containing only a .info.yml file (with dependencies on + migrate_plus and migrate_drupal) and a config/install directory. +4. Export your site configuration, e.g. drush cex --destination=/tmp/export +5. Copy the migration configuration that was generated by migrate-upgrade into + the custom module - be sure *not* to copy the default group configuration, + which is defined by migrate_plus: + cp /tmp/export/migrate_plus.migration.* /tmp/export/migrate_plus.migration_group.migrate_*.yml migrate_custom/config/install/ +6. Look at that migrate_plus.migration_group.* file - you'll see your database + configuration captured there. In most cases, what you'll want to do is define + your database connection in settings.php with those credentials under the key + that is configured there - you won't want to commit the credentials to your + git repo. +7. Edit the generated .yml files to reflect your custom migration path. +8. Reinstall D8, enable your custom module and migrate_tools, and proceed to + work with your Drupal migrations as you would with any custom migration. + Hint: you'll probably want config_devel so you can edit .yml files in + config/install and immediately test your changes. + +Note that the configuration entities generated above need to be prefixed to +avoid conflict with the core migration plugins they originated from. For +example, by default the core d6_user plugin generates the upgrade_d6_user +configuration entity. You may modify the 'upgrade_' prefix by providing a +--migration-prefix option. diff --git a/sites/all/modules/contrib/migrate/migrate_upgrade/composer.json b/sites/all/modules/contrib/migrate/migrate_upgrade/composer.json new file mode 100644 index 000000000..cb5dd87ec --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_upgrade/composer.json @@ -0,0 +1,11 @@ +{ + "name": "drupal/migrate_upgrade", + "description": "Drush support for direct upgrades from older Drupal versions.", + "type": "drupal-module", + "require-dev": { + "drupal/coder": "^8" + }, + "license": "GPL-2.0+", + "require": {}, + "minimum-stability": "dev" +} diff --git a/sites/all/modules/contrib/migrate/migrate_upgrade/migrate_upgrade.drush.inc b/sites/all/modules/contrib/migrate/migrate_upgrade/migrate_upgrade.drush.inc new file mode 100644 index 000000000..ef5404d31 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_upgrade/migrate_upgrade.drush.inc @@ -0,0 +1,93 @@ + 'Perform one or more upgrade processes.', + 'options' => [ + 'legacy-db-url' => 'A Drupal 6 style database URL. Required if you do not set legacy-db-key.', + 'legacy-db-key' => 'A database connection key from settings.php. Use as an alternative to legacy-db-url', + 'legacy-db-prefix' => 'Prefix of the legacy Drupal installation.', + 'legacy-root' => 'Site address or root of the legacy Drupal installation', + 'configure-only' => 'Set up the appropriate upgrade processes but do not perform them', + 'migration-prefix' => 'With configure-only, a prefix to apply to generated migration ids. Defaults to \'upgrade_\'', + ], + 'examples' => [ + 'migrate-upgrade --legacy-db-url=\'mysql://root:pass@127.0.0.1/d6\'' => 'Upgrade a Drupal 6 database to Drupal 8', + 'migrate-upgrade --legacy-db-key=\'drupal_7\'' => 'Upgrade Drupal 7 database where the connection to Drupal 7 has already been created in settings.php ($databases[\'drupal_7\'])', + 'migrate-upgrade --legacy-db-url=\'mysql://root:pass@127.0.0.1/d7\' --configure-only --migration-prefix=d7_custom_' => 'Generate migrations for a custom migration from Drupal 7 to Drupal 8', + ], + 'drupal dependencies' => ['migrate_upgrade'], + ]; + $items['migrate-upgrade-rollback'] = [ + 'description' => 'Rolls back and removes upgrade migrations.', + 'examples' => [ + 'migrate-upgrade-rollback' => 'Rolls back a previously-run upgrade', + ], + 'drupal dependencies' => ['migrate_upgrade'], + ]; + + return $items; +} + +/** + * Execute the upgrade command, configuring the necessary migrations and + * optionally perform the imports. + */ +function drush_migrate_upgrade() { + $runner = new MigrateUpgradeDrushRunner(); + + try { + $runner->configure(); + if (drush_get_option('configure-only')) { + $runner->export(); + } + else { + $runner->import(); + \Drupal::state()->set('migrate_drupal_ui.performed', REQUEST_TIME); + } + // Remove the global database state. + \Drupal::state()->delete('migrate.fallback_state_key'); + } + catch (\Exception $e) { + drush_log($e->getMessage(), 'error'); + } +} + +/** + * Rolls back any upgrade migrations that are present, and deletes the migrations + * themselves. + */ +function drush_migrate_upgrade_rollback() { + if ($date_performed = \Drupal::state()->get('migrate_drupal_ui.performed')) { + if (drush_confirm(dt('All migrations tagged as \'Drupal\' will be rolled back. Are you sure?'))) { + $runner = new MigrateUpgradeDrushRunner(); + + try { + drush_log(dt('Rolling back the upgrades performed @date', + ['@date' => \Drupal::service('date.formatter')->format($date_performed)])); + $runner->rollback(); + \Drupal::state()->delete('migrate_drupal_ui.performed'); + drush_log(dt('Rolled back upgrades')); + } + catch (\Exception $e) { + drush_log($e->getMessage(), 'error'); + } + } + else { + drush_user_abort(); + } + } + else { + drush_log(dt('No upgrade operation has been performed.'), 'warning'); + } +} diff --git a/sites/all/modules/contrib/migrate/migrate_upgrade/migrate_upgrade.info.yml b/sites/all/modules/contrib/migrate/migrate_upgrade/migrate_upgrade.info.yml new file mode 100644 index 000000000..822bd9c7a --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_upgrade/migrate_upgrade.info.yml @@ -0,0 +1,16 @@ +name: Drupal Upgrade +type: module +description: 'Drush support for direct upgrades from older Drupal versions.' +package: Migration +# core: 8.x +dependencies: + - drupal:migrate (>=8.2) + - drupal:migrate_drupal + - migrate_plus:migrate_plus + - drupal:dblog + +# Information added by Drupal.org packaging script on 2017-09-02 +version: '8.x-3.0-rc2' +core: '8.x' +project: 'migrate_upgrade' +datestamp: 1504384149 diff --git a/sites/all/modules/contrib/migrate/migrate_upgrade/phpcs.xml b/sites/all/modules/contrib/migrate/migrate_upgrade/phpcs.xml new file mode 100644 index 000000000..1ccfe7eb4 --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_upgrade/phpcs.xml @@ -0,0 +1,126 @@ + + + PHP CodeSniffer configuration + . + + + + ./vendor/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sites/all/modules/contrib/migrate/migrate_upgrade/src/DrushLogMigrateMessage.php b/sites/all/modules/contrib/migrate/migrate_upgrade/src/DrushLogMigrateMessage.php new file mode 100644 index 000000000..888fafd3d --- /dev/null +++ b/sites/all/modules/contrib/migrate/migrate_upgrade/src/DrushLogMigrateMessage.php @@ -0,0 +1,23 @@ +version = $this->getLegacyDrupalVersion($connection); + $database_state['key'] = drush_get_option('legacy-db-key'); + $database_state_key = 'migrate_drupal_' . $this->version; + \Drupal::state()->set($database_state_key, $database_state); + \Drupal::state()->set('migrate.fallback_state_key', $database_state_key); + } + else { + $db_url = drush_get_option('legacy-db-url'); + $db_spec = drush_convert_db_from_db_url($db_url); + $db_prefix = drush_get_option('legacy-db-prefix'); + $db_spec['prefix'] = $db_prefix; + $connection = $this->getConnection($db_spec); + $this->version = $this->getLegacyDrupalVersion($connection); + $this->createDatabaseStateSettings($db_spec, $this->version); + } + + $this->databaseStateKey = 'migrate_drupal_' . $this->version; + $migrations = $this->getMigrations($this->databaseStateKey, $this->version); + $this->migrationList = []; + foreach ($migrations as $migration) { + $this->applyFilePath($migration); + $this->expandNodeMigrations($migration); + $this->migrationList[$migration->id()] = $migration; + } + } + + /** + * Adds the source base path configuration to relevant migrations. + * + * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * Migration to alter with the provided path. + */ + protected function applyFilePath(MigrationInterface $migration) { + $destination = $migration->getDestinationConfiguration(); + if ($destination['plugin'] === 'entity:file') { + // Make sure we have a single trailing slash. + $source_base_path = rtrim(drush_get_option('legacy-root'), '/') . '/'; + $source = $migration->getSourceConfiguration(); + $source['constants']['source_base_path'] = $source_base_path; + $migration->set('source', $source); + } + } + + /** + * For D6 term_node migrations, make sure the nid reference is expanded. + * + * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * Migration to alter with the list of node migrations. + */ + protected function expandNodeMigrations(MigrationInterface $migration) { + $source = $migration->getSourceConfiguration(); + // Track the node migrations as we see them. + if ($source['plugin'] == 'd6_node') { + $this->nodeMigrations[] = $migration->id(); + } + elseif ($source['plugin'] == 'd6_term_node' || $source['plugin'] == 'd6_term_node_revision') { + if ($source['plugin'] == 'd6_term_node') { + $id_property = 'nid'; + } + else { + $id_property = 'vid'; + } + // If the ID mapping is to the underived d6_node migration, replace + // it with an expanded list of node migrations. + $process = $migration->getProcess(); + $new_nid_process = []; + foreach ($process[$id_property] as $delta => $plugin_configuration) { + if (in_array($plugin_configuration['plugin'], $this->migrationLookupPluginIds) && + is_string($plugin_configuration['migration']) && + substr($plugin_configuration['migration'], -7) == 'd6_node') { + $plugin_configuration['migration'] = $this->nodeMigrations; + } + $new_nid_process[$delta] = $plugin_configuration; + } + $migration->setProcessOfProperty($id_property, $new_nid_process); + } + } + + /** + * Run the configured migrations. + */ + public function import() { + static::$messages = new DrushLogMigrateMessage(); + if (drush_get_option('debug')) { + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::IDMAP_MESSAGE, + [get_class(), 'onIdMapMessage']); + } + foreach ($this->migrationList as $migration_id => $migration) { + drush_print(dt('Upgrading @migration', ['@migration' => $migration_id])); + $executable = new MigrateExecutable($migration, static::$messages); + // drush_op() provides --simulate support. + drush_op([$executable, 'import']); + } + } + + /** + * Export the configured migration plugins as configuration entities. + */ + public function export() { + $db_info = \Drupal::state()->get($this->databaseStateKey); + + // Create a group to hold the database configuration. + $group = [ + 'id' => $this->databaseStateKey, + 'label' => 'Import from Drupal ' . $this->version, + 'description' => 'Migrations originally generated from drush migrate-upgrade --configure-only', + 'source_type' => 'Drupal ' . $this->version, + 'shared_configuration' => [ + 'source' => [ + 'key' => 'drupal_' . $this->version, + ] + ] + ]; + + // Only add the database connection info to the configuration entity + // if it was passed in as a parameter. + if (!empty(drush_get_option('legacy-db-url'))) { + $group['shared_configuration']['source']['database'] = $db_info['database']; + } + + // Ditto for the key. + if (!empty(drush_get_option('legacy-db-key'))) { + $group['shared_configuration']['source']['key'] = drush_get_option('legacy-db-key'); + } + + $group = MigrationGroup::create($group); + $group->save(); + foreach ($this->migrationList as $migration_id => $migration) { + drush_print(dt('Exporting @migration as @new_migration', + ['@migration' => $migration_id, '@new_migration' => $this->modifyId($migration_id)])); + $entity_array['id'] = $migration_id; + $entity_array['class'] = $migration->get('class'); + $entity_array['cck_plugin_method'] = $migration->get('cck_plugin_method'); + $entity_array['migration_group'] = $this->databaseStateKey; + $entity_array['migration_tags'] = $migration->get('migration_tags'); + $entity_array['label'] = $migration->get('label'); + $entity_array['source'] = $migration->getSourceConfiguration(); + $entity_array['destination'] = $migration->getDestinationConfiguration(); + $entity_array['process'] = $migration->get('process'); + $entity_array['migration_dependencies'] = $migration->getMigrationDependencies(); + $migration_entity = Migration::create($this->substituteIds($entity_array)); + $migration_entity->save(); + } + } + + /** + * Rewrite any migration plugin IDs so they won't conflict with the core + * IDs. + * + * @param $entity_array + * A configuration array for a migration. + * + * @return array + * The migration configuration array modified with new IDs. + */ + protected function substituteIds($entity_array) { + $entity_array['id'] = $this->modifyId($entity_array['id']); + foreach ($entity_array['migration_dependencies'] as $type => $dependencies) { + foreach ($dependencies as $key => $dependency) { + $entity_array['migration_dependencies'][$type][$key] = $this->modifyId($dependency); + } + } + $this->substituteMigrationIds($entity_array['process']); + return $entity_array; + } + + /** + * Recursively substitute IDs for migration plugins. + * + * @param mixed $process + */ + protected function substituteMigrationIds(&$process) { + if (is_array($process)) { + // We found a migration plugin, change the ID. + if (isset($process['plugin']) && in_array($process['plugin'], $this->migrationLookupPluginIds)) { + if (is_array($process['migration'])) { + $new_migration = []; + foreach ($process['migration'] as $migration) { + $new_migration[] = $this->modifyId($migration); + } + $process['migration'] = $new_migration; + } + else { + $process['migration'] = $this->modifyId($process['migration']); + } + } + else { + // Recurse on each array member. + foreach ($process as &$subprocess) { + $this->substituteMigrationIds($subprocess); + } + } + } + } + + /** + * @param $id + * The original core plugin ID. + * + * @return string + * The ID modified to serve as a configuration entity ID. + */ + protected function modifyId($id) { + return drush_get_option('migration-prefix', 'upgrade_') . str_replace(':', '_', $id); + } + + /** + * Rolls back the configured migrations. + */ + public function rollback() { + static::$messages = new DrushLogMigrateMessage(); + $database_state_key = \Drupal::state()->get('migrate.fallback_state_key'); + $database_state = \Drupal::state()->get($database_state_key); + $db_spec = $database_state['database']; + $connection = $this->getConnection($db_spec); + $version = $this->getLegacyDrupalVersion($connection); + $migrations = $this->getMigrations('migrate_drupal_' . $version, $version); + + // Roll back in reverse order. + $this->migrationList = array_reverse($migrations); + + foreach ($migrations as $migration) { + drush_print(dt('Rolling back @migration', ['@migration' => $migration->id()])); + $executable = new MigrateExecutable($migration, static::$messages); + // drush_op() provides --simulate support. + drush_op([$executable, 'rollback']); + } + } + + /** + * Display any messages being logged to the ID map. + * + * @param \Drupal\migrate\Event\MigrateIdMapMessageEvent $event + * The message event. + */ + public static function onIdMapMessage(MigrateIdMapMessageEvent $event) { + if ($event->getLevel() == MigrationInterface::MESSAGE_NOTICE || + $event->getLevel() == MigrationInterface::MESSAGE_INFORMATIONAL) { + $type = 'status'; + } + else { + $type = 'error'; + } + $source_id_string = implode(',', $event->getSourceIdValues()); + $message = t('Source ID @source_id: @message', + ['@source_id' => $source_id_string, '@message' => $event->getMessage()]); + static::$messages->display($message, $type); + } + +}