all contrib module security updates done
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
Conventions to make porting changes between Drupal 6 and Drupal 7 easier:
|
||||
|
||||
Try to always use specific DBTNG functions such as db_select() instead of the
|
||||
more general db_query(), which needs to be renamed to dbtng_query() under
|
||||
Drupal 6.
|
||||
@@ -1,3 +1,282 @@
|
||||
Next release
|
||||
============
|
||||
|
||||
Features and enhancements
|
||||
- #2552189 - Return migration status from drush migrate-import.
|
||||
- #2550793 - Option to always use chunk IDs in MigrateContentParser.
|
||||
- #2505683 - Pass item ID to MigrateContentParser implementations.
|
||||
- #2532222 - Allow spreadsheet source data on a row different that 1.
|
||||
- #1406802 - Add callback() field mapping method, to pass additional arguments
|
||||
to callbacks.
|
||||
- #2516828 - Add mdreg alias for migrate-deregister.
|
||||
- #2504517 - Add timezone handling to timestamp().
|
||||
|
||||
Bug fixes
|
||||
- #2612110 - Notice when units omitted on --limit.
|
||||
- #2597606 - Escape MySQL database names.
|
||||
- #2577091 - Do strict type-check on XML ids.
|
||||
- #2537206 - copy() return not checked.
|
||||
- #2536616 - Improve message when sourceMigration not found.
|
||||
- #2578391 - Improve message when class not found.
|
||||
- #2565043 - Handle empty idlist in MigrateSourceMultiItems.
|
||||
- #2510010 - Stop stripping group name prefixes from migration names.
|
||||
- #2542520 - Fallback for missing source key description.
|
||||
- #2499861 - Properly save/disable/restore mail system.
|
||||
- #2541996 - Prevent NULL file_mask being passed to file_scan_directory().
|
||||
|
||||
Migrate 2.8
|
||||
===========
|
||||
|
||||
Features and enhancements
|
||||
- #2379289 - Better handle interaction of --update with highwater marks.
|
||||
- #2403643 - Support an additional level of subfields.
|
||||
- #2472045 - Add language subfields only if field is translatable.
|
||||
- #2474809 - Provide better message for bad dependencies.
|
||||
- #2397791 - Provide detailed field validation errors.
|
||||
- #2309563 - Add support for running migrations via wildcard name.
|
||||
- #2095841 - Abstract mail system disablement for more flexibility.
|
||||
- #2419373 - Provide ability to cache map lookups.
|
||||
- #2141687 - Provide detailed message on file copy error.
|
||||
|
||||
Bug fixes
|
||||
Field sanitization added to prevent possibility of XSS - see security advisory
|
||||
https://security.drupal.org/node/155268.
|
||||
- #2447115 - Add xpath handling to the field mapping editor.
|
||||
- #2497015 - Term reference handler would ignore all terms if one was NULL.
|
||||
- #2488560 - MigrateSourceList/MigrateSourceMultiItems iterators prematurely
|
||||
return.
|
||||
- #2446105 - Keep coded DNM source field mappings from overriding UI mappings.
|
||||
- #2415977 - Use temporary:// instead of /tmp for drush logging.
|
||||
- #2475473 - Fix handling of --idlist when map not joined.
|
||||
- #2465387 - Fix handling of --stop option on migrate-import.
|
||||
|
||||
Migrate 2.7
|
||||
===========
|
||||
|
||||
Features and enhancements
|
||||
- #2296911 - Add a source handler for IBM DB2.
|
||||
- #2256761 - Add a destination handler for variables.
|
||||
- #2047815 - Support multi-column source keys in idlist.
|
||||
- #1751438 - Add spreadsheet source plugin.
|
||||
|
||||
Bug fixes
|
||||
- #2415597 - Make batching of SQL sources optional, and force map_joinable FALSE.
|
||||
- #2403593 - SQL batching messes up cases with altered queries, such as idlist.
|
||||
- #2298969 - Verify wizard validation function exists.
|
||||
- #2268863 - Fix drush --all option.
|
||||
- #2410523 - Remove inconsistent escaping of migrate_drush_path.
|
||||
|
||||
Migrate 2.6
|
||||
===========
|
||||
|
||||
IMPORTANT CHANGES SINCE MIGRATE 2.5
|
||||
-----------------------------------
|
||||
Migration developers will need to add the "advanced migration information"
|
||||
permission to their roles to continue seeing all the info in the UI they're
|
||||
used to.
|
||||
|
||||
Auto-registration (having classes be registered just based on their class name,
|
||||
with no call to registerMigration or definition in hook_migrate_api()) is no
|
||||
longer supported. Registration of classes defined in hook_migrate_api() is no
|
||||
longer automatic - do a drush migrate-register or use the Register button in the
|
||||
UI to register them.
|
||||
|
||||
Migration class constructors should now always accept an $arguments array as
|
||||
the first parameter and pass it to its parent. This version does support legacy
|
||||
migrations which pass a group object, or nothing, but these methods are
|
||||
deprecated.
|
||||
|
||||
Features and enhancements
|
||||
- #2390229 - Make literal strings monolithic.
|
||||
- #2391789 - Add extender getter to better support extending wizards.
|
||||
- #2363015 - Add support for modifying wizards defined by other modules.
|
||||
- #2353527 - Add getter/setter for trackLastImported.
|
||||
- #2302929 - Explicitly count IDs for JSON source counts.
|
||||
- #2296187 - Batch MigrateSQLSource queries.
|
||||
- #2260211 - Allow skipping of file contents in MigrateListFiles.
|
||||
- #2259089 - Display actual query for SQL sources.
|
||||
- #2224297 - Destination handler for custom blocks.
|
||||
- #2370677 - Add removeStep() method to Wizard API.
|
||||
- #2306953 - Give basic users a little more information.
|
||||
- #2261357 - Add prepareCallback/completeCallback to table destination.
|
||||
- #2306923 - Propagate message statuses to drupal_set_message in batch.
|
||||
- #2301679 - Display all source key fields in the message view.
|
||||
- #2312075 - Add --ignore-highwater option to drush imports
|
||||
- #1961316 - Get data from all XML namespaces
|
||||
- #1298724 - Add a node revision destination plugin.
|
||||
- #1890610 - Add a MongoDB source plugin
|
||||
- #2065295 - Add ability to disable other module's hooks during migration.
|
||||
- #2051547 - Add ability to revert UI-defined field mappings.
|
||||
- #1998632 - Extend MigrateItemsXML to handle an array of XML files.
|
||||
- #1990612 - Add a row status argument to MigrateException, allowing rows to
|
||||
be cleanly skipped by throwing exceptions.
|
||||
- #2017835 - Add MigrateFileUriAsIs file class, and make file migrations more
|
||||
flexible.
|
||||
- #2004426 - UI support for editing dependencies; enable setting dependencies
|
||||
through arguments.
|
||||
- #1826112 - A new API has been added to support external modules in developing
|
||||
easy-to-use wizard-based UIs.
|
||||
- #1833380 - Major refactoring of the UI, breaking groups and migrations
|
||||
(tasks) into separate pages, introducing an advanced permission and
|
||||
presenting a simplified UI to those with only the basic permission.
|
||||
- #1860450 - The UI now has the capability of spawning import/rollback operations
|
||||
in drush, with email notifications of completion. The notification
|
||||
ability is available when running drush at the command line, which
|
||||
may be useful for running imports via cron.
|
||||
- #1996602 - A default field handler is now provided that should handle many if
|
||||
not most field types without custom handlers.
|
||||
- #1901980 - Support encrypting particular arguments saved to the database, to
|
||||
support wizard implementations that may be prompting for secure
|
||||
database credentials.
|
||||
- #1896096 - Add ability to define field mappings via arguments at registration
|
||||
time, and use those to override mappings in code.
|
||||
- #1961620 - When editing field mappings, allow destination or source fields to
|
||||
be explicitly set DNM.
|
||||
- #1996280 - Allow all field/subfields/options to be edited, and indent
|
||||
subfields/options to make the relationship more clear.
|
||||
- #1996350 - Allow sourceMigration to be edited.
|
||||
- #1996736 - Add support for hook_migrate_api_alter().
|
||||
- #1984568 - Add setters to enable constructing migrations according to a
|
||||
dependency injection pattern.
|
||||
- #1835822 - Hash source rows to detect changes.
|
||||
- #1975180 - Explicitly check dependency existence so missing dependencies aren't
|
||||
reported as circular.
|
||||
- #1984534 - Updating/cleanup of examples to reflect current best practices.
|
||||
- #1972600 - Add getter for the MigrateXMLReader member of MigrateSourceXML.
|
||||
- #1856694 - Allow disabling of uri encoding.
|
||||
- #1835142 - Add names-only option to drush ms command.
|
||||
- #1892296 - Increase time limit for batch UI migrations.
|
||||
- #1839644 - Groups have been extended to record titles and arguments in the db.
|
||||
- #1896920 - Remove auto-registration, and make static registration expicit.
|
||||
- #1928956 - Clean up registration/instantiation chicken-and-egg problems, by
|
||||
supporting consistent constructor arguments.
|
||||
- #1550878 - Added UI for deregistering migrations, including one-button
|
||||
deregistration of orphans.
|
||||
- #1792894 - Remove empty field values, permitting proper defaulting.
|
||||
- #1903236 - Allow reset of migrate_migrations() cache.
|
||||
- #1894344 - Support warn_on_override for bulk unmigrated methods.
|
||||
- #1886404 - Allow subfields to be mapped before primary fields.
|
||||
|
||||
Bug fixes
|
||||
- #2391891 - idlist with XML source causes exception.
|
||||
- #2314077 - Don't merge group dependencies into migration dependencies.
|
||||
- #2392683 - Set vocabulary name (bundle) explicitly for term reference fields.
|
||||
- #2266395 - Check for valid picture file saving users.
|
||||
- #2225551 - Add field validation to entity destination plugins.
|
||||
- #2285966 - Added exception handling around constructors.
|
||||
- #2370877 - Fix reloading of wizard steps after first.
|
||||
- #2358767 - Topological sort to help avoid false circular dependency errors.
|
||||
- #2019193 - Properly handle destination DNM status on field mapping save.
|
||||
- #2250081 - Fix PostgreSQL failure updating node statistics on comments.
|
||||
- #2157933 - Fix PostgreSQL error with empty source migrations.
|
||||
- #2177313 - Make sure reused file entities are saved to pick up field changes.
|
||||
- #2352971 - Treat empty default values as NULL in field mapping editor.
|
||||
- #2347205 - Explicitly default the language for terms.
|
||||
- #2307599 - Notice for non-advanced users on rollback.
|
||||
- #2305105 - Remove pedantically-deprecated hyphens.
|
||||
- #2325237 - Handle language arrays for file fields.
|
||||
- #2258909 - Use migrated languages for file fields.
|
||||
- #2323605 - Handle single-parent terms properly.
|
||||
- #2313495 - Handle multilingual path aliases.
|
||||
- #2261227 - Fixed missing subfields on edit mapping form.
|
||||
- #2154385 - Add file/line context to source plugin exceptions.
|
||||
- #2190255 - Remove duplicate warnings on missing XML source.
|
||||
- #2129609 - Remove warnings on MS SQL counts.
|
||||
- #2109931 - Pass drush global options to subprocess.
|
||||
- #2236279 - Prevent ignore_case from causing created terms to be lower-cased.
|
||||
- #2104149 - Sanity-check decrypted arguments.
|
||||
- #2047523 - Consistently document destination handler fields() implementations.
|
||||
- #2244759 - Rollback migrations in proper (reverse) order in UI.
|
||||
- #2184641 - Make saveHighwater() work with PostgreSQL.
|
||||
- #2076035 - Properly check key values in saveMessage().
|
||||
- #2076035 - Validate that term names are not empty.
|
||||
- #2225809 - Use proper API for registering wizard classes.
|
||||
- #2105037 - Expose nid/uid destinations when is_new is on.
|
||||
- #2145213 - Add SQLMap constructor documentation.
|
||||
- #2213033 - Add batch callback documentation.
|
||||
- #2095829 - Remove all variables on uninstall.
|
||||
- #2072721 - Proper title for migrate_example_baseball feature.
|
||||
- #1999228 - Removed AJAX from field mapping form for performance.
|
||||
- #2237755 - Fix activeUrl handling in JSON source.
|
||||
- #2237891 - Use drupal_register_shutdown_function().
|
||||
- #2141409 - Handle drush spawning when Drupal in subdirectory.
|
||||
- #2191335 - Remove unused columns from CSV source row.
|
||||
- #2210533 - Properly pass directory by reference.
|
||||
- #2141569 - Explicitly create group in baseball example.
|
||||
- #2159291 - Add message for missing argument to drush migrate-messages.
|
||||
- #2019193 - Fix unsetting of DNM source fields through the UI.
|
||||
- #2227061 - Fix to subfields in a multi-value context.
|
||||
- #2170177 - Properly handle multi-value fields when a subfield is mapped first.
|
||||
- #2120205 - Fix bug when upgrading lastimported from D6 to D7.
|
||||
- #2109821 - Handle multi-value subfields in the default field handler.
|
||||
- #2039649 - Smarter setting of file 'type' field.
|
||||
- #2106925 - Fix machine_name generation for legacy migrations.
|
||||
- #2102087 - Don't pass --group to drush subprocess.
|
||||
- #2099585 - Enable group option on drush migrate-stop.
|
||||
- #2088255 - Eliminate notices on migrate_map_hash with track_changes.
|
||||
- #2014849 - Make sure (again) statistics properly default to 0.
|
||||
- #2042535 - Properly set file_usage for user pictures.
|
||||
- #2060631 - Fix notices on poll vote import.
|
||||
- #2049689 - Empty values for node is_new caused SQL errors.
|
||||
- #2042399 - Node import did not respect revision flag.
|
||||
- #2034885 - For XML sources, don't override values populated in prepareRow().
|
||||
- #2041267 - Handle lack of potential dependencies on edit page.
|
||||
- #2040101 - Breadcrumbs in migration UI missing sections
|
||||
- #2033947 - Empty migration display names when matching group names.
|
||||
- #2037265 - Static migrations were not registered on module enable.
|
||||
- #2021457 - Coding standard improvements for xml.inc.
|
||||
- #2030559 - Add deprecation messages for arguments().
|
||||
- #1854382 - Fix handling of terms with leading spaces.
|
||||
- #2025137 - Ignore --update in the presence of --idlist.
|
||||
- #2023813 - Apply defaultValue() for empty XML values.
|
||||
- #2023657 - Prevent duplicate aliases when updating nodes.
|
||||
- #2021973 - Drush deregistration needs to handle mixed-case machine names.
|
||||
- #2017443 - Properly hash XML source rows with track_changes on.
|
||||
- #2020095 - Preserve arguments in legacy constructors.
|
||||
- #2018841 - Default field handler needs to account for empty column
|
||||
descriptions.
|
||||
- #2016173 - Fix fatal error editing MigrationBase migrations.
|
||||
- #2015327 - Remove broken automatic field mapping feature.
|
||||
- #2014849 - Make sure statistics properly default to 0.
|
||||
- #2011024 - Wildcard groups/tasks in the menu hierarchy, saving the need to
|
||||
rebuild menus when migrations are registered/deregistered.
|
||||
- #2010884 - Clean up empty default groups on dbupdate.
|
||||
- #2004296 - Enforce map/message table names with prefixes.
|
||||
- #2009222 - Properly update map/message tables on a non-default connection.
|
||||
- #2006158 - Fix warnings on default field handler fields().
|
||||
- #1999918 - Move permissions to core Migrate module.
|
||||
- #1999290 - Check that an XMLMigration has actually populated $row->xml.
|
||||
- #1989012 - Support preserve_files for MigrateFileFid.
|
||||
- #1982564 - Properly handle messages for ignored rows.
|
||||
- #1989022 - Strip HTML tags from drush fields output.
|
||||
- #1988008 - Restore group dependency support.
|
||||
- #1985750 - Add missing prepareRollback/completeRollback for file destinations.
|
||||
- #1978702 - Check highwater marks against the starting highwater.
|
||||
- #1974216 - Change field mapping table PK to a simple serial column.
|
||||
- #1977578 - Bad exception handling in MigrateDestinationTableCopy.
|
||||
- #1973030 - Handle zero-valued timestamps properly.
|
||||
- #1973092 - Fix preservation of sourceMigration when editing mappings.
|
||||
- #1968358 - Prevent unnecessary reimport when using track_changes.
|
||||
- #1968014 - Fix menu notices on task list page.
|
||||
- #1967946 - Missing check on existence of mapping.
|
||||
- #1849350 - Make sure encoding of uris doesn't break query strings.
|
||||
- #1844316 - Ensure rollback_action is added to all map tables.
|
||||
- #1913462 - Fix update functions returning arrays.
|
||||
- #1831940 - Allow empty string source keys in handleSourceMigration().
|
||||
- #1896042 - Fix incorrect usage of originalQuery.
|
||||
- #1954936 - Allow overriding source count in isComplete.
|
||||
- #1952430 - Prevent bogus "no error provided" messages.
|
||||
- #1931168 - Properly cache class info in _migrate_class_list().
|
||||
- #1900914 - Disable email with no custom mail system configured.
|
||||
- #1901648 - Missing bundle property on file migrations.
|
||||
- #1885362 - Make access callback explicit, to avoid conflict with admin_views.
|
||||
- #1880512 - Strip tags from descriptions on drush migrate-mappings.
|
||||
- #1872446 - Properly handle updates on role migration.
|
||||
- #1871764 - Pass zero values through handleSourceMigration.
|
||||
- #1839534 - Handle missing chunk separator in MigrateItemFile.
|
||||
- #1854382 - Prevent duplicate terms due to leading/trailing spaces.
|
||||
- #1794236 - Namespace detail pages within menu system.
|
||||
- #1836426 - Proper check on activeUrl for multiple XML files.
|
||||
|
||||
Migrate 2.5
|
||||
===========
|
||||
@@ -10,12 +289,6 @@ If your migrations are not explicitly registered, you must request auto-registra
|
||||
with a "drush mar" (drush migrate-auto-register) command, or by clicking the
|
||||
"Register" button at admin/content/migrate/registration.
|
||||
|
||||
Bug fixes
|
||||
- #1827052 - Properly check for bad XML.
|
||||
|
||||
Migrate 2.5 Release Candidate 2
|
||||
===============================
|
||||
|
||||
Features and enhancements
|
||||
- #1824024 - Destination and field handlers may now be registered through
|
||||
hook_migrate_api(). Automatic registration of all migration and
|
||||
@@ -26,14 +299,6 @@ Features and enhancements
|
||||
hook_migrate_api().
|
||||
- #1819730 - Make migration of files in a field context non-fatal.
|
||||
- #1816652 - Provide useful warning when file subfield arrays don't line up.
|
||||
|
||||
Bug fixes
|
||||
- #1824118 - Make --force work for rollbacks.
|
||||
|
||||
Migrate 2.5 Release Candidate 1
|
||||
===============================
|
||||
|
||||
Features and enhancements
|
||||
- #1778952 - Enable registration of dynamic migrations via hook_migrate_api().
|
||||
Add explicit auto-registration of non-dynamic migrations, remove
|
||||
performing registration on cache clear.
|
||||
@@ -54,6 +319,8 @@ Features and enhancements
|
||||
- #1621906 - Support DESTINATION system-of-records for menu links.
|
||||
|
||||
Bug fixes
|
||||
- #1827052 - Properly check for bad XML.
|
||||
- #1824118 - Make --force work for rollbacks.
|
||||
- #1659150 - Change 'ok' message types to 'status'.
|
||||
- #1690080 - Deal with self-references in handleSourceMigration().
|
||||
- #1797644 - Remove bogus assignment on term update.
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
|
||||
The Migrate module provides a flexible framework for migrating content into Drupal
|
||||
from other sources (e.g., when converting a web site from another CMS to Drupal).
|
||||
The Migrate module provides a flexible framework for migrating content into
|
||||
Drupal from other sources
|
||||
(e.g., when converting a web site from another CMS to Drupal).
|
||||
Out-of-the-box, support for creating Drupal nodes, taxonomy terms, comments, and
|
||||
users are included. Plugins permit migration of other types of content.
|
||||
|
||||
Usage
|
||||
-----
|
||||
Documentation is at http://drupal.org/node/415260. To get started, enable the
|
||||
migrate_example module and browse to admin/content/migrate to see its dashboard.
|
||||
The code for this migration is in migrate_example/beer.inc (advanced examples are
|
||||
in wine.inc). Mimic that file in order to specify your own migrations.
|
||||
Documentation is at http://drupal.org/migrate. To get started, enable the
|
||||
migrate_example module, enable migrate_ui, and then browse to
|
||||
admin/content/migrate to see its dashboard.
|
||||
The code for this migration is in migrate_example/beer.inc
|
||||
(advanced examples are in wine.inc). Mimic that file in order to specify your
|
||||
own migrations.
|
||||
|
||||
The Migrate module itself has support for migration into core objects. Support
|
||||
for migration involving contrib modules is in the migrate_extras module.
|
||||
@@ -17,8 +19,8 @@ for migration involving contrib modules is in the migrate_extras module.
|
||||
Known issues
|
||||
------------
|
||||
A user migration with systemOfRecord == DESTINATION will drop pictures from user
|
||||
records due to core bug http://drupal.org/node/935592 - the simpletests report an
|
||||
error reflecting this. We have not developed a work-around.
|
||||
records due to core bug http://drupal.org/node/935592 - the simpletests report
|
||||
an error reflecting this. We have not developed a work-around.
|
||||
|
||||
Upgrading
|
||||
---------
|
||||
@@ -30,9 +32,9 @@ projects.
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
Much of the Migrate module functionality was sponsored by Cyrve, for its clients GenomeWeb
|
||||
(http://www.genomeweb.com), The Economist (http://www.economist.com), and Examiner.com
|
||||
(http://www.examiner.com).
|
||||
Much of the Migrate module functionality was sponsored by Cyrve, for its clients
|
||||
GenomeWeb (http://www.genomeweb.com), The Economist (http://www.economist.com),
|
||||
and Examiner.com (http://www.examiner.com).
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,12 @@
|
||||
* MigrateDestinationEntity for an example.
|
||||
*/
|
||||
abstract class MigrateDestination {
|
||||
|
||||
/**
|
||||
* To support MigrateSQLMap maps, derived destination classes should return
|
||||
* schema field definition(s) corresponding to the primary key of the destination
|
||||
* being implemented. These are used to construct the destination key fields
|
||||
* of the map table for a migration using this destination.
|
||||
* schema field definition(s) corresponding to the primary key of the
|
||||
* destination being implemented. These are used to construct the destination
|
||||
* key fields of the map table for a migration using this destination.
|
||||
*
|
||||
* abstract static public function getKeySchema()
|
||||
*/
|
||||
@@ -36,6 +37,7 @@ abstract class MigrateDestination {
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -43,9 +45,9 @@ abstract class MigrateDestination {
|
||||
abstract public function fields();
|
||||
|
||||
/**
|
||||
* Derived classes must implement either bulkRollback or rollback() according to
|
||||
* the signatures below, to rollback (usually by deletion) previously-migrated
|
||||
* items.
|
||||
* Derived classes must implement either bulkRollback or rollback() according
|
||||
* to the signatures below, to rollback (usually by deletion)
|
||||
* previously-migrated items.
|
||||
*
|
||||
* $ids is an array of single-field keys to be deleted
|
||||
* abstract public function bulkRollback(array $ids);
|
||||
@@ -55,9 +57,10 @@ abstract class MigrateDestination {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Derived classes must implement import(), to construct one new object (pre-pppulated
|
||||
* using field mappings in the Migration). It is expected to call prepare and
|
||||
* complete handlers, passing them $row (the raw data from the source).
|
||||
* Derived classes must implement import(), to construct one new object
|
||||
* (pre-pppulated using field mappings in the Migration). It is expected to
|
||||
* call prepare and complete handlers, passing them $row (the raw data from
|
||||
* the source).
|
||||
*/
|
||||
abstract public function import(stdClass $object, stdClass $row);
|
||||
|
||||
@@ -78,10 +81,13 @@ abstract class MigrateDestination {
|
||||
* @var int
|
||||
*/
|
||||
protected $numCreated = 0;
|
||||
|
||||
public function getCreated() {
|
||||
return $this->numCreated;
|
||||
}
|
||||
|
||||
protected $numUpdated = 0;
|
||||
|
||||
public function getUpdated() {
|
||||
return $this->numUpdated;
|
||||
}
|
||||
@@ -105,11 +111,24 @@ abstract class MigrateDestination {
|
||||
* All destination handlers should be derived from MigrateDestinationHandler
|
||||
*/
|
||||
abstract class MigrateDestinationHandler extends MigrateHandler {
|
||||
// abstract function arguments(...)
|
||||
|
||||
// Any one or more of these methods may be implemented
|
||||
|
||||
/**
|
||||
* Any one or more of these methods may be implemented
|
||||
* Documentation of any fields added to the destination by this handler.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type (node, user, etc.) for which to list fields.
|
||||
* @param $bundle
|
||||
* The bundle (article, blog, etc.), if any, for which to list fields.
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration providing the context.
|
||||
*
|
||||
* @return array
|
||||
* An array keyed by field name, with field descriptions as values.
|
||||
*/
|
||||
//abstract public function fields();
|
||||
//abstract public function fields($entity_type, $bundle, $migration = NULL);
|
||||
|
||||
//abstract public function prepare($entity, stdClass $row);
|
||||
//abstract public function complete($entity, stdClass $row);
|
||||
}
|
||||
|
||||
@@ -6,13 +6,34 @@
|
||||
*/
|
||||
|
||||
class MigrateException extends Exception {
|
||||
|
||||
/**
|
||||
* The level of the error being reported (a Migration::MESSAGE_* constant)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $level;
|
||||
|
||||
public function getLevel() {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
public function __construct($message, $level = Migration::MESSAGE_ERROR) {
|
||||
/**
|
||||
* The status to record in the map table for the current item (a
|
||||
* MigrateMap::STATUS_* constant)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
public function getStatus() {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function __construct($message, $level = Migration::MESSAGE_ERROR,
|
||||
$status = MigrateMap::STATUS_FAILED) {
|
||||
$this->level = $level;
|
||||
$this->status = $status;
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
class MigrateFieldMapping {
|
||||
|
||||
/**
|
||||
* Destination field name for the mapping. If empty, the mapping is just a
|
||||
* stub for annotating the source field.
|
||||
@@ -14,6 +15,7 @@ class MigrateFieldMapping {
|
||||
* @var string
|
||||
*/
|
||||
protected $destinationField;
|
||||
|
||||
public function getDestinationField() {
|
||||
return $this->destinationField;
|
||||
}
|
||||
@@ -25,10 +27,28 @@ class MigrateFieldMapping {
|
||||
* @var string
|
||||
*/
|
||||
protected $sourceField;
|
||||
|
||||
public function getSourceField() {
|
||||
return $this->sourceField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
const MAPPING_SOURCE_CODE = 1;
|
||||
|
||||
const MAPPING_SOURCE_DB = 2;
|
||||
|
||||
protected $mappingSource = self::MAPPING_SOURCE_CODE;
|
||||
|
||||
public function getMappingSource() {
|
||||
return $this->mappingSource;
|
||||
}
|
||||
|
||||
public function setMappingSource($mapping_source) {
|
||||
$this->mappingSource = $mapping_source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default value for simple mappings, when there is no source mapping or the
|
||||
* source field is empty. If both this and the sourceField are omitted, the
|
||||
@@ -37,6 +57,7 @@ class MigrateFieldMapping {
|
||||
* @var mixed
|
||||
*/
|
||||
protected $defaultValue;
|
||||
|
||||
public function getDefaultValue() {
|
||||
return $this->defaultValue;
|
||||
}
|
||||
@@ -48,6 +69,7 @@ class MigrateFieldMapping {
|
||||
* @var string
|
||||
*/
|
||||
protected $separator;
|
||||
|
||||
public function getSeparator() {
|
||||
return $this->separator;
|
||||
}
|
||||
@@ -61,6 +83,7 @@ class MigrateFieldMapping {
|
||||
* An array of source migrations, or string for a single migration.
|
||||
*/
|
||||
protected $sourceMigration;
|
||||
|
||||
public function getSourceMigration() {
|
||||
return $this->sourceMigration;
|
||||
}
|
||||
@@ -71,6 +94,7 @@ class MigrateFieldMapping {
|
||||
* @var string
|
||||
*/
|
||||
protected $callbacks = array();
|
||||
|
||||
public function getCallbacks() {
|
||||
return $this->callbacks;
|
||||
}
|
||||
@@ -86,6 +110,7 @@ class MigrateFieldMapping {
|
||||
* @var string
|
||||
*/
|
||||
protected $dedupe;
|
||||
|
||||
public function getDedupe() {
|
||||
return $this->dedupe;
|
||||
}
|
||||
@@ -93,40 +118,49 @@ class MigrateFieldMapping {
|
||||
/**
|
||||
* Argument overrides. If present this will be an array, keyed by
|
||||
* a field API array key, with one or both of these entries:
|
||||
* 'source_field' - Name of the source field in the incoming row containing the
|
||||
* value to be assigned
|
||||
* 'default_value' - A constant value to be assigned in the absence of source_field
|
||||
* 'source_field' - Name of the source field in the incoming row containing
|
||||
* the value to be assigned
|
||||
* 'default_value' - A constant value to be assigned in the absence of
|
||||
* source_field Deprecated - subfield notation is now preferred.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments;
|
||||
|
||||
public function getArguments() {
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
protected $description = '';
|
||||
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
protected $issueGroup;
|
||||
|
||||
public function getIssueGroup() {
|
||||
return $this->issueGroup;
|
||||
}
|
||||
|
||||
protected $issueNumber;
|
||||
|
||||
public function getIssueNumber() {
|
||||
return $this->issueNumber;
|
||||
}
|
||||
|
||||
protected $issuePriority = self::ISSUE_PRIORITY_OK;
|
||||
|
||||
public function getIssuePriority() {
|
||||
return $this->issuePriority;
|
||||
}
|
||||
|
||||
const ISSUE_PRIORITY_OK = 1;
|
||||
|
||||
const ISSUE_PRIORITY_LOW = 2;
|
||||
|
||||
const ISSUE_PRIORITY_MEDIUM = 3;
|
||||
|
||||
const ISSUE_PRIORITY_BLOCKER = 4;
|
||||
|
||||
public static $priorities = array();
|
||||
@@ -163,7 +197,17 @@ class MigrateFieldMapping {
|
||||
}
|
||||
|
||||
public function callbacks($callbacks) {
|
||||
$this->callbacks = func_get_args();
|
||||
foreach (func_get_args() as $callback) {
|
||||
$this->callback($callback);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function callback($callback) {
|
||||
$this->callbacks[] = array(
|
||||
'callback' => $callback,
|
||||
'params' => array_slice(func_get_args(), 1),
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -173,6 +217,9 @@ class MigrateFieldMapping {
|
||||
}
|
||||
|
||||
public function arguments($arguments) {
|
||||
if (variable_get('migrate_deprecation_warnings', 1)) {
|
||||
MigrationBase::displayMessage(t('The field mapping arguments() method is now deprecated - please use subfield notation instead.'));
|
||||
}
|
||||
$this->arguments = $arguments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -6,22 +6,48 @@
|
||||
*/
|
||||
|
||||
class MigrateGroup {
|
||||
|
||||
/**
|
||||
* The name of the group - used to identify it in drush commands.
|
||||
* The machine name of the group - used to identify it in drush commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user-visible title of the group.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
public function getTitle() {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Domain-specific arguments for the group (to be applied to all migrations
|
||||
* in the group).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = array();
|
||||
|
||||
public function getArguments() {
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of groups this group is dependent on.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dependencies = array();
|
||||
|
||||
public function getDependencies() {
|
||||
return $this->dependencies;
|
||||
}
|
||||
@@ -32,12 +58,15 @@ class MigrateGroup {
|
||||
* @var array
|
||||
*/
|
||||
static protected $groupList = array();
|
||||
|
||||
static public function groups() {
|
||||
$groups = array();
|
||||
$dependent_groups = array();
|
||||
$dependencies_list = array();
|
||||
$required_groups = array();
|
||||
foreach (self::$groupList as $name => $group) {
|
||||
$dependencies = $group->getDependencies();
|
||||
$dependencies_list[$name] = $dependencies;
|
||||
if (count($dependencies) > 0) {
|
||||
// Set groups with dependencies aside for reordering
|
||||
$dependent_groups[$name] = $group;
|
||||
@@ -48,29 +77,11 @@ class MigrateGroup {
|
||||
$groups[$name] = $group;
|
||||
}
|
||||
}
|
||||
$iterations = 0;
|
||||
while (count($dependent_groups) > 0) {
|
||||
if ($iterations++ > 20) {
|
||||
$group_names = implode(',', array_keys($dependent_groups));
|
||||
throw new MigrateException(t('Failure to sort migration list - most likely due ' .
|
||||
'to circular dependencies involving groups !group_names',
|
||||
array('!group_names' => $group_names)));
|
||||
}
|
||||
foreach ($dependent_groups as $name => $group) {
|
||||
$ready = TRUE;
|
||||
// Scan all the dependencies for this group and make sure they're all
|
||||
// in the final list
|
||||
foreach ($group->getDependencies() as $dependency) {
|
||||
if (!isset($groups[$dependency])) {
|
||||
$ready = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ready) {
|
||||
// Yes they are! Move this group to the final list
|
||||
$groups[$name] = $group;
|
||||
unset($dependent_groups[$name]);
|
||||
}
|
||||
|
||||
$ordered_groups = migrate_order_dependencies($dependencies_list);
|
||||
foreach ($ordered_groups as $name) {
|
||||
if (!isset($groups[$name])) {
|
||||
$groups[$name] = $dependent_groups[$name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +97,10 @@ class MigrateGroup {
|
||||
* @param array $dependencies
|
||||
* List of dependent groups.
|
||||
*/
|
||||
public function __construct($name, $dependencies = array()) {
|
||||
public function __construct($name, $dependencies = array(), $title = '', $arguments = array()) {
|
||||
$this->name = $name;
|
||||
$this->title = $title;
|
||||
$this->arguments = $arguments;
|
||||
$this->dependencies = $dependencies;
|
||||
}
|
||||
|
||||
@@ -102,8 +115,94 @@ class MigrateGroup {
|
||||
*/
|
||||
static public function getInstance($name, $dependencies = array()) {
|
||||
if (empty(self::$groupList[$name])) {
|
||||
self::$groupList[$name] = new MigrateGroup($name, $dependencies);
|
||||
$row = db_select('migrate_group', 'mg')
|
||||
->fields('mg')
|
||||
->condition('name', $name)
|
||||
->execute()
|
||||
->fetchObject();
|
||||
if ($row) {
|
||||
$arguments = unserialize($row->arguments);
|
||||
$arguments = MigrationBase::decryptArguments($arguments);
|
||||
if (empty($dependencies) && isset($arguments['dependencies'])) {
|
||||
$dependencies = $arguments['dependencies'];
|
||||
}
|
||||
self::$groupList[$name] = new MigrateGroup($name, $dependencies,
|
||||
$row->title, $arguments);
|
||||
}
|
||||
else {
|
||||
self::register($name);
|
||||
self::$groupList[$name] = new MigrateGroup($name, $dependencies);
|
||||
}
|
||||
}
|
||||
return self::$groupList[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new migration group in the migrate_group table.
|
||||
*
|
||||
* @param string $name
|
||||
* The machine name (unique identifier) for the group.
|
||||
*
|
||||
* @param string $title
|
||||
* A user-visible title for the group. Defaults to the machine name.
|
||||
*
|
||||
* @param array $arguments
|
||||
* An array of group arguments - generally data that applies to all
|
||||
* migrations in the group.
|
||||
*/
|
||||
static public function register($name, $title = NULL, array $arguments = array()) {
|
||||
if (!$title) {
|
||||
$title = $name;
|
||||
}
|
||||
|
||||
$arguments = MigrationBase::encryptArguments($arguments);
|
||||
|
||||
// Register the migration if it's not already there; if it is,
|
||||
// update the class and arguments in case they've changed.
|
||||
db_merge('migrate_group')
|
||||
->key(array('name' => $name))
|
||||
->fields(array(
|
||||
'title' => $title,
|
||||
'arguments' => serialize($arguments),
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deregister a migration group - remove it from the database, and also
|
||||
* remove migrations attached to it.
|
||||
*
|
||||
* @param string $name
|
||||
* (Machine) name of the group to deregister.
|
||||
*/
|
||||
static public function deregister($name) {
|
||||
$result = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('machine_name'))
|
||||
->condition('group_name', $name)
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
Migration::deregisterMigration($row->machine_name);
|
||||
}
|
||||
|
||||
db_delete('migrate_group')
|
||||
->condition('name', $name)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any groups which no longer contain any migrations.
|
||||
*/
|
||||
static public function deleteOrphans() {
|
||||
$query = db_select('migrate_group', 'mg');
|
||||
$query->addField('mg', 'name');
|
||||
$query->leftJoin('migrate_status', 'ms', 'mg.name=ms.group_name');
|
||||
$query->isNull('ms.machine_name');
|
||||
$result = $query->execute();
|
||||
foreach ($result as $row) {
|
||||
db_delete('migrate_group')
|
||||
->condition('name', $row->name)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
* to implement appropriate methods (e.g., prepare, complete, or fields).
|
||||
*/
|
||||
abstract class MigrateHandler {
|
||||
|
||||
/**
|
||||
* List of other handler classes which should be invoked before the current one.
|
||||
* List of other handler classes which should be invoked before the current
|
||||
* one.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dependencies = array();
|
||||
|
||||
public function getDependencies() {
|
||||
return $this->dependencies;
|
||||
}
|
||||
@@ -27,6 +30,7 @@ abstract class MigrateHandler {
|
||||
* @var array
|
||||
*/
|
||||
protected $typesHandled = array();
|
||||
|
||||
public function getTypesHandled() {
|
||||
return $this->typesHandled;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,16 @@
|
||||
* for the purpose of rollback.
|
||||
*/
|
||||
abstract class MigrateMap implements Iterator {
|
||||
|
||||
/**
|
||||
* Codes reflecting the current status of a map row.
|
||||
*/
|
||||
const STATUS_IMPORTED = 0;
|
||||
|
||||
const STATUS_NEEDS_UPDATE = 1;
|
||||
|
||||
const STATUS_IGNORED = 2;
|
||||
|
||||
const STATUS_FAILED = 3;
|
||||
|
||||
/**
|
||||
@@ -23,6 +27,7 @@ abstract class MigrateMap implements Iterator {
|
||||
*
|
||||
*/
|
||||
const ROLLBACK_DELETE = 0;
|
||||
|
||||
const ROLLBACK_PRESERVE = 1;
|
||||
|
||||
/**
|
||||
@@ -32,7 +37,9 @@ abstract class MigrateMap implements Iterator {
|
||||
* @var array
|
||||
*/
|
||||
protected $sourceKey, $destinationKey;
|
||||
|
||||
abstract public function getSourceKey();
|
||||
|
||||
abstract public function getDestinationKey();
|
||||
|
||||
/**
|
||||
@@ -43,6 +50,13 @@ abstract class MigrateMap implements Iterator {
|
||||
*/
|
||||
protected $sourceKeyMap, $destinationKeyMap;
|
||||
|
||||
/**
|
||||
* Get the source key map.
|
||||
*/
|
||||
public function getSourceKeyMap() {
|
||||
return $this->sourceKeyMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean determining whether to track last_imported times in map tables
|
||||
*
|
||||
@@ -50,6 +64,16 @@ abstract class MigrateMap implements Iterator {
|
||||
*/
|
||||
protected $trackLastImported = FALSE;
|
||||
|
||||
public function getTrackLastImported() {
|
||||
return $this->trackLastImported;
|
||||
}
|
||||
|
||||
public function setTrackLastImported($trackLastImported) {
|
||||
if (is_bool($trackLastImported)) {
|
||||
$this->trackLastImported = $trackLastImported;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a mapping from the key values in the source row to the destination
|
||||
* keys.
|
||||
@@ -58,9 +82,11 @@ abstract class MigrateMap implements Iterator {
|
||||
* @param $dest_ids
|
||||
* @param $status
|
||||
* @param $rollback_action
|
||||
* @param $hash
|
||||
*/
|
||||
abstract public function saveIDMapping(stdClass $source_row, array $dest_ids,
|
||||
$status = MigrateMap::STATUS_IMPORTED, $rollback_action = MigrateMap::ROLLBACK_DELETE);
|
||||
$status = MigrateMap::STATUS_IMPORTED,
|
||||
$rollback_action = MigrateMap::ROLLBACK_DELETE, $hash = NULL);
|
||||
|
||||
/**
|
||||
* Record a message related to a source record
|
||||
@@ -130,6 +156,7 @@ abstract class MigrateMap implements Iterator {
|
||||
* Retrieve map data for a given source or destination item
|
||||
*/
|
||||
abstract public function getRowBySource(array $source_id);
|
||||
|
||||
abstract public function getRowByDestination(array $destination_id);
|
||||
|
||||
/**
|
||||
@@ -138,29 +165,32 @@ abstract class MigrateMap implements Iterator {
|
||||
abstract public function getRowsNeedingUpdate($count);
|
||||
|
||||
/**
|
||||
* Given a (possibly multi-field) destination key, return the (possibly multi-field)
|
||||
* source key mapped to it.
|
||||
* Given a (possibly multi-field) destination key, return the (possibly
|
||||
* multi-field) source key mapped to it.
|
||||
*
|
||||
* @param array $destination_id
|
||||
* Array of destination key values.
|
||||
*
|
||||
* @return array
|
||||
* Array of source key values, or NULL on failure.
|
||||
*/
|
||||
abstract public function lookupSourceID(array $destination_id);
|
||||
|
||||
/**
|
||||
* Given a (possibly multi-field) source key, return the (possibly multi-field)
|
||||
* destination key it is mapped to.
|
||||
* Given a (possibly multi-field) source key, return the (possibly
|
||||
* multi-field) destination key it is mapped to.
|
||||
*
|
||||
* @param array $source_id
|
||||
* Array of source key values.
|
||||
*
|
||||
* @return array
|
||||
* Array of destination key values, or NULL on failure.
|
||||
*/
|
||||
abstract public function lookupDestinationID(array $source_id);
|
||||
|
||||
/**
|
||||
* Remove any persistent storage used by this map (e.g., map and message tables)
|
||||
* Remove any persistent storage used by this map (e.g., map and message
|
||||
* tables)
|
||||
*/
|
||||
abstract public function destroy();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@
|
||||
* MigrateSourceSQL for an example.
|
||||
*/
|
||||
abstract class MigrateSource implements Iterator {
|
||||
|
||||
/**
|
||||
* The current row from the quey
|
||||
*
|
||||
@@ -26,6 +27,7 @@ abstract class MigrateSource implements Iterator {
|
||||
* @var array
|
||||
*/
|
||||
protected $currentKey;
|
||||
|
||||
public function getCurrentKey() {
|
||||
return $this->currentKey;
|
||||
}
|
||||
@@ -50,6 +52,7 @@ abstract class MigrateSource implements Iterator {
|
||||
* @var int
|
||||
*/
|
||||
protected $numIgnored = 0;
|
||||
|
||||
public function getIgnored() {
|
||||
return $this->numIgnored;
|
||||
}
|
||||
@@ -60,6 +63,7 @@ abstract class MigrateSource implements Iterator {
|
||||
* @var int
|
||||
*/
|
||||
protected $numProcessed = 0;
|
||||
|
||||
public function getProcessed() {
|
||||
return $this->numProcessed;
|
||||
}
|
||||
@@ -78,6 +82,20 @@ abstract class MigrateSource implements Iterator {
|
||||
*/
|
||||
protected $highwaterField;
|
||||
|
||||
/**
|
||||
* The highwater mark at the beginning of the import operation.
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
protected $originalHighwater = '';
|
||||
|
||||
/**
|
||||
* Used in the case of multiple key sources that need to use idlist.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $multikeySeparator = ':';
|
||||
|
||||
/**
|
||||
* List of source IDs to process.
|
||||
*
|
||||
@@ -116,6 +134,14 @@ abstract class MigrateSource implements Iterator {
|
||||
*/
|
||||
protected $skipCount = FALSE;
|
||||
|
||||
/**
|
||||
* If TRUE, we will maintain hashed source rows to determine whether incoming
|
||||
* data has changed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $trackChanges = FALSE;
|
||||
|
||||
/**
|
||||
* By default, next() will directly read the map row and add it to the data
|
||||
* row. A source plugin implementation may do this itself (in particular, the
|
||||
@@ -138,7 +164,7 @@ abstract class MigrateSource implements Iterator {
|
||||
}
|
||||
|
||||
if (!isset($this->cacheKey)) {
|
||||
$this->cacheKey = md5((string)$this);
|
||||
$this->cacheKey = md5((string) $this);
|
||||
}
|
||||
|
||||
// If a refresh is requested, or we're not caching counts, ask the derived
|
||||
@@ -186,6 +212,9 @@ abstract class MigrateSource implements Iterator {
|
||||
if (!empty($options['cache_key'])) {
|
||||
$this->cacheKey = $options['cache_key'];
|
||||
}
|
||||
if (!empty($options['track_changes'])) {
|
||||
$this->trackChanges = $options['track_changes'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,17 +231,18 @@ abstract class MigrateSource implements Iterator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::key - called when entering a loop iteration, returning
|
||||
* the key of the current row. It must be a scalar - we will serialize
|
||||
* to fulfill the requirement, but using getCurrentKey() is preferable.
|
||||
* Implementation of Iterator::key - called when entering a loop iteration,
|
||||
* returning the key of the current row. It must be a scalar - we will
|
||||
* serialize to fulfill the requirement, but using getCurrentKey() is
|
||||
* preferable.
|
||||
*/
|
||||
public function key() {
|
||||
return serialize($this->currentKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::valid() - called at the top of the loop, returning
|
||||
* TRUE to process the loop and FALSE to terminate it
|
||||
* Implementation of Iterator::valid() - called at the top of the loop,
|
||||
* returning TRUE to process the loop and FALSE to terminate it
|
||||
*/
|
||||
public function valid() {
|
||||
return !is_null($this->currentRow);
|
||||
@@ -229,6 +259,9 @@ abstract class MigrateSource implements Iterator {
|
||||
$this->numProcessed = 0;
|
||||
$this->numIgnored = 0;
|
||||
$this->highwaterField = $this->activeMigration->getHighwaterField();
|
||||
if (!empty($this->highwaterField)) {
|
||||
$this->originalHighwater = $this->activeMigration->getHighwater();
|
||||
}
|
||||
if ($this->activeMigration->getOption('idlist')) {
|
||||
$this->idList = explode(',', $this->activeMigration->getOption('idlist'));
|
||||
}
|
||||
@@ -248,9 +281,11 @@ abstract class MigrateSource implements Iterator {
|
||||
public function next() {
|
||||
$this->currentKey = NULL;
|
||||
$this->currentRow = NULL;
|
||||
|
||||
migrate_instrument_start(get_class($this) . ' getNextRow');
|
||||
while ($row = $this->getNextRow()) {
|
||||
migrate_instrument_stop(get_class($this) . ' getNextRow');
|
||||
|
||||
// Populate the source key for this row
|
||||
$this->currentKey = $this->activeMigration->prepareKey(
|
||||
$this->activeMap->getSourceKey(), $row);
|
||||
@@ -262,28 +297,29 @@ abstract class MigrateSource implements Iterator {
|
||||
if ($map_row) {
|
||||
foreach ($map_row as $field => $value) {
|
||||
$field = 'migrate_map_' . $field;
|
||||
$row->$field = $value;
|
||||
$row->{$field} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First, determine if this row should be passed to prepareRow(), or skipped
|
||||
// entirely. The rules are:
|
||||
// First, determine if this row should be passed to prepareRow(), or
|
||||
// skipped entirely. The rules are:
|
||||
// 1. If there's an explicit idlist, that's all we care about (ignore
|
||||
// highwaters and map rows).
|
||||
$prepared = FALSE;
|
||||
if (!empty($this->idList)) {
|
||||
if (in_array(reset($this->currentKey), $this->idList)) {
|
||||
// In the list, fall through.
|
||||
}
|
||||
else {
|
||||
// Not in the list, skip it
|
||||
$this->currentRow = NULL;
|
||||
continue;
|
||||
// Check first source key.
|
||||
if (!in_array(reset($this->currentKey), $this->idList)) {
|
||||
// If this is a compound source key, check the full key.
|
||||
$compoundKey = implode($this->multikeySeparator, $this->currentKey);
|
||||
if (count($this->currentKey) == 1 || !in_array($compoundKey, $this->idList)) {
|
||||
// Could not find the key, skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. If the row is not in the map (we have never tried to import it before),
|
||||
// we always want to try it.
|
||||
// 2. If the row is not in the map (we have never tried to import it
|
||||
// before), we always want to try it.
|
||||
elseif (!isset($row->migrate_map_sourceid1)) {
|
||||
// Fall through
|
||||
}
|
||||
@@ -293,36 +329,52 @@ abstract class MigrateSource implements Iterator {
|
||||
}
|
||||
// 4. At this point, we have a row which has previously been imported and
|
||||
// not marked for update. If we're not using highwater marks, then we
|
||||
// will not take this row.
|
||||
// will not take this row. Except, if we're looking for changes in the
|
||||
// data, we need to go through prepareRow() before we can decide to
|
||||
// skip it.
|
||||
elseif (empty($this->highwaterField)) {
|
||||
// No highwater, skip
|
||||
$this->currentRow = NULL;
|
||||
continue;
|
||||
if ($this->trackChanges) {
|
||||
if ($this->prepareRow($row) !== FALSE) {
|
||||
if ($this->dataChanged($row)) {
|
||||
// This is a keeper
|
||||
$this->currentRow = $row;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// No change, skip it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// prepareRow() told us to skip it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No highwater and not tracking changes, skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 5. The initial highwater mark, before anything is migrated, is ''. We
|
||||
// want to make sure we don't mistakenly skip rows with a highwater
|
||||
// field value of 0, so explicitly handle '' here.
|
||||
elseif ($this->activeMigration->getHighwater() === '') {
|
||||
elseif ($this->originalHighwater === '') {
|
||||
// Fall through
|
||||
}
|
||||
// 6. So, we are using highwater marks. Take the row if its highwater field
|
||||
// value is greater than the saved mark, otherwise skip it.
|
||||
// 6. So, we are using highwater marks. Take the row if its highwater
|
||||
// field value is greater than the saved mark, otherwise skip it.
|
||||
else {
|
||||
// Call prepareRow() here, in case the highwaterField needs preparation
|
||||
if ($this->prepareRow($row) !== FALSE) {
|
||||
if ($row->{$this->highwaterField['name']} > $this->activeMigration->getHighwater()) {
|
||||
if ($row->{$this->highwaterField['name']} > $this->originalHighwater) {
|
||||
$this->currentRow = $row;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Skip
|
||||
$this->currentRow = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->currentRow = NULL;
|
||||
}
|
||||
$prepared = TRUE;
|
||||
}
|
||||
|
||||
@@ -358,6 +410,10 @@ abstract class MigrateSource implements Iterator {
|
||||
migrate_instrument_stop(get_class($this->activeMigration) . ' prepareRow');
|
||||
// We're explicitly skipping this row - keep track in the map table
|
||||
if ($return === FALSE) {
|
||||
// Make sure we replace any previous messages for this item with any
|
||||
// new ones.
|
||||
$this->activeMigration->getMap()->delete($this->currentKey, TRUE);
|
||||
$this->activeMigration->saveQueuedMessages();
|
||||
$this->activeMigration->getMap()->saveIDMapping($row, array(),
|
||||
MigrateMap::STATUS_IGNORED, $this->activeMigration->rollbackAction);
|
||||
$this->numIgnored++;
|
||||
@@ -366,8 +422,62 @@ abstract class MigrateSource implements Iterator {
|
||||
}
|
||||
else {
|
||||
$return = TRUE;
|
||||
// When tracking changed data, We want to quietly skip (rather than
|
||||
// "ignore") rows with changes. The caller needs to make that decision,
|
||||
// so we need to provide them with the necessary information (before and
|
||||
// after hashes).
|
||||
if ($this->trackChanges) {
|
||||
$unhashed_row = clone $row;
|
||||
// Remove all map data, otherwise we'll have a false positive on the
|
||||
// second import (attempt) on a row.
|
||||
foreach ($unhashed_row as $field => $data) {
|
||||
if (strpos($field, 'migrate_map_') === 0) {
|
||||
unset($unhashed_row->{$field});
|
||||
}
|
||||
}
|
||||
$row->migrate_map_original_hash = isset($row->migrate_map_hash) ?
|
||||
$row->migrate_map_hash : '';
|
||||
$row->migrate_map_hash = $this->hash($unhashed_row);
|
||||
}
|
||||
else {
|
||||
$row->migrate_map_hash = '';
|
||||
}
|
||||
}
|
||||
|
||||
$this->numProcessed++;
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this row has changed, and therefore whether it should
|
||||
* be processed.
|
||||
*
|
||||
* @param $row
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function dataChanged($row) {
|
||||
if ($row->migrate_map_original_hash != $row->migrate_map_hash) {
|
||||
$return = TRUE;
|
||||
}
|
||||
else {
|
||||
$return = FALSE;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a hash of the source row.
|
||||
*
|
||||
* @param $row
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function hash($row) {
|
||||
migrate_instrument_start('MigrateSource::hash');
|
||||
$hash = md5(serialize($row));
|
||||
migrate_instrument_stop('MigrateSource::hash');
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,21 @@
|
||||
*/
|
||||
|
||||
class MigrateTeamMember {
|
||||
|
||||
protected $name;
|
||||
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
protected $emailAddress;
|
||||
|
||||
public function getEmailAddress() {
|
||||
return $this->emailAddress;
|
||||
}
|
||||
|
||||
protected $group;
|
||||
|
||||
public function getGroup() {
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
@@ -6,17 +6,99 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers your module as an implementor of Migrate-based classes.
|
||||
* Registers your module as an implementor of Migrate-based classes and
|
||||
* provides
|
||||
* default configuration for migration processes.
|
||||
*
|
||||
* @return
|
||||
* An associative array with the following keys (of which only 'api' is
|
||||
* required):
|
||||
* - api: Always 2 for any module implementing the Migrate 2 API.
|
||||
* - groups: An associative array, keyed by group machine name, defining one
|
||||
* or more migration groups. Each value is an associative array - the
|
||||
* 'title' key defines a user-visible name for the group; any other values
|
||||
* are passed as arguments to all migrations in the group.
|
||||
* - migrations: An associative array, keyed by migration machine name,
|
||||
* defining one or more migrations. Each value is an associative array -
|
||||
* any
|
||||
* keys other than the following are passed as arguments to the migration
|
||||
* constructor:
|
||||
* - class_name (required): The name of the class implementing the
|
||||
* migration.
|
||||
* - group_name: The machine name of the group containing the migration.
|
||||
* - disable_hooks: An associative array, keyed by hook name, listing hook
|
||||
* implementations to be disabled during migration. Each value is an
|
||||
* array of module names whose implementations of the hook in the key is
|
||||
* to be disabled.
|
||||
* - destination handlers: An array of classes implementing destination
|
||||
* handlers.
|
||||
* - field handlers: An array of classes implementing field handlers.
|
||||
* - wizard classes: An array of classes that provide Migrate UI wizards.
|
||||
* - wizard extenders: An array of classes that extend Migrate UI wizards.
|
||||
* Keys are the wizard classes, values are arrays of extender classes.
|
||||
*
|
||||
* See system_hook_info() for all hook groups defined by Drupal core.
|
||||
*
|
||||
* @see hook_migrate_api_alter().
|
||||
*/
|
||||
function hook_migrate_api() {
|
||||
$api = array(
|
||||
'api' => 2,
|
||||
'groups' => array(
|
||||
'legacy' => array(
|
||||
'title' => t('Import from legacy system'),
|
||||
// Default format for all content migrations
|
||||
'default_format' => 'filtered_html',
|
||||
),
|
||||
),
|
||||
'migrations' => array(
|
||||
'ExampleUser' => array(
|
||||
'class_name' => 'ExampleUserMigration',
|
||||
'group_name' => 'legacy',
|
||||
'default_role' => 'member', // Added to constructor $arguments
|
||||
),
|
||||
'ExampleNode' => array(
|
||||
'class_name' => 'ExampleNodeMigration',
|
||||
'group_name' => 'legacy',
|
||||
'default_uid' => 1, // Added to constructor $arguments
|
||||
'disable_hooks' => array(
|
||||
// Improve migration performance, and prevent accidental emails.
|
||||
'node_insert' => array(
|
||||
'expensive_module',
|
||||
'email_notification_module',
|
||||
),
|
||||
'node_update' => array(
|
||||
'expensive_module',
|
||||
'email_notification_module',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter information from all implementations of hook_migrate_api().
|
||||
*
|
||||
* @param array $info
|
||||
* An array of results from hook_migrate_api(), keyed by module name.
|
||||
*
|
||||
* @see hook_migrate_api().
|
||||
*/
|
||||
function hook_migrate_api_alter(array &$info) {
|
||||
// Override the class for another module's migration - say, to add some
|
||||
// additional preprocessing in prepareRow().
|
||||
if (isset($info['MODULE_NAME']['migrations']['ExampleNode'])) {
|
||||
$info['MODULE_NAME']['migrations']['ExampleNode']['class_name'] = 'MyBetterExampleNodeMigration';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides text to be displayed at the top of the dashboard page (migrate_ui).
|
||||
*
|
||||
* @return
|
||||
* Translated text for display on the dashboard page.
|
||||
*/
|
||||
function hook_migrate_overview() {
|
||||
return t('<p>Listed below are all the migration processes defined for migration
|
||||
|
||||
@@ -17,12 +17,15 @@ function migrate_drush_command() {
|
||||
'instrument' => 'Capture performance information (timer, memory, or all)',
|
||||
'force' => 'Force an operation to run, even if all dependencies are not satisfied',
|
||||
'group' => 'Name of the migration group to run',
|
||||
'notify' => 'Send email notification upon completion of operation',
|
||||
'wildcard' => 'Process migrations that match a certain pattern. For example, Content*.',
|
||||
);
|
||||
$items['migrate-status'] = array(
|
||||
'description' => 'List all migrations with current status.',
|
||||
'options' => array(
|
||||
'refresh' => 'Recognize new migrations and update counts',
|
||||
'group' => 'Name of the migration group to list',
|
||||
'names-only' => 'Only return names, not all the details (faster)',
|
||||
),
|
||||
'arguments' => array(
|
||||
'migration' => 'Restrict to a single migration. Optional',
|
||||
@@ -129,8 +132,6 @@ function migrate_drush_command() {
|
||||
$items['migrate-rollback'] = array(
|
||||
'description' => 'Roll back the destination objects from a given migration',
|
||||
'options' => $migration_options,
|
||||
// We will bootstrap to login from within the command callback.
|
||||
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
|
||||
'arguments' => array(
|
||||
'migration' => 'Name of migration(s) to roll back. Delimit multiple using commas.',
|
||||
),
|
||||
@@ -144,12 +145,13 @@ function migrate_drush_command() {
|
||||
'drupal dependencies' => array('migrate'),
|
||||
'aliases' => array('mr'),
|
||||
);
|
||||
$migration_options['update'] = 'In addition to processing unimported items from the source, update previously-imported items with new data';
|
||||
$migration_options['update'] = 'In addition to processing unprocessed items from the source, update previously-imported items with new data';
|
||||
$migration_options['needs-update'] =
|
||||
'Reimport up to 10K records where needs_update=1. This option is only needed when your Drupal DB is on a different DB server from your source data. Otherwise, these records get migrated with just migrate-import.';
|
||||
$migration_options['stop'] = 'Stop specified migration(s) if applicable.';
|
||||
$migration_options['rollback'] = 'Rollback specified migration(s) if applicable.';
|
||||
$migration_options['file_function'] = 'Override file function to use when migrating images.';
|
||||
$migration_options['ignore-highwater'] = 'Ignore the highwater field during migration';
|
||||
$items['migrate-import'] = array(
|
||||
'description' => 'Perform one or more migration processes',
|
||||
'options' => $migration_options,
|
||||
@@ -160,6 +162,7 @@ function migrate_drush_command() {
|
||||
'migrate-import Article' => 'Import new articles',
|
||||
'migrate-import Article --update' => 'Import new items, and also update previously-imported items',
|
||||
'migrate-import Article --idlist=4,9' => 'Import two specific articles. The ids refer to the value of the primary key in base table',
|
||||
'migrate-import Article --idlist=450:pasta,451' => 'Import two specific articles. A colon can be used to separate parts of compound keys; otherwise, compound keys match by the first key field.',
|
||||
'migrate-import Article --limit="60 seconds" --stop --rollback' =>
|
||||
'Import for up to 60 seconds after stopping and rolling back the Article migration.',
|
||||
'migrate-import Article --limit="100 items"' =>
|
||||
@@ -172,7 +175,10 @@ function migrate_drush_command() {
|
||||
);
|
||||
$items['migrate-stop'] = array(
|
||||
'description' => 'Stop an active migration operation',
|
||||
'options' => array('all' => 'Stop all active migration operations'),
|
||||
'options' => array(
|
||||
'all' => 'Stop all active migration operations',
|
||||
'group' => 'Name of a specific migration group to stop',
|
||||
),
|
||||
'arguments' => array(
|
||||
'migration' => 'Name of migration to stop',
|
||||
),
|
||||
@@ -198,25 +204,35 @@ function migrate_drush_command() {
|
||||
);
|
||||
$items['migrate-deregister'] = array(
|
||||
'description' => 'Remove all tracking of a migration',
|
||||
'options' => array('orphans' => 'Remove tracking for any migrations whose implementing class no longer exists'),
|
||||
'options' => array(
|
||||
'orphans' => 'Remove tracking for any migrations whose implementing class no longer exists',
|
||||
'group' => 'Remove tracking of a migration group, and any migrations assigned to it',
|
||||
),
|
||||
'arguments' => array(
|
||||
'migration' => 'Name of migration to deregister',
|
||||
),
|
||||
'examples' => array(
|
||||
'migrate-deregister Article' => 'Deregister the Article migration',
|
||||
'migrate-deregister --orphans' => 'Deregister any no-longer-implemented migrations',
|
||||
'migrate-deregister --group=myblog' => 'Deregister the myblog group and all migrations within it',
|
||||
),
|
||||
'drupal dependencies' => array('migrate'),
|
||||
'aliases' => array('mdreg'),
|
||||
);
|
||||
$items['migrate-auto-register'] = array(
|
||||
'description' => 'Register any newly-defined migration classes',
|
||||
'description' => 'Register any newly defined migration classes',
|
||||
'drupal dependencies' => array('migrate'),
|
||||
'aliases' => array('mar'),
|
||||
);
|
||||
$items['migrate-register'] = array(
|
||||
'description' => 'Register or reregister any statically defined migrations',
|
||||
'drupal dependencies' => array('migrate'),
|
||||
'aliases' => array('mreg'),
|
||||
);
|
||||
$items['migrate-wipe'] = array(
|
||||
'description' => 'Delete all nodes from specified content types.',
|
||||
'examples' => array(
|
||||
"migrate-wipe story article" => 'Delete all story and article nodes.',
|
||||
"migrate-wipe story article" => 'Delete all story and article nodes.',
|
||||
),
|
||||
'arguments' => array(
|
||||
'type' => 'A space delimited list of content type machine readable Ids.',
|
||||
@@ -236,9 +252,11 @@ function migrate_drush_command() {
|
||||
*/
|
||||
function drush_migrate_get_options() {
|
||||
$options = array();
|
||||
$blacklist = array('stop', 'rollback', 'update', 'all');
|
||||
$blacklist = array('stop', 'rollback', 'update', 'all', 'group');
|
||||
$command = drush_parse_command();
|
||||
foreach ($command['options'] as $key => $value) {
|
||||
$global_options = drush_get_global_options();
|
||||
$opts = array_merge($command['options'], $global_options);
|
||||
foreach ($opts as $key => $value) {
|
||||
// Strip leading --
|
||||
$key = ltrim($key, '-');
|
||||
if (!in_array($key, $blacklist)) {
|
||||
@@ -281,6 +299,7 @@ function drush_migrate_status($name = NULL) {
|
||||
try {
|
||||
$refresh = drush_get_option('refresh');
|
||||
$group_option = drupal_strtolower(drush_get_option('group'));
|
||||
$names_only = drush_get_option('names-only');
|
||||
|
||||
// Validate input and load Migration(s).
|
||||
if ($name) {
|
||||
@@ -311,61 +330,88 @@ function drush_migrate_status($name = NULL) {
|
||||
if ($group_members_count == 1) {
|
||||
// An empty line and the headers.
|
||||
$table[] = array('');
|
||||
$table[] = array(dt('Group: !name', array('!name' => $group->getName())), dt('Total'), dt('Imported'), dt('Unimported'), dt('Status'), dt('Last imported'));
|
||||
if ($names_only) {
|
||||
$table[] = array(
|
||||
dt('Group: !name',
|
||||
array('!name' => $group->getName())),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$table[] = array(
|
||||
dt('Group: !name',
|
||||
array('!name' => $group->getName())),
|
||||
dt('Total'),
|
||||
dt('Imported'),
|
||||
dt('Unprocessed'),
|
||||
dt('Status'),
|
||||
dt('Last imported'),
|
||||
);
|
||||
}
|
||||
}
|
||||
$has_counts = TRUE;
|
||||
if (method_exists($migration, 'sourceCount')) {
|
||||
$total = $migration->sourceCount($refresh);
|
||||
if ($total < 0) {
|
||||
if (!$names_only) {
|
||||
$has_counts = TRUE;
|
||||
if (method_exists($migration, 'sourceCount')) {
|
||||
$total = $migration->sourceCount($refresh);
|
||||
if ($total < 0) {
|
||||
$has_counts = FALSE;
|
||||
$total = dt('N/A');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$has_counts = FALSE;
|
||||
$total = dt('N/A');
|
||||
}
|
||||
if (method_exists($migration, 'importedCount')) {
|
||||
$imported = $migration->importedCount();
|
||||
$processed = $migration->processedCount();
|
||||
}
|
||||
else {
|
||||
$has_counts = FALSE;
|
||||
$imported = dt('N/A');
|
||||
}
|
||||
if ($has_counts) {
|
||||
$unimported = $total - $processed;
|
||||
}
|
||||
else {
|
||||
$unimported = dt('N/A');
|
||||
}
|
||||
$status = $migration->getStatus();
|
||||
switch ($status) {
|
||||
case MigrationBase::STATUS_IDLE:
|
||||
$status = dt('Idle');
|
||||
break;
|
||||
case MigrationBase::STATUS_IMPORTING:
|
||||
$status = dt('Importing');
|
||||
break;
|
||||
case MigrationBase::STATUS_ROLLING_BACK:
|
||||
$status = dt('Rolling back');
|
||||
break;
|
||||
case MigrationBase::STATUS_STOPPING:
|
||||
$status = dt('Stopping');
|
||||
break;
|
||||
case MigrationBase::STATUS_DISABLED:
|
||||
$status = dt('Disabled');
|
||||
break;
|
||||
default:
|
||||
$status = dt('Unknown');
|
||||
break;
|
||||
}
|
||||
$table[] = array(
|
||||
$migration->getMachineName(),
|
||||
$total,
|
||||
$imported,
|
||||
$unimported,
|
||||
$status,
|
||||
$migration->getLastImported(),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$has_counts = FALSE;
|
||||
$total = dt('N/A');
|
||||
$table[] = array($migration->getMachineName());
|
||||
}
|
||||
if (method_exists($migration, 'importedCount')) {
|
||||
$imported = $migration->importedCount();
|
||||
$processed = $migration->processedCount();
|
||||
}
|
||||
else {
|
||||
$has_counts = FALSE;
|
||||
$imported = dt('N/A');
|
||||
}
|
||||
if ($has_counts) {
|
||||
$unimported = $total - $processed;
|
||||
}
|
||||
else {
|
||||
$unimported = dt('N/A');
|
||||
}
|
||||
$status = $migration->getStatus();
|
||||
switch ($status) {
|
||||
case MigrationBase::STATUS_IDLE:
|
||||
$status = dt('Idle');
|
||||
break;
|
||||
case MigrationBase::STATUS_IMPORTING:
|
||||
$status = dt('Importing');
|
||||
break;
|
||||
case MigrationBase::STATUS_ROLLING_BACK:
|
||||
$status = dt('Rolling back');
|
||||
break;
|
||||
case MigrationBase::STATUS_STOPPING:
|
||||
$status = dt('Stopping');
|
||||
break;
|
||||
case MigrationBase::STATUS_DISABLED:
|
||||
$status = dt('Disabled');
|
||||
break;
|
||||
default:
|
||||
$status = dt('Unknown');
|
||||
break;
|
||||
}
|
||||
$table[] = array($migration->getMachineName(), $total, $imported, $unimported, $status, $migration->getLastImported());
|
||||
}
|
||||
}
|
||||
drush_print_table($table);
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -382,7 +428,7 @@ function drush_migrate_fields_destination($args = NULL) {
|
||||
if (method_exists($destination, 'fields')) {
|
||||
$table = array();
|
||||
foreach ($destination->fields($migration) as $machine_name => $description) {
|
||||
$table[] = array($description, $machine_name);
|
||||
$table[] = array(strip_tags($description), $machine_name);
|
||||
}
|
||||
drush_print_table($table);
|
||||
}
|
||||
@@ -390,8 +436,7 @@ function drush_migrate_fields_destination($args = NULL) {
|
||||
drush_print(dt('No fields were found.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -407,7 +452,7 @@ function drush_migrate_fields_source($args = NULL) {
|
||||
if (method_exists($source, 'fields')) {
|
||||
$table = array();
|
||||
foreach ($source->fields() as $machine_name => $description) {
|
||||
$table[] = array($description, $machine_name);
|
||||
$table[] = array(strip_tags($description), $machine_name);
|
||||
}
|
||||
drush_print_table($table);
|
||||
}
|
||||
@@ -415,8 +460,7 @@ function drush_migrate_fields_source($args = NULL) {
|
||||
drush_print(dt('No fields were found.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -438,14 +482,20 @@ function drush_migrate_mappings($args = NULL) {
|
||||
$dest_descriptions = array();
|
||||
if (method_exists($destination, 'fields')) {
|
||||
foreach ($destination->fields($migration) as $machine_name => $description) {
|
||||
$dest_descriptions[$machine_name] = $description;
|
||||
if (is_array($description)) {
|
||||
$description = reset($description);
|
||||
}
|
||||
$dest_descriptions[$machine_name] = strip_tags($description);
|
||||
}
|
||||
}
|
||||
$source = $migration->getSource();
|
||||
$src_descriptions = array();
|
||||
if (method_exists($source, 'fields')) {
|
||||
foreach ($source->fields() as $machine_name => $description) {
|
||||
$src_descriptions[$machine_name] = $description;
|
||||
if (is_array($description)) {
|
||||
$description = reset($description);
|
||||
}
|
||||
$src_descriptions[$machine_name] = strip_tags($description);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,12 +524,22 @@ function drush_migrate_mappings($args = NULL) {
|
||||
// Put out each group header
|
||||
$table = array();
|
||||
if ($full) {
|
||||
$table[] = array(dt('Destination'), dt(''), dt('Source'), dt(''), dt('Default'),
|
||||
dt('Description'));
|
||||
$table[] = array(
|
||||
dt('Destination'),
|
||||
dt(''),
|
||||
dt('Source'),
|
||||
dt(''),
|
||||
dt('Default'),
|
||||
dt('Description'),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$table[] = array(dt('Destination'), dt('Source'), dt('Default'),
|
||||
dt('Description'));
|
||||
$table[] = array(
|
||||
dt('Destination'),
|
||||
dt('Source'),
|
||||
dt('Default'),
|
||||
dt('Description'),
|
||||
);
|
||||
}
|
||||
$first = TRUE;
|
||||
|
||||
@@ -515,12 +575,22 @@ function drush_migrate_mappings($args = NULL) {
|
||||
else {
|
||||
$src_description = '';
|
||||
}
|
||||
$table[] = array($destination, $dest_description, $source, $src_description,
|
||||
$default, $mapping->getDescription());
|
||||
$table[] = array(
|
||||
$destination,
|
||||
$dest_description,
|
||||
$source,
|
||||
$src_description,
|
||||
$default,
|
||||
$mapping->getDescription(),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$table[] = array($destination, $source,
|
||||
$default, $mapping->getDescription());
|
||||
$table[] = array(
|
||||
$destination,
|
||||
$source,
|
||||
$default,
|
||||
$mapping->getDescription(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -534,8 +604,7 @@ function drush_migrate_mappings($args = NULL) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -545,14 +614,18 @@ function drush_migrate_mappings($args = NULL) {
|
||||
* Display messages for a migration.
|
||||
*/
|
||||
function drush_migrate_messages($migration_name) {
|
||||
if (!trim($migration_name)) {
|
||||
drush_log(dt('You must specify a migration name'), 'status');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$migration = MigrationBase::getInstance($migration_name);
|
||||
if (is_a($migration, 'Migration')) {
|
||||
$map = $migration->getMap();
|
||||
$message_table = $map->getMessageTable();
|
||||
$result = db_select($message_table, 'msg', array('fetch' => PDO::FETCH_ASSOC))
|
||||
->fields('msg')
|
||||
->execute();
|
||||
->fields('msg')
|
||||
->execute();
|
||||
$first = TRUE;
|
||||
$table = array();
|
||||
foreach ($result as $row) {
|
||||
@@ -582,8 +655,7 @@ function drush_migrate_messages($migration_name) {
|
||||
drush_print_table($table, TRUE, $widths);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -602,8 +674,10 @@ function drush_migrate_analyze($args = NULL) {
|
||||
if (!empty($analysis)) {
|
||||
foreach ($analysis as $field_name => $details) {
|
||||
if (!empty($details['description'])) {
|
||||
drush_print(dt('@name (@description):', array('@name' => $field_name,
|
||||
'@description' => $details['description'])));
|
||||
drush_print(dt('@name (@description):', array(
|
||||
'@name' => $field_name,
|
||||
'@description' => $details['description'],
|
||||
)));
|
||||
}
|
||||
else {
|
||||
drush_print(dt('@name:', array('@name' => $field_name)));
|
||||
@@ -616,17 +690,23 @@ function drush_migrate_analyze($args = NULL) {
|
||||
}
|
||||
else {
|
||||
drush_print(' ' . dt('Only one value present: @value',
|
||||
array('@value' => $value)));
|
||||
array('@value' => $value)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($details['is_numeric']) {
|
||||
drush_print(' ' . dt('Numeric field with a range of @min to @max',
|
||||
array('@min' => $details['min_numeric'], '@max' => $details['max_numeric'])));
|
||||
array(
|
||||
'@min' => $details['min_numeric'],
|
||||
'@max' => $details['max_numeric'],
|
||||
)));
|
||||
}
|
||||
else {
|
||||
drush_print(' ' . dt('String field with a length ranging from @min to @max',
|
||||
array('@min' => $details['min_strlen'], '@max' => $details['max_strlen'])));
|
||||
array(
|
||||
'@min' => $details['min_strlen'],
|
||||
'@max' => $details['max_strlen'],
|
||||
)));
|
||||
}
|
||||
$values = array();
|
||||
$header = NULL;
|
||||
@@ -742,10 +822,16 @@ function drush_migrate_audit($args = NULL) {
|
||||
}
|
||||
|
||||
foreach (array_diff_key($source_fields, $used_sources) as $name => $description) {
|
||||
$problems['sources_unmapped'][] = array('Field' => $name, 'Description' => $description);
|
||||
$problems['sources_unmapped'][] = array(
|
||||
'Field' => $name,
|
||||
'Description' => $description,
|
||||
);
|
||||
}
|
||||
foreach (array_diff_key($destination_fields, $used_destinations) as $name => $description) {
|
||||
$problems['destinations_unmapped'][] = array('Field' => $name, 'Description' => $description);
|
||||
$problems['destinations_unmapped'][] = array(
|
||||
'Field' => $name,
|
||||
'Description' => $description,
|
||||
);
|
||||
}
|
||||
|
||||
$problems = array_filter($problems);
|
||||
@@ -771,8 +857,7 @@ function drush_migrate_audit($args = NULL) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -783,6 +868,12 @@ function drush_migrate_audit($args = NULL) {
|
||||
*/
|
||||
function drush_migrate_rollback($args = NULL) {
|
||||
try {
|
||||
if (drush_get_option('notify', FALSE)) {
|
||||
// Capture non-informational output for mailing
|
||||
ob_start();
|
||||
ob_implicit_flush(FALSE);
|
||||
}
|
||||
|
||||
$migrations = drush_migrate_get_migrations($args);
|
||||
|
||||
// Rollback in reverse order
|
||||
@@ -804,9 +895,9 @@ function drush_migrate_rollback($args = NULL) {
|
||||
$options['limit']['unit'] = 'items';
|
||||
}
|
||||
elseif ($options['limit']['unit'] != 'seconds' &&
|
||||
$options['limit']['unit'] != 'second' &&
|
||||
$options['limit']['unit'] != 'items' &&
|
||||
$options['limit']['unit'] != 'item') {
|
||||
$options['limit']['unit'] != 'second' &&
|
||||
$options['limit']['unit'] != 'items' &&
|
||||
$options['limit']['unit'] != 'item') {
|
||||
drush_set_error(NULL, dt("Invalid limit unit '!unit'",
|
||||
array('!unit' => $options['limit']['unit'])));
|
||||
return;
|
||||
@@ -819,9 +910,9 @@ function drush_migrate_rollback($args = NULL) {
|
||||
$options['feedback']['value'] = $parts[0];
|
||||
$options['feedback']['unit'] = $parts[1];
|
||||
if ($options['feedback']['unit'] != 'seconds' &&
|
||||
$options['feedback']['unit'] != 'second' &&
|
||||
$options['feedback']['unit'] != 'items' &&
|
||||
$options['feedback']['unit'] != 'item') {
|
||||
$options['feedback']['unit'] != 'second' &&
|
||||
$options['feedback']['unit'] != 'items' &&
|
||||
$options['feedback']['unit'] != 'item') {
|
||||
drush_set_error(NULL, dt("Invalid feedback frequency unit '!unit'",
|
||||
array('!unit' => $options['feedback']['unit'])));
|
||||
return;
|
||||
@@ -862,8 +953,7 @@ function drush_migrate_rollback($args = NULL) {
|
||||
'warning');
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -873,6 +963,31 @@ function drush_migrate_rollback($args = NULL) {
|
||||
if ($_migrate_track_timer && !drush_get_context('DRUSH_DEBUG')) {
|
||||
drush_print_timers();
|
||||
}
|
||||
|
||||
// Notify user
|
||||
if (drush_get_option('notify')) {
|
||||
_drush_migrate_notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email notification to the user running the operation.
|
||||
*/
|
||||
function _drush_migrate_notify() {
|
||||
global $user;
|
||||
if ($user->uid) {
|
||||
$uid = $user->uid;
|
||||
}
|
||||
else {
|
||||
$uid = 1;
|
||||
}
|
||||
$account = user_load($uid);
|
||||
$params['account'] = $account;
|
||||
$params['output'] = ob_get_contents();
|
||||
drush_print_r(ob_get_status());
|
||||
ob_end_flush();
|
||||
drupal_mail('migrate_ui', 'import_complete', $account->mail,
|
||||
user_preferred_language($account), $params);
|
||||
}
|
||||
|
||||
function drush_migrate_get_migrations($args) {
|
||||
@@ -883,7 +998,7 @@ function drush_migrate_get_migrations($args) {
|
||||
$seen = $start === TRUE ? TRUE : FALSE;
|
||||
|
||||
foreach ($migration_objects as $name => $migration) {
|
||||
if (!$seen && (drupal_strtolower($start) . 'migration' == drupal_strtolower($name))) {
|
||||
if (!$seen && (drupal_strtolower($start) == drupal_strtolower($name))) {
|
||||
// We found our starting migration. $seen is always TRUE now.
|
||||
$seen = TRUE;
|
||||
}
|
||||
@@ -896,8 +1011,16 @@ function drush_migrate_get_migrations($args) {
|
||||
elseif ($group = drush_get_option('group')) {
|
||||
foreach ($migration_objects as $name => $migration) {
|
||||
if (drupal_strtolower($group) !=
|
||||
drupal_strtolower($migration->getGroup()->getName()) ||
|
||||
!$migration->getEnabled()) {
|
||||
drupal_strtolower($migration->getGroup()->getName()) ||
|
||||
!$migration->getEnabled()) {
|
||||
unset($migration_objects[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($wildcard = drush_get_option('wildcard')) {
|
||||
foreach ($migration_objects as $name => $migration) {
|
||||
if (!fnmatch(drupal_strtolower($wildcard), drupal_strtolower($name)) ||
|
||||
!$migration->getEnabled()) {
|
||||
unset($migration_objects[$name]);
|
||||
}
|
||||
}
|
||||
@@ -974,28 +1097,33 @@ function drush_migrate_rollback_validate($args = NULL) {
|
||||
|
||||
function drush_migrate_validate_common($args) {
|
||||
if (drush_get_option('all')) {
|
||||
if (!empty($args) || drush_get_option('group')) {
|
||||
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
|
||||
if (!empty($args) || drush_get_option('group') || drush_get_option('wildcard')) {
|
||||
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group, or --wildcard'));
|
||||
}
|
||||
}
|
||||
elseif (drush_get_option('group')) {
|
||||
if (!empty($args) || drush_get_option('all')) {
|
||||
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
|
||||
if (!empty($args) || drush_get_option('all') || drush_get_option('wildcard')) {
|
||||
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group, or --wildcard'));
|
||||
}
|
||||
}
|
||||
elseif (drush_get_option('wildcard')) {
|
||||
if (!empty($args) || drush_get_option('all') || drush_get_option('group')) {
|
||||
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group, or --wildcard'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (empty($args)) {
|
||||
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
|
||||
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group, or --wildcard'));
|
||||
}
|
||||
$machine_names = explode(',', $args);
|
||||
|
||||
foreach ($machine_names as $machine_name) {
|
||||
$machine_name = trim($machine_name);
|
||||
$class_name = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('class_name'))
|
||||
->condition('machine_name', $machine_name)
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('ms', array('class_name'))
|
||||
->condition('machine_name', $machine_name)
|
||||
->execute()
|
||||
->fetchField();
|
||||
if (!$class_name || !class_exists($class_name)) {
|
||||
drush_set_error(dt('Unrecognized migration: !name', array('!name' => $machine_name)));
|
||||
}
|
||||
@@ -1008,9 +1136,9 @@ function drush_migrate_validate_common($args) {
|
||||
$options['feedback']['value'] = $parts[0];
|
||||
$options['feedback']['unit'] = $parts[1];
|
||||
if ($options['feedback']['unit'] != 'seconds' &&
|
||||
$options['feedback']['unit'] != 'second' &&
|
||||
$options['feedback']['unit'] != 'items' &&
|
||||
$options['feedback']['unit'] != 'item') {
|
||||
$options['feedback']['unit'] != 'second' &&
|
||||
$options['feedback']['unit'] != 'items' &&
|
||||
$options['feedback']['unit'] != 'item') {
|
||||
drush_set_error(NULL, dt("Invalid feedback frequency unit '!unit'",
|
||||
array('!unit' => $options['feedback']['unit'])));
|
||||
return;
|
||||
@@ -1024,7 +1152,28 @@ function drush_migrate_validate_common($args) {
|
||||
*/
|
||||
function drush_migrate_pre_migrate_import($args = NULL) {
|
||||
if (drush_get_option('stop')) {
|
||||
drush_invoke('migrate-stop', $args);
|
||||
drush_unset_option('stop');
|
||||
try {
|
||||
/** @var Migration[] $migrations */
|
||||
$migrations = drush_migrate_get_migrations($args);
|
||||
foreach ($migrations as $migration) {
|
||||
$status = $migration->getStatus();
|
||||
if ($status == MigrationBase::STATUS_IMPORTING ||
|
||||
$status == MigrationBase::STATUS_ROLLING_BACK) {
|
||||
drush_log(dt("Stopping '!description' migration", array('!description' => $migration->getMachineName())));
|
||||
$migration->stopProcess();
|
||||
// Give the process a chance to stop.
|
||||
$count = 0;
|
||||
while ($migration->getStatus() != MigrationBase::STATUS_IDLE
|
||||
&& $count++ < 5) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
if (drush_get_option('rollback')) {
|
||||
drush_unset_option('rollback');
|
||||
@@ -1039,7 +1188,14 @@ function drush_migrate_pre_migrate_import($args = NULL) {
|
||||
* A comma delimited list of machine names, or the special name 'all'
|
||||
*/
|
||||
function drush_migrate_import($args = NULL) {
|
||||
$status = NULL;
|
||||
|
||||
try {
|
||||
if (drush_get_option('notify', FALSE)) {
|
||||
// Capture non-informational output for mailing
|
||||
ob_start();
|
||||
ob_implicit_flush(FALSE);
|
||||
}
|
||||
$migrations = drush_migrate_get_migrations($args);
|
||||
$options = array();
|
||||
if ($idlist = drush_get_option('idlist', FALSE)) {
|
||||
@@ -1055,14 +1211,18 @@ function drush_migrate_import($args = NULL) {
|
||||
if ($limit) {
|
||||
$parts = explode(' ', $limit);
|
||||
$options['limit']['value'] = $parts[0];
|
||||
$options['limit']['unit'] = $parts[1];
|
||||
if (!$options['limit']['unit']) {
|
||||
$options['limit']['unit'] = 'items';
|
||||
// Default unit.
|
||||
if (!isset($parts[1])) {
|
||||
$parts[1] = 'items';
|
||||
}
|
||||
elseif ($options['limit']['unit'] != 'seconds' &&
|
||||
$options['limit']['unit'] != 'second' &&
|
||||
$options['limit']['unit'] != 'items' &&
|
||||
$options['limit']['unit'] != 'item') {
|
||||
$options['limit']['unit'] = $parts[1];
|
||||
// Validation.
|
||||
if (!in_array($options['limit']['unit'], array(
|
||||
'seconds',
|
||||
'second',
|
||||
'items',
|
||||
'item',
|
||||
))) {
|
||||
drush_set_error(NULL, dt("Invalid limit unit '!unit'",
|
||||
array('!unit' => $options['limit']['unit'])));
|
||||
return;
|
||||
@@ -1074,9 +1234,9 @@ function drush_migrate_import($args = NULL) {
|
||||
$options['feedback']['value'] = $parts[0];
|
||||
$options['feedback']['unit'] = $parts[1];
|
||||
if ($options['feedback']['unit'] != 'seconds' &&
|
||||
$options['feedback']['unit'] != 'second' &&
|
||||
$options['feedback']['unit'] != 'items' &&
|
||||
$options['feedback']['unit'] != 'item') {
|
||||
$options['feedback']['unit'] != 'second' &&
|
||||
$options['feedback']['unit'] != 'items' &&
|
||||
$options['feedback']['unit'] != 'item') {
|
||||
drush_set_error(NULL, dt("Invalid feedback frequency unit '!unit'",
|
||||
array('!unit' => $options['feedback']['unit'])));
|
||||
return;
|
||||
@@ -1101,8 +1261,12 @@ function drush_migrate_import($args = NULL) {
|
||||
foreach ($migrations as $machine_name => $migration) {
|
||||
drush_log(dt("Importing '!description' migration",
|
||||
array('!description' => $machine_name)));
|
||||
if (drush_get_option('update')) {
|
||||
if (drush_get_option('update') && !$idlist) {
|
||||
$migration->prepareUpdate();
|
||||
|
||||
if (drush_get_option('ignore-highwater')) {
|
||||
$migration->setHighwaterField(array());
|
||||
}
|
||||
}
|
||||
if (drush_get_option('needs-update')) {
|
||||
$map_rows = $migration->getMap()->getRowsNeedingUpdate(10000);
|
||||
@@ -1168,8 +1332,10 @@ function drush_migrate_import($args = NULL) {
|
||||
elseif ($status == MigrationBase::RESULT_INCOMPLETE) {
|
||||
$stop = TRUE;
|
||||
}
|
||||
drush_backend_set_result(array('status' => $status,
|
||||
'migrations' => implode(',', array_keys($migrations))));
|
||||
drush_backend_set_result(array(
|
||||
'status' => $status,
|
||||
'migrations' => implode(',', array_keys($migrations)),
|
||||
));
|
||||
}
|
||||
|
||||
if ($stop) {
|
||||
@@ -1177,8 +1343,7 @@ function drush_migrate_import($args = NULL) {
|
||||
}
|
||||
unset($migrations[$machine_name]);
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -1190,14 +1355,21 @@ function drush_migrate_import($args = NULL) {
|
||||
if ($_migrate_track_timer && !drush_get_context('DRUSH_DEBUG')) {
|
||||
drush_print_timers();
|
||||
}
|
||||
|
||||
// Notify user
|
||||
if (drush_get_option('notify')) {
|
||||
_drush_migrate_notify();
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
//**
|
||||
// * Stop clearing or importing a given content set.
|
||||
// *
|
||||
// * @param $content_set
|
||||
// * The name of the Migration
|
||||
// */
|
||||
/**
|
||||
* Stop clearing or importing a given content set.
|
||||
*
|
||||
* @param $content_set
|
||||
* The name of the Migration
|
||||
*/
|
||||
function drush_migrate_stop($args = NULL) {
|
||||
try {
|
||||
$migrations = drush_migrate_get_migrations($args);
|
||||
@@ -1205,8 +1377,7 @@ function drush_migrate_stop($args = NULL) {
|
||||
drush_log(dt("Stopping '!description' migration", array('!description' => $migration->getMachineName())));
|
||||
$migration->stopProcess();
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -1223,42 +1394,49 @@ function drush_migrate_reset_status($args = NULL) {
|
||||
array('!description' => $migration->getMachineName())));
|
||||
$migration->resetStatus();
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregister a given migration, or all orphaned migrations. Note that the
|
||||
* migration might no longer "exist" (the class implementation might be gone),
|
||||
* so we can't count on being able to instantiate it, or use migrate_migrations().
|
||||
* Deregister a given migration, migration group, or all orphaned migrations.
|
||||
* Note that the migration might no longer "exist" (the class implementation
|
||||
* might be gone), so we can't count on being able to instantiate it, or use
|
||||
* migrate_migrations().
|
||||
*/
|
||||
function drush_migrate_deregister($args = NULL) {
|
||||
try {
|
||||
$orphans = drush_get_option('orphans');
|
||||
if ($orphans) {
|
||||
$migrations = array();
|
||||
$result = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('class_name', 'machine_name'))
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
if (!class_exists($row->class_name)) {
|
||||
$migrations[] = $row->machine_name;
|
||||
}
|
||||
}
|
||||
$group = drush_get_option('group');
|
||||
if ($group) {
|
||||
MigrateGroup::deregister($group);
|
||||
drush_log(dt("Deregistered group '!description' and all its migrations",
|
||||
array('!description' => $group)), 'success');
|
||||
}
|
||||
else {
|
||||
$migrations = explode(',', $args);
|
||||
if ($orphans) {
|
||||
$migrations = array();
|
||||
$result = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('class_name', 'machine_name'))
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
if (!class_exists($row->class_name)) {
|
||||
$migrations[] = $row->machine_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$migrations = explode(',', $args);
|
||||
}
|
||||
foreach ($migrations as $machine_name) {
|
||||
drush_migrate_deregister_migration(drupal_strtolower($machine_name));
|
||||
drush_log(dt("Deregistered '!description' migration",
|
||||
array('!description' => $machine_name)), 'success');
|
||||
}
|
||||
}
|
||||
foreach ($migrations as $machine_name) {
|
||||
drush_migrate_deregister_migration($machine_name);
|
||||
drush_log(dt("Deregistered '!description' migration",
|
||||
array('!description' => $machine_name)), 'success');
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
drush_print($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
@@ -1277,13 +1455,28 @@ function drush_migrate_deregister_migration($machine_name) {
|
||||
db_delete('migrate_status')
|
||||
->condition('machine_name', $machine_name)
|
||||
->execute();
|
||||
db_delete('migrate_field_mapping')
|
||||
->condition('machine_name', $machine_name)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any previously-unrecognized non-dynamic migrations.
|
||||
* Auto-registration is no longer supported. This command should be removed
|
||||
* entirely in a future point release.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
function drush_migrate_auto_register($args = NULL) {
|
||||
migrate_autoregister();
|
||||
drush_log(dt('The auto-registration feature has been removed. Migrations '
|
||||
. 'must now be explicitly registered.'), 'error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any migrations defined in hook_migrate_api().
|
||||
*/
|
||||
function drush_migrate_register($args = NULL) {
|
||||
migrate_static_registration();
|
||||
drush_log(dt('All statically defined migrations have been (re)registered.'), 'success');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1292,10 +1485,10 @@ function drush_migrate_auto_register($args = NULL) {
|
||||
function drush_migrate_wipe() {
|
||||
$types = func_get_args();
|
||||
$nids = db_select('node', 'n')
|
||||
->fields('n', array('nid'))
|
||||
->condition('type', $types, 'IN')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
->fields('n', array('nid'))
|
||||
->condition('type', $types, 'IN')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
$chunks = array_chunk($nids, 50);
|
||||
foreach ($chunks as $chunk) {
|
||||
node_delete_multiple($chunk);
|
||||
@@ -1307,7 +1500,7 @@ function drush_migrate_wipe() {
|
||||
function drush_migrate_print_memory() {
|
||||
global $_migrate_memory;
|
||||
$temparray = array();
|
||||
foreach ((array)$_migrate_memory as $name => $memoryrec) {
|
||||
foreach ((array) $_migrate_memory as $name => $memoryrec) {
|
||||
// We have to use timer_read() for active timers, and check the record for others
|
||||
if (isset($memoryrec['start'])) {
|
||||
$temparray[$name] = migrate_memory_read($name);
|
||||
@@ -1325,7 +1518,7 @@ function drush_migrate_print_memory() {
|
||||
foreach ($temparray as $name => $memory) {
|
||||
$count = $_migrate_memory[$name]['count'];
|
||||
if ($count > 0) {
|
||||
$avg = round($memory/$count, 0);
|
||||
$avg = round($memory / $count, 0);
|
||||
}
|
||||
else {
|
||||
$avg = 'N/A';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name = "Migrate"
|
||||
description = "Import content from external sources"
|
||||
package = "Development"
|
||||
package = "Migration"
|
||||
core = 7.x
|
||||
|
||||
files[] = includes/base.inc
|
||||
@@ -14,6 +14,7 @@ files[] = includes/map.inc
|
||||
files[] = includes/source.inc
|
||||
files[] = includes/team.inc
|
||||
files[] = migrate.mail.inc
|
||||
files[] = plugins/destinations/block_custom.inc
|
||||
files[] = plugins/destinations/entity.inc
|
||||
files[] = plugins/destinations/term.inc
|
||||
files[] = plugins/destinations/user.inc
|
||||
@@ -28,15 +29,19 @@ files[] = plugins/destinations/table_copy.inc
|
||||
files[] = plugins/destinations/menu.inc
|
||||
files[] = plugins/destinations/menu_links.inc
|
||||
files[] = plugins/destinations/statistics.inc
|
||||
files[] = plugins/destinations/variable.inc
|
||||
files[] = plugins/sources/csv.inc
|
||||
files[] = plugins/sources/db2.inc
|
||||
files[] = plugins/sources/files.inc
|
||||
files[] = plugins/sources/json.inc
|
||||
files[] = plugins/sources/list.inc
|
||||
files[] = plugins/sources/mongodb.inc
|
||||
files[] = plugins/sources/multiitems.inc
|
||||
files[] = plugins/sources/sql.inc
|
||||
files[] = plugins/sources/sqlmap.inc
|
||||
files[] = plugins/sources/mssql.inc
|
||||
files[] = plugins/sources/oracle.inc
|
||||
files[] = plugins/sources/spreadsheet.inc
|
||||
files[] = plugins/sources/xml.inc
|
||||
files[] = tests/import/options.test
|
||||
files[] = tests/plugins/destinations/comment.test
|
||||
@@ -46,9 +51,8 @@ files[] = tests/plugins/destinations/term.test
|
||||
files[] = tests/plugins/destinations/user.test
|
||||
files[] = tests/plugins/sources/xml.test
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-07
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2018-06-10
|
||||
version = "7.x-2.11"
|
||||
core = "7.x"
|
||||
project = "migrate"
|
||||
datestamp = "1352299007"
|
||||
|
||||
datestamp = "1528674486"
|
||||
|
||||
@@ -9,6 +9,8 @@ function migrate_schema() {
|
||||
$schema = array();
|
||||
$schema['migrate_status'] = migrate_schema_status();
|
||||
$schema['migrate_log'] = migrate_schema_log();
|
||||
$schema['migrate_group'] = migrate_schema_group();
|
||||
$schema['migrate_field_mapping'] = migrate_schema_field_mapping();
|
||||
return $schema;
|
||||
}
|
||||
|
||||
@@ -28,6 +30,12 @@ function migrate_schema_status() {
|
||||
'not null' => TRUE,
|
||||
'description' => 'Name of class to instantiate for this migration',
|
||||
),
|
||||
'group_name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Name of group containing migration',
|
||||
),
|
||||
'status' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
@@ -115,6 +123,74 @@ function migrate_schema_log() {
|
||||
);
|
||||
}
|
||||
|
||||
function migrate_schema_group() {
|
||||
return array(
|
||||
'description' => 'Information on migration groups',
|
||||
'fields' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Unique machine name for a migration group',
|
||||
),
|
||||
'title' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Display name for a migration group',
|
||||
),
|
||||
'arguments' => array(
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
'description' => 'A serialized array of arguments to the migration group',
|
||||
),
|
||||
),
|
||||
'primary key' => array('name'),
|
||||
);
|
||||
}
|
||||
|
||||
function migrate_schema_field_mapping() {
|
||||
return array(
|
||||
'description' => 'History of migration processes',
|
||||
'fields' => array(
|
||||
'fmid' => array(
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Unique ID for the field mapping row',
|
||||
),
|
||||
'machine_name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Parent migration for the field mapping',
|
||||
),
|
||||
'destination_field' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Destination field for the field mapping',
|
||||
),
|
||||
'source_field' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Source field for the field mapping',
|
||||
),
|
||||
'options' => array(
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
'description' => 'A serialized MigrateFieldMapping object holding all options',
|
||||
),
|
||||
),
|
||||
'primary key' => array('fmid'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
* Drop map/message tables, in case implementing classes did not.
|
||||
@@ -137,25 +213,29 @@ function migrate_uninstall() {
|
||||
->condition('module', 'migrate')
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Remove variables
|
||||
variable_del('migrate_disable_autoregistration');
|
||||
variable_del('migrate_disabled_handlers');
|
||||
variable_del('migrate_deprecation_warnings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add highwater mark
|
||||
*/
|
||||
function migrate_update_7001() {
|
||||
$ret = array();
|
||||
if (!db_field_exists('migrate_status', 'highwater')) {
|
||||
db_add_field('migrate_status', 'highwater', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Highwater mark for detecting updated content',
|
||||
)
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Highwater mark for detecting updated content',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$ret[] = t('Added highwater column to migrate_status table');
|
||||
$ret = t('Added highwater column to migrate_status table');
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -163,7 +243,6 @@ function migrate_update_7001() {
|
||||
* Add last_imported field to all map tables
|
||||
*/
|
||||
function migrate_update_7002() {
|
||||
$ret = array();
|
||||
foreach (db_find_tables('migrate_map_%') as $tablename) {
|
||||
if (!db_field_exists($tablename, 'last_imported')) {
|
||||
db_add_field($tablename, 'last_imported', array(
|
||||
@@ -175,7 +254,7 @@ function migrate_update_7002() {
|
||||
));
|
||||
}
|
||||
}
|
||||
$ret[] = t('Added last_imported column to all map tables');
|
||||
$ret = t('Added last_imported column to all map tables');
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -183,7 +262,7 @@ function migrate_update_7002() {
|
||||
* Add lastthroughput column to migrate_status
|
||||
*/
|
||||
function migrate_update_7003() {
|
||||
$ret = array();
|
||||
$ret = '';
|
||||
if (!db_field_exists('migrate_status', 'lastthroughput')) {
|
||||
db_add_field('migrate_status', 'lastthroughput', array(
|
||||
'type' => 'int',
|
||||
@@ -194,7 +273,7 @@ function migrate_update_7003() {
|
||||
);
|
||||
}
|
||||
|
||||
$ret[] = t('Added lastthroughput column to migrate_status table');
|
||||
$ret = t('Added lastthroughput column to migrate_status table');
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -202,7 +281,7 @@ function migrate_update_7003() {
|
||||
* Convert lastimported datetime field to lastimportedtime int field.
|
||||
*/
|
||||
function migrate_update_7004() {
|
||||
$ret = array();
|
||||
$ret = '';
|
||||
if (!db_field_exists('migrate_status', 'lastimportedtime')) {
|
||||
db_add_field('migrate_status', 'lastimportedtime', array(
|
||||
'type' => 'int',
|
||||
@@ -212,10 +291,10 @@ function migrate_update_7004() {
|
||||
)
|
||||
);
|
||||
|
||||
if (!db_field_exists('migrate_status', 'lastimported')) {
|
||||
if (db_field_exists('migrate_status', 'lastimported')) {
|
||||
$result = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('machine_name', 'lastimported'))
|
||||
->execute();
|
||||
->fields('ms', array('machine_name', 'lastimported'))
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$lastimportedtime = strtotime($row->lastimported);
|
||||
db_update('migrate_status')
|
||||
@@ -226,7 +305,7 @@ function migrate_update_7004() {
|
||||
|
||||
db_drop_field('migrate_status', 'lastimported');
|
||||
|
||||
$ret[] = t('Converted lastimported datetime field to lastimportedtime int field');
|
||||
$ret .= "\n" . t('Converted lastimported datetime field to lastimportedtime int field');
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
@@ -236,11 +315,11 @@ function migrate_update_7004() {
|
||||
* Add support for history logging
|
||||
*/
|
||||
function migrate_update_7005() {
|
||||
$ret = array();
|
||||
$ret = '';
|
||||
if (!db_table_exists('migrate_log')) {
|
||||
$ret[] = t('Create migrate_log table');
|
||||
$ret .= "\n" . t('Create migrate_log table');
|
||||
db_create_table('migrate_log', migrate_schema_log());
|
||||
$ret[] = t('Remove historic columns from migrate_status table');
|
||||
$ret .= "\n" . t('Remove historic columns from migrate_status table');
|
||||
db_drop_field('migrate_status', 'lastthroughput');
|
||||
db_drop_field('migrate_status', 'lastimportedtime');
|
||||
}
|
||||
@@ -252,21 +331,21 @@ function migrate_update_7005() {
|
||||
* dependencies or sourceMigration() must be changed! See CHANGELOG.txt.
|
||||
*/
|
||||
function migrate_update_7006() {
|
||||
$ret = array();
|
||||
$ret = '';
|
||||
if (!db_field_exists('migrate_status', 'class_name')) {
|
||||
db_add_field('migrate_status', 'class_name', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Name of class to instantiate for this migration',
|
||||
)
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'Name of class to instantiate for this migration',
|
||||
)
|
||||
);
|
||||
|
||||
db_query("UPDATE {migrate_status}
|
||||
SET class_name = CONCAT(machine_name, 'Migration')
|
||||
");
|
||||
$ret[] = t('Added class_name column to migrate_status table');
|
||||
$ret = t('Added class_name column to migrate_status table');
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
@@ -275,18 +354,18 @@ function migrate_update_7006() {
|
||||
* Add arguments field to migrate_status table.
|
||||
*/
|
||||
function migrate_update_7007() {
|
||||
$ret = array();
|
||||
$ret = '';
|
||||
if (!db_field_exists('migrate_status', 'arguments')) {
|
||||
db_add_field('migrate_status', 'arguments', array(
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
'description' => 'A serialized array of arguments to the migration constructor',
|
||||
)
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
'description' => 'A serialized array of arguments to the migration constructor',
|
||||
)
|
||||
|
||||
);
|
||||
$ret[] = t('Added arguments column to migrate_status table');
|
||||
$ret = t('Added arguments column to migrate_status table');
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
@@ -298,10 +377,9 @@ function migrate_update_7008() {
|
||||
// Updates can be run when the module is disabled, which would mean the
|
||||
// call to migrate_migrations() will fail. Just bail in that case...
|
||||
if (!module_exists('migrate')) {
|
||||
throw new DrupalUpdateException(t('This update cannot be run while the Migrate ' .
|
||||
'module is disabled - you must enable Migrate to run this update.'));
|
||||
throw new DrupalUpdateException(t('This update cannot be run while the Migrate module is disabled - you must enable Migrate to run this update.'));
|
||||
}
|
||||
$ret = array();
|
||||
$ret = '';
|
||||
foreach (migrate_migrations() as $migration) {
|
||||
if (is_a($migration, 'Migration')) {
|
||||
// Since we're now tracking failed/ignored rows in the map table,
|
||||
@@ -316,18 +394,18 @@ function migrate_update_7008() {
|
||||
$field = 'destid' . $index++;
|
||||
$field_schema['not null'] = FALSE;
|
||||
$map_connection->schema()->changeField($map_table, $field, $field,
|
||||
$field_schema);
|
||||
$ret[] = t('Changed !table.!field to be non-null',
|
||||
array('!table' => $map_table, '!field' => $field));
|
||||
$field_schema);
|
||||
$ret .= "\n" . t('Changed !table.!field to be non-null',
|
||||
array('!table' => $map_table, '!field' => $field));
|
||||
}
|
||||
|
||||
// Add any existing failures to the map table
|
||||
$msg_table = $map->getMessageTable();
|
||||
$msg_marked = FALSE;
|
||||
$result = $map_connection->select($msg_table, 'msg')
|
||||
->fields('msg')
|
||||
->condition('level', Migration::MESSAGE_INFORMATIONAL, '<>')
|
||||
->execute();
|
||||
->fields('msg')
|
||||
->condition('level', Migration::MESSAGE_INFORMATIONAL, '<>')
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$keys = array();
|
||||
$index = 1;
|
||||
@@ -337,13 +415,13 @@ function migrate_update_7008() {
|
||||
}
|
||||
}
|
||||
$map_connection->merge($map_table)
|
||||
->key($keys)
|
||||
->fields(array('needs_update' => MigrateMap::STATUS_FAILED))
|
||||
->execute();
|
||||
->key($keys)
|
||||
->fields(array('needs_update' => MigrateMap::STATUS_FAILED))
|
||||
->execute();
|
||||
$msg_marked = TRUE;
|
||||
}
|
||||
if ($msg_marked) {
|
||||
$ret[] = t('Marked failures in !table', array('!table' => $map_table));
|
||||
$ret .= "\n" . t('Marked failures in !table', array('!table' => $map_table));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,10 +441,11 @@ function migrate_update_7201() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add rollback_action field to all map tables
|
||||
* Add rollback_action field to all map tables in the Drupal database.
|
||||
*/
|
||||
function migrate_update_7202() {
|
||||
$ret = array();
|
||||
// Note this won't catch any prefixed tables, or any stored in the source
|
||||
// database - ensureTables() will take care of those.
|
||||
foreach (db_find_tables('migrate_map_%') as $tablename) {
|
||||
if (!db_field_exists($tablename, 'rollback_action')) {
|
||||
db_add_field($tablename, 'rollback_action', array(
|
||||
@@ -379,6 +458,121 @@ function migrate_update_7202() {
|
||||
));
|
||||
}
|
||||
}
|
||||
$ret[] = t('Added rollback_action column to all map tables');
|
||||
$ret = t('Added rollback_action column to all map tables');
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add database tracking of per-group info.
|
||||
*/
|
||||
function migrate_update_7203() {
|
||||
$ret = '';
|
||||
if (!db_table_exists('migrate_group')) {
|
||||
$ret .= t('Create migrate_group table') . "\n";
|
||||
db_create_table('migrate_group', migrate_schema_group());
|
||||
}
|
||||
if (!db_field_exists('migrate_status', 'group_name')) {
|
||||
$ret .= t('Add group relationship to migrate_status table') . "\n";
|
||||
db_add_field('migrate_status', 'group_name', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => 'default',
|
||||
'description' => 'Name of group containing migration',
|
||||
)
|
||||
);
|
||||
// Populate each migration's group_name field
|
||||
$groups = array();
|
||||
foreach (migrate_migrations() as $machine_name => $migration) {
|
||||
$group_name = $migration->getGroup()->getName();
|
||||
if (empty($group_name)) {
|
||||
$group_name = 'default';
|
||||
}
|
||||
$groups[$group_name] = $group_name;
|
||||
db_update('migrate_status')
|
||||
->fields(array('group_name' => $group_name))
|
||||
->condition('machine_name', $machine_name)
|
||||
->execute();
|
||||
}
|
||||
// Populate the migrate_group table
|
||||
foreach ($groups as $group_name) {
|
||||
$title = db_select('migrate_group', 'mg')
|
||||
->fields('mg', array('title'))
|
||||
->condition('name', $group_name)
|
||||
->execute()
|
||||
->fetchField();
|
||||
if (!$title) {
|
||||
db_insert('migrate_group')
|
||||
->fields(array(
|
||||
'name' => $group_name,
|
||||
'title' => $group_name,
|
||||
'arguments' => serialize(array()),
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add database tracking of field mappings.
|
||||
*/
|
||||
function migrate_update_7204() {
|
||||
$ret = '';
|
||||
if (!db_table_exists('migrate_field_mapping')) {
|
||||
$ret = t('Create migrate_field_mapping table');
|
||||
db_create_table('migrate_field_mapping', migrate_schema_field_mapping());
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove obsolete autoregistration disablement.
|
||||
*/
|
||||
function migrate_update_7205() {
|
||||
variable_del('migrate_disable_autoregistration');
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace three-column PK with a simple serial.
|
||||
*/
|
||||
function migrate_update_7206() {
|
||||
if (!db_field_exists('migrate_field_mapping', 'fmid')) {
|
||||
db_drop_primary_key('migrate_field_mapping');
|
||||
db_add_field('migrate_field_mapping', 'fmid',
|
||||
array(
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Unique ID for the field mapping row',
|
||||
),
|
||||
array(
|
||||
'primary key' => array('fmid'),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we remove an empty 'default' group created by the previous updates.
|
||||
*/
|
||||
function migrate_update_7207() {
|
||||
$rows = db_select('migrate_group', 'mg')
|
||||
->fields('mg', array('name'))
|
||||
->condition('name', 'default')
|
||||
->execute()
|
||||
->rowCount();
|
||||
if ($rows > 0) {
|
||||
$rows = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('machine_name'))
|
||||
->condition('group_name', 'default')
|
||||
->execute()
|
||||
->rowCount();
|
||||
if ($rows == 0) {
|
||||
db_delete('migrate_group')
|
||||
->condition('name', 'default')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
class MigrateMailIgnore extends DefaultMailSystem {
|
||||
|
||||
/**
|
||||
* On an email request, do nothing and say we did.
|
||||
*
|
||||
@@ -14,6 +15,7 @@ class MigrateMailIgnore extends DefaultMailSystem {
|
||||
*
|
||||
* @param $message
|
||||
* A message array, as described in hook_mail_alter().
|
||||
*
|
||||
* @return
|
||||
* TRUE if the mail was successfully accepted, otherwise FALSE.
|
||||
*/
|
||||
|
||||
@@ -15,16 +15,46 @@
|
||||
|
||||
define('MIGRATE_API_VERSION', 2);
|
||||
|
||||
define('MIGRATE_ACCESS_BASIC', 'migration information');
|
||||
define('MIGRATE_ACCESS_ADVANCED', 'advanced migration information');
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function migrate_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/help#migrate':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Migrate module provides a flexible framework for migrating content into Drupal from other sources (e.g., when converting a web site from another CMS to Drupal). Out-of-the-box, support for creating Drupal nodes, taxonomy terms, comments, and users are included. Plugins permit migration of other types of content. For more information, see the online documentation for the <a href="@handbook">Migrate module</a>.', array('@handbook' => 'http://drupal.org/migrate')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Getting Started') . '</dt>';
|
||||
$output .= '<dd>' . t('To get started, enable the migrate_example module and browse to admin/content/migrate to see its dashboard. The code for this migration is in migrate_example/beer.inc (advanced examples are in wine.inc). Mimic that file in order to specify your own migrations.') . '</dd>';
|
||||
$output .= '<dt>' . t('Migrate Support') . '</dt>';
|
||||
$output .= '<dd>' . t('The Migrate module itself has support for migration into core objects. Some built-in support for migration involving contrib modules is in the migrate_extras module.') . '</dd>';
|
||||
$output .= '<dt>' . t('Migrate Extended Support') . '</dt>';
|
||||
$output .= '<dd>' . t('The best place to implement migration support for a contributed module is in that module, not in the Migrate or Migrate Extras modules. That way, the migration support is always self-consistent with the current module implementation - it is not practical for the migrate modules to keep up with changes to all other contrib modules.') . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of all active migrations, ordered by dependencies. To be
|
||||
* recognized, a class must be non-abstract, and derived from MigrationBase.
|
||||
*
|
||||
* @param $reset
|
||||
* If TRUE, the static cache of migrations will be flushed before attempting to
|
||||
* reinstantiate all active migrations. This can be important for script runs
|
||||
* where migration classes may be dynamically registered.
|
||||
*
|
||||
* @return
|
||||
* Array of migration objects, keyed by the machine name.
|
||||
*/
|
||||
function migrate_migrations() {
|
||||
function migrate_migrations($reset = NULL) {
|
||||
static $migrations = array();
|
||||
if (!empty($migrations)) {
|
||||
if (!empty($migrations) && empty($reset)) {
|
||||
return $migrations;
|
||||
}
|
||||
|
||||
@@ -32,70 +62,47 @@ function migrate_migrations() {
|
||||
// make sure any dynamic migrations defined in hook_migrate_api() get registered.
|
||||
migrate_get_module_apis(TRUE);
|
||||
|
||||
$dependencies_list = array();
|
||||
$dependent_migrations = array();
|
||||
$required_migrations = array();
|
||||
|
||||
$result = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('machine_name', 'class_name', 'arguments'))
|
||||
->execute();
|
||||
->fields('ms', array('machine_name', 'class_name'))
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
if (class_exists($row->class_name)) {
|
||||
$reflect = new ReflectionClass($row->class_name);
|
||||
if (!$reflect->isAbstract() && $reflect->isSubclassOf('MigrationBase')) {
|
||||
$arguments = unserialize($row->arguments);
|
||||
if (!$arguments || !is_array($arguments)) {
|
||||
$arguments = array();
|
||||
}
|
||||
$migration = MigrationBase::getInstance($row->machine_name,
|
||||
$row->class_name, $arguments);
|
||||
$dependencies = $migration->getDependencies();
|
||||
if (count($dependencies) > 0) {
|
||||
// Set classes with dependencies aside for reordering
|
||||
$dependent_migrations[$row->machine_name] = $migration;
|
||||
$required_migrations += $dependencies;
|
||||
}
|
||||
else {
|
||||
// No dependencies, just add
|
||||
$migrations[$row->machine_name] = $migration;
|
||||
$migration = MigrationBase::getInstance($row->machine_name);
|
||||
if ($migration) {
|
||||
$dependencies = $migration->getDependencies();
|
||||
$dependencies_list[$row->machine_name] = $dependencies;
|
||||
if (count($dependencies) > 0) {
|
||||
// Set classes with dependencies aside for reordering
|
||||
$dependent_migrations[$row->machine_name] = $migration;
|
||||
$required_migrations += $dependencies;
|
||||
}
|
||||
else {
|
||||
// No dependencies, just add
|
||||
$migrations[$row->machine_name] = $migration;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
MigrationBase::displayMessage(t('Class !class is no longer a valid concrete migration class',
|
||||
array('!class' => $row->class_name)));
|
||||
array('!class' => $row->class_name)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
MigrationBase::displayMessage(t('Class !class no longer exists',
|
||||
array('!class' => $row->class_name)));
|
||||
MigrationBase::displayMessage(t('Class !class could not be found',
|
||||
array('!class' => $row->class_name)));
|
||||
}
|
||||
}
|
||||
|
||||
// Scan modules with dependencies - we'll take 20 passes at it before
|
||||
// giving up
|
||||
// TODO: Can we share code with _migrate_class_list()?
|
||||
$iterations = 0;
|
||||
while (count($dependent_migrations) > 0) {
|
||||
if ($iterations++ > 20) {
|
||||
$migration_names = implode(',', array_keys($dependent_migrations));
|
||||
throw new MigrateException(t('Failure to sort migration list - most likely due ' .
|
||||
'to circular dependencies involving !migration_names',
|
||||
array('!migration_names' => $migration_names)));
|
||||
}
|
||||
foreach ($dependent_migrations as $name => $migration) {
|
||||
$ready = TRUE;
|
||||
// Scan all the dependencies for this class and make sure they're all
|
||||
// in the final list
|
||||
foreach ($migration->getDependencies() as $dependency) {
|
||||
if (!isset($migrations[$dependency])) {
|
||||
$ready = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ready) {
|
||||
// Yes they are! Move this class to the final list
|
||||
$migrations[$name] = $migration;
|
||||
unset($dependent_migrations[$name]);
|
||||
}
|
||||
$ordered_migrations = migrate_order_dependencies($dependencies_list);
|
||||
foreach ($ordered_migrations as $name) {
|
||||
if (!isset($migrations[$name])) {
|
||||
$migrations[$name] = $dependent_migrations[$name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,11 +115,19 @@ function migrate_migrations() {
|
||||
$final_migrations[$name] = array();
|
||||
}
|
||||
|
||||
// Fill in the grouped list
|
||||
// Fill in the grouped list.
|
||||
foreach ($migrations as $machine_name => $migration) {
|
||||
$final_migrations[$migration->getGroup()->getName()][$machine_name] = $migration;
|
||||
if (!method_exists($migration, 'getGroup')) {
|
||||
MigrationBase::displayMessage(t('Migration !machine_name is not a valid Migration dependency.', array(
|
||||
'!machine_name' => $machine_name,
|
||||
)));
|
||||
}
|
||||
else {
|
||||
$final_migrations[$migration->getGroup()
|
||||
->getName()][$machine_name] = $migration;
|
||||
}
|
||||
}
|
||||
// Then flatten the list
|
||||
// Flatten the grouped list.
|
||||
$migrations = array();
|
||||
foreach ($final_migrations as $group_name => $group_migrations) {
|
||||
foreach ($group_migrations as $machine_name => $migration) {
|
||||
@@ -123,59 +138,6 @@ function migrate_migrations() {
|
||||
return $migrations;
|
||||
}
|
||||
|
||||
/**
|
||||
* On request, scan the Drupal code registry for any new migration classes
|
||||
* for us to register in migrate_status.
|
||||
*/
|
||||
function migrate_autoregister() {
|
||||
// Make sure the registry is up-to-date on all available classes.
|
||||
require_once 'includes/registry.inc';
|
||||
_registry_update();
|
||||
|
||||
// Get list of modules implementing Migrate API
|
||||
$modules = array_keys(migrate_get_module_apis(TRUE));
|
||||
|
||||
// Get list of classes we already know about
|
||||
$existing_classes = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('class_name'))
|
||||
->execute()
|
||||
->fetchCol();
|
||||
|
||||
// Discover class names registered with Drupal by modules implementing our API
|
||||
$result = db_select('registry', 'r')
|
||||
->fields('r', array('name'))
|
||||
->condition('type', 'class')
|
||||
->condition('module', $modules, 'IN')
|
||||
->condition('filename', '%.test', 'NOT LIKE')
|
||||
->execute();
|
||||
|
||||
foreach ($result as $record) {
|
||||
$class_name = $record->name;
|
||||
// If we already know about this class, skip it
|
||||
if (isset($existing_classes[$class_name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate it's an implemented subclass of the parent class
|
||||
// Ignore errors
|
||||
try {
|
||||
$class = new ReflectionClass($class_name);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
continue;
|
||||
}
|
||||
if (!$class->isAbstract() && $class->isSubclassOf('MigrationBase')) {
|
||||
// Verify that it's not a dynamic class (the implementor will be responsible
|
||||
// for registering those).
|
||||
$dynamic = call_user_func(array($class_name, 'isDynamic'));
|
||||
if (!$dynamic) {
|
||||
// OK, this is a new non-dynamic migration class, register it
|
||||
MigrationBase::registerMigration($class_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke any available handlers attached to a given destination type.
|
||||
* If any handlers have dependencies defined, they will be invoked after
|
||||
@@ -185,8 +147,8 @@ function migrate_autoregister() {
|
||||
* Destination type ('Node', 'User', etc.) - generally the same string as
|
||||
* the destination class name without the MigrateDestination prefix.
|
||||
* @param $method
|
||||
* Method name such as 'prepare' (called at the beginning of an import operation)
|
||||
* or 'complete' (called at the end of an import operation).
|
||||
* Method name such as 'prepare' (called at the beginning of an import
|
||||
* operation) or 'complete' (called at the end of an import operation).
|
||||
* @param ...
|
||||
* Parameters to be passed to the handler.
|
||||
*/
|
||||
@@ -199,7 +161,7 @@ function migrate_handler_invoke_all($destination, $method) {
|
||||
$disabled = unserialize(variable_get('migrate_disabled_handlers', serialize(array())));
|
||||
foreach ($class_list as $class_name => $handler) {
|
||||
if (!in_array($class_name, $disabled) && $handler->handlesType($destination)
|
||||
&& method_exists($handler, $method)) {
|
||||
&& method_exists($handler, $method)) {
|
||||
migrate_instrument_start($class_name . '->' . $method);
|
||||
$result = call_user_func_array(array($handler, $method), $args);
|
||||
migrate_instrument_stop($class_name . '->' . $method);
|
||||
@@ -216,8 +178,6 @@ function migrate_handler_invoke_all($destination, $method) {
|
||||
|
||||
/**
|
||||
* Invoke any available handlers attached to a given field type.
|
||||
* If any handlers have dependencies defined, they will be invoked after
|
||||
* the specified handlers.
|
||||
*
|
||||
* @param $entity
|
||||
* The object we are building up before calling example_save().
|
||||
@@ -231,18 +191,21 @@ function migrate_handler_invoke_all($destination, $method) {
|
||||
* @param $method
|
||||
* Handler method to call (defaults to prepare()).
|
||||
*/
|
||||
function migrate_field_handler_invoke_all($entity, array $field_info, array $instance,
|
||||
array $values, $method = 'prepare') {
|
||||
function migrate_field_handler_invoke_all($entity, array $field_info,
|
||||
array $instance, array $values, $method = 'prepare') {
|
||||
$return = array();
|
||||
$type = $field_info['type'];
|
||||
$class_list = _migrate_class_list('MigrateFieldHandler');
|
||||
$disabled = unserialize(variable_get('migrate_disabled_handlers', serialize(array())));
|
||||
$disabled = unserialize(variable_get('migrate_disabled_handlers',
|
||||
serialize(array())));
|
||||
$handler_called = FALSE;
|
||||
foreach ($class_list as $class_name => $handler) {
|
||||
if (!in_array($class_name, $disabled) && $handler->handlesType($type)
|
||||
&& method_exists($handler, $method)) {
|
||||
&& method_exists($handler, $method)) {
|
||||
migrate_instrument_start($class_name . '->' . $method);
|
||||
$result = call_user_func_array(array($handler, $method),
|
||||
array($entity, $field_info, $instance, $values));
|
||||
$handler_called = TRUE;
|
||||
migrate_instrument_stop($class_name . '->' . $method);
|
||||
if (isset($result) && is_array($result)) {
|
||||
$return = array_merge_recursive($return, $result);
|
||||
@@ -252,27 +215,42 @@ function migrate_field_handler_invoke_all($entity, array $field_info, array $ins
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$handler_called && $method == 'prepare') {
|
||||
$handler = new MigrateDefaultFieldHandler();
|
||||
migrate_instrument_start('MigrateDefaultFieldHandler->prepare');
|
||||
$result = call_user_func_array(array($handler, 'prepare'),
|
||||
array($entity, $field_info, $instance, $values));
|
||||
migrate_instrument_stop('MigrateDefaultFieldHandler->prepare');
|
||||
if (isset($result) && is_array($result)) {
|
||||
$return = array_merge_recursive($return, $result);
|
||||
}
|
||||
elseif (isset($result)) {
|
||||
$return[] = $result;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given parent class, identify and instantiate objects for any non-abstract
|
||||
* classes derived from the parent, returning an array of the objects indexed by
|
||||
* class name. The array will be ordered such that any classes with dependencies
|
||||
* are listed after the classes they are dependent on.
|
||||
* For a given parent class, identify and instantiate objects for any
|
||||
* non-abstract classes derived from the parent, returning an array of the
|
||||
* objects indexed by class name. The array will be ordered such that any
|
||||
* classes with dependencies are listed after the classes they are dependent
|
||||
* on.
|
||||
*
|
||||
* @param $parent_class
|
||||
* Name of a class from which results will be derived.
|
||||
*
|
||||
* @return
|
||||
* Array of objects, keyed by the class name.
|
||||
*/
|
||||
function _migrate_class_list($parent_class) {
|
||||
// Get info on modules implementing Migrate API
|
||||
static $module_info;
|
||||
if (!isset($modules)) {
|
||||
if (!isset($module_info)) {
|
||||
$module_info = migrate_get_module_apis();
|
||||
}
|
||||
$modules = array_keys($module_info);
|
||||
|
||||
static $class_lists = array();
|
||||
if (!isset($class_lists[$parent_class])) {
|
||||
@@ -291,90 +269,6 @@ function _migrate_class_list($parent_class) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Avoid scrounging the registry for handler classes if possible.
|
||||
if (variable_get('migrate_disable_autoregistration', FALSE)) {
|
||||
return $class_lists[$parent_class];
|
||||
}
|
||||
$dependent_classes = array();
|
||||
$required_classes = array();
|
||||
// Discover class names registered with Drupal by modules implementing our API
|
||||
$result = db_select('registry', 'r')
|
||||
->fields('r', array('name'))
|
||||
->condition('type', 'class')
|
||||
->condition('module', $modules, 'IN')
|
||||
->condition('filename', '%.test', 'NOT LIKE')
|
||||
->execute();
|
||||
|
||||
foreach ($result as $record) {
|
||||
// Validate it's an implemented subclass of the parent class
|
||||
// We can get funky errors here, ignore them (and the class that caused them)
|
||||
try {
|
||||
$class = new ReflectionClass($record->name);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
continue;
|
||||
}
|
||||
if (!$class->isAbstract() && $class->isSubclassOf($parent_class)) {
|
||||
// If the constructor has required parameters, this may fail. We will
|
||||
// silently ignore - it is up to the implementor of such a class to
|
||||
// instantiate it in hook_migrations_alter().
|
||||
try {
|
||||
$object = new $record->name;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
unset($object);
|
||||
}
|
||||
if (isset($object)) {
|
||||
$dependencies = $object->getDependencies();
|
||||
if (count($dependencies) > 0) {
|
||||
// Set classes with dependencies aside for reordering
|
||||
$dependent_classes[$record->name] = $object;
|
||||
$required_classes += $dependencies;
|
||||
}
|
||||
else {
|
||||
// No dependencies, just add
|
||||
$class_lists[$parent_class][$record->name] = $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that each depended-on class at least exists
|
||||
foreach ($required_classes as $class_name) {
|
||||
if ((!isset($dependent_classes[$class_name])) && !isset($class_lists[$parent_class][$class_name])) {
|
||||
throw new MigrateException(t('Dependency on non-existent class !class - make sure ' .
|
||||
'you have added the file defining !class to the .info file.',
|
||||
array('!class' => $class_name)));
|
||||
}
|
||||
}
|
||||
|
||||
// Scan modules with dependencies - we'll take 20 passes at it before
|
||||
// giving up
|
||||
$iterations = 0;
|
||||
while (count($dependent_classes) > 0) {
|
||||
if ($iterations++ > 20) {
|
||||
$class_names = implode(',', array_keys($dependent_classes));
|
||||
throw new MigrateException(t('Failure to sort class list - most likely due ' .
|
||||
'to circular dependencies involving !class_names.',
|
||||
array('!class_names' => $class_names)));
|
||||
}
|
||||
foreach ($dependent_classes as $name => $object) {
|
||||
$ready = TRUE;
|
||||
// Scan all the dependencies for this class and make sure they're all
|
||||
// in the final list
|
||||
foreach ($object->getDependencies() as $dependency) {
|
||||
if (!isset($class_lists[$parent_class][$dependency])) {
|
||||
$ready = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ready) {
|
||||
// Yes they are! Move this class to the final list
|
||||
$class_lists[$parent_class][$name] = $object;
|
||||
unset($dependent_classes[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $class_lists[$parent_class];
|
||||
}
|
||||
@@ -387,9 +281,26 @@ function migrate_hook_info() {
|
||||
$hooks['migrate_api'] = array(
|
||||
'group' => 'migrate',
|
||||
);
|
||||
$hooks['migrate_api_alter'] = array(
|
||||
'group' => 'migrate',
|
||||
);
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_permission().
|
||||
*/
|
||||
function migrate_permission() {
|
||||
return array(
|
||||
MIGRATE_ACCESS_BASIC => array(
|
||||
'title' => t('Access to basic migration information'),
|
||||
),
|
||||
MIGRATE_ACCESS_ADVANCED => array(
|
||||
'title' => t('Access to advanced migration information'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of modules that support the current migrate API.
|
||||
*/
|
||||
@@ -405,26 +316,108 @@ function migrate_get_module_apis($reset = FALSE) {
|
||||
$info = $function();
|
||||
if (isset($info['api']) && $info['api'] == MIGRATE_API_VERSION) {
|
||||
$cache[$module] = $info;
|
||||
// Register any migrations defined via the hook.
|
||||
if (isset($info['migrations']) && is_array($info['migrations'])) {
|
||||
foreach ($info['migrations'] as $machine_name => $arguments) {
|
||||
MigrationBase::registerMigration($arguments['class_name'],
|
||||
$machine_name, $arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('%function supports Migrate API version %modversion,
|
||||
Migrate module API version is %version - migration support not loaded.',
|
||||
array('%function' => $function, '%modversion' => $info['api'],
|
||||
'%version' => MIGRATE_API_VERSION)));
|
||||
array(
|
||||
'%function' => $function,
|
||||
'%modversion' => $info['api'],
|
||||
'%version' => MIGRATE_API_VERSION,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// Allow modules to alter the migration information.
|
||||
drupal_alter('migrate_api', $cache);
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any migrations defined in hook_migrate_api().
|
||||
*
|
||||
* @param array $machine_names
|
||||
* If populated, only (re)register the specified migrations.
|
||||
*/
|
||||
function migrate_static_registration($machine_names = array()) {
|
||||
$module_info = migrate_get_module_apis(TRUE);
|
||||
foreach ($module_info as $module => $info) {
|
||||
// Register any groups defined via the hook.
|
||||
if (isset($info['groups']) && is_array($info['groups'])) {
|
||||
foreach ($info['groups'] as $name => $arguments) {
|
||||
$title = $arguments['title'];
|
||||
unset($arguments['title']);
|
||||
MigrateGroup::register($name, $title, $arguments);
|
||||
}
|
||||
}
|
||||
// Register any migrations defined via the hook.
|
||||
if (isset($info['migrations']) && is_array($info['migrations'])) {
|
||||
foreach ($info['migrations'] as $machine_name => $arguments) {
|
||||
// If we have an explicit list to register, skip any not in the list.
|
||||
if (!empty($machine_names) && !in_array($machine_name, $machine_names)) {
|
||||
continue;
|
||||
}
|
||||
$class_name = $arguments['class_name'];
|
||||
unset($arguments['class_name']);
|
||||
// Call the right registerMigration implementation. Note that this means
|
||||
// that classes that override registerMigration() must always call it
|
||||
// directly, they cannot register those classes by defining them in
|
||||
// hook_migrate_api() and expect their extension to be called.
|
||||
if (is_subclass_of($class_name, 'Migration')) {
|
||||
Migration::registerMigration($class_name, $machine_name, $arguments);
|
||||
}
|
||||
else {
|
||||
MigrationBase::registerMigration($class_name, $machine_name, $arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a topological sort on our dependencies graph.
|
||||
*/
|
||||
function migrate_order_dependencies($dependencies) {
|
||||
$visited = array();
|
||||
$list = array();
|
||||
|
||||
foreach (array_keys($dependencies) as $name) {
|
||||
$visited[$name] = FALSE;
|
||||
}
|
||||
|
||||
foreach (array_keys($dependencies) as $name) {
|
||||
migrate_visit_dependent($dependencies, $name, $list, $visited);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Depth-first search for independent migrations.
|
||||
*/
|
||||
function migrate_visit_dependent($dependencies, $name, &$list, &$visited) {
|
||||
if ($visited[$name]) {
|
||||
if ($list[$name]) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw new MigrateException(t('Failure to sort migration list due to circular dependencies involving %name.', array('%name' => $name)));
|
||||
}
|
||||
}
|
||||
|
||||
$visited[$name] = TRUE;
|
||||
if (isset($dependencies[$name])) {
|
||||
foreach ($dependencies[$name] as $dependent) {
|
||||
migrate_visit_dependent($dependencies, $dependent, $list, $visited);
|
||||
}
|
||||
}
|
||||
|
||||
$list[$name] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_watchdog().
|
||||
* Find the migration that is currently running and notify it.
|
||||
@@ -483,6 +476,7 @@ function migrate_memory_start($name) {
|
||||
*
|
||||
* @param name
|
||||
* The name of the memory measurement.
|
||||
*
|
||||
* @return
|
||||
* The change in bytes since the last start.
|
||||
*/
|
||||
@@ -506,9 +500,11 @@ function migrate_memory_read($name) {
|
||||
*
|
||||
* @param name
|
||||
* The name of the memory measurement.
|
||||
*
|
||||
* @return
|
||||
* A memory array. The array contains the number of times the memory has been
|
||||
* started and stopped (count) and the accumulated memory difference value in bytes.
|
||||
* started and stopped (count) and the accumulated memory difference value in
|
||||
* bytes.
|
||||
*/
|
||||
function migrate_memory_stop($name) {
|
||||
global $_migrate_memory;
|
||||
@@ -531,9 +527,9 @@ function migrate_memory_stop($name) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Start measuring time and (optionally) memory consumption over a section of code.
|
||||
* Note that the memory consumption measurement is generally not useful in
|
||||
* lower areas of the code, where data is being generated that will be freed
|
||||
* Start measuring time and (optionally) memory consumption over a section of
|
||||
* code. Note that the memory consumption measurement is generally not useful
|
||||
* in lower areas of the code, where data is being generated that will be freed
|
||||
* by the next call to the same area. For example, measuring the memory
|
||||
* consumption of db_query is not going to be helpful.
|
||||
*
|
||||
@@ -569,7 +565,8 @@ function migrate_instrument_stop($name) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Call hook_migrate_overview for overall documentation on implemented migrations.
|
||||
* Call hook_migrate_overview for overall documentation on implemented
|
||||
* migrations.
|
||||
*/
|
||||
function migrate_overview() {
|
||||
$overview = '';
|
||||
@@ -579,3 +576,28 @@ function migrate_overview() {
|
||||
}
|
||||
return $overview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_modules_enabled.
|
||||
*/
|
||||
function migrate_modules_enabled($modules) {
|
||||
if (array_intersect($modules, module_implements('migrate_api'))) {
|
||||
migrate_static_registration();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_module_implements_alter().
|
||||
*/
|
||||
function migrate_module_implements_alter(&$implementation, $hook) {
|
||||
// Ensure that the Migration class exists, as different bootstrap phases may
|
||||
// not have included migration.inc yet.
|
||||
if (class_exists('Migration') && $migration = Migration::currentMigration()) {
|
||||
$disable_hooks = $migration->getDisableHooks();
|
||||
if (isset($disable_hooks[$hook])) {
|
||||
foreach ($disable_hooks[$hook] as $module) {
|
||||
unset($implementation[$module]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,34 +16,43 @@
|
||||
* - Comments to be attached to the beer nodes are described in the source
|
||||
* migrate_example_beer_comment table.
|
||||
*
|
||||
* We will use the Migrate API to import and transform this data and turn it into
|
||||
* a working Drupal system.
|
||||
* We will use the Migrate API to import and transform this data and turn it
|
||||
* into a working Drupal site.
|
||||
*/
|
||||
|
||||
/**
|
||||
* To define a migration process from a set of source data to a particular
|
||||
* kind of Drupal object (for example, a specific node type), you define
|
||||
* a class derived from Migration. You must define a constructor to initialize
|
||||
* your migration object. By default, your class name will be the "machine name"
|
||||
* of the migration, by which you refer to it. Note that the machine name is
|
||||
* case-sensitive.
|
||||
* your migration object.
|
||||
*
|
||||
* For your classes to be instantiated so they can be used to import content,
|
||||
* you must register them - look at migrate_example.migrate.inc to see how
|
||||
* registration works. Right now, it's important to understand that each
|
||||
* migration will have a unique "machine name", which is displayed in the UI
|
||||
* and is used to reference the migration in drush commands.
|
||||
*
|
||||
* In any serious migration project, you will find there are some options
|
||||
* which are common to the individual migrations you're implementing. You can
|
||||
* define an abstract intermediate class derived from Migration, then derive your
|
||||
* individual migrations from that, to share settings, utility functions, etc.
|
||||
* define an abstract intermediate class derived from Migration, then derive
|
||||
* your individual migrations from that, to share settings, utility functions,
|
||||
* etc.
|
||||
*/
|
||||
abstract class BasicExampleMigration extends DynamicMigration {
|
||||
public function __construct() {
|
||||
// Always call the parent constructor first for basic setup
|
||||
parent::__construct();
|
||||
abstract class BasicExampleMigration extends Migration {
|
||||
|
||||
// A Migration constructor takes an array of arguments as its first parameter.
|
||||
// The arguments must be passed through to the parent constructor.
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
|
||||
// With migrate_ui enabled, migration pages will indicate people involved in
|
||||
// the particular migration, with their role and contact info. We default the
|
||||
// list in the shared class; it can be overridden for specific migrations.
|
||||
$this->team = array(
|
||||
new MigrateTeamMember('Liz Taster', 'ltaster@example.com', t('Product Owner')),
|
||||
new MigrateTeamMember('Larry Brewer', 'lbrewer@example.com', t('Implementor')),
|
||||
new MigrateTeamMember('Liz Taster', 'ltaster@example.com',
|
||||
t('Product Owner')),
|
||||
new MigrateTeamMember('Larry Brewer', 'lbrewer@example.com',
|
||||
t('Implementor')),
|
||||
);
|
||||
|
||||
// Individual mappings in a migration can be linked to a ticket or issue
|
||||
@@ -62,114 +71,129 @@ abstract class BasicExampleMigration extends DynamicMigration {
|
||||
* this will receive data that originated from the source and has been mapped
|
||||
* by the Migration class, and create Drupal objects.
|
||||
* $this->map - An instance of a class derived from MigrateMap, this will keep
|
||||
* track of which source items have been imported and what destination objects
|
||||
* they map to.
|
||||
* Mappings - Use $this->addFieldMapping to tell the Migration class what source
|
||||
* fields correspond to what destination fields, and additional information
|
||||
* associated with the mappings.
|
||||
* track of which source items have been imported and what destination
|
||||
* objects they map to.
|
||||
* Field mappings - Use $this->addFieldMapping to tell the Migration class what
|
||||
* source fields correspond to what destination fields, and additional
|
||||
* information associated with the mappings.
|
||||
*/
|
||||
class BeerTermMigration extends BasicExampleMigration {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
// Human-friendly description of your migration process. Be as detailed as you
|
||||
// like.
|
||||
$this->description = t('Migrate styles from the source database to taxonomy terms');
|
||||
|
||||
// Create a map object for tracking the relationships between source rows
|
||||
// and their resulting Drupal objects. Usually, you'll use the MigrateSQLMap
|
||||
// class, which uses database tables for tracking. Pass the machine name
|
||||
// (BeerTerm) of this migration to use in generating map and message tables.
|
||||
// And, pass schema definitions for the primary keys of the source and
|
||||
// destination - we need to be explicit for our source, but the destination
|
||||
// class knows its schema already.
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array(
|
||||
'style' => array('type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Topic ID',
|
||||
)
|
||||
),
|
||||
MigrateDestinationTerm::getKeySchema()
|
||||
);
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
// Human-friendly description of your migration process. Be as detailed as
|
||||
// you like.
|
||||
$this->description =
|
||||
t('Migrate styles from the source database to taxonomy terms');
|
||||
|
||||
// In this example, we're using tables that have been added to the existing
|
||||
// Drupal database but which are not Drupal tables. You can examine the
|
||||
// various tables (starting here with migrate_example_beer_topic) using a
|
||||
// database browser like phpMyAdmin.
|
||||
// database browser such as phpMyAdmin.
|
||||
// First, we set up a query for this data. Note that by ordering on
|
||||
// style_parent, we guarantee root terms are migrated first, so the
|
||||
// parent_name mapping below will find that the parent exists.
|
||||
$query = db_select('migrate_example_beer_topic', 'met')
|
||||
->fields('met', array('style', 'details', 'style_parent', 'region', 'hoppiness'))
|
||||
// This sort assures that parents are saved before children.
|
||||
->orderBy('style_parent', 'ASC');
|
||||
->fields('met', array(
|
||||
'style',
|
||||
'details',
|
||||
'style_parent',
|
||||
'region',
|
||||
'hoppiness',
|
||||
))
|
||||
// This sort assures that parents are saved before children.
|
||||
->orderBy('style_parent', 'ASC');
|
||||
|
||||
// Create a MigrateSource object, which manages retrieving the input data.
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
|
||||
// Set up our destination - terms in the migrate_example_beer_styles vocabulary
|
||||
$this->destination = new MigrateDestinationTerm('migrate_example_beer_styles');
|
||||
// Set up our destination - terms in the migrate_example_beer_styles
|
||||
// vocabulary (note that we pass the machine name of the vocabulary).
|
||||
$this->destination =
|
||||
new MigrateDestinationTerm('migrate_example_beer_styles');
|
||||
|
||||
// Create a map object for tracking the relationships between source rows
|
||||
// and their resulting Drupal objects. We will use the MigrateSQLMap class,
|
||||
// which uses database tables for tracking. Pass the machine name (BeerTerm)
|
||||
// of this migration to use in generating map and message table names.
|
||||
// And, pass schema definitions for the primary keys of the source and
|
||||
// destination - we need to be explicit for our source, but the destination
|
||||
// class knows its schema already.
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array(
|
||||
'style' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Topic ID',
|
||||
),
|
||||
),
|
||||
MigrateDestinationTerm::getKeySchema()
|
||||
);
|
||||
|
||||
// Assign mappings TO destination fields FROM source fields. To discover
|
||||
// the names used in these calls, use the drush commands
|
||||
// drush migrate-fields-destination BeerTerm
|
||||
// drush migrate-fields-source BeerTerm
|
||||
// drush migrate-fields-destination BeerTerm
|
||||
// drush migrate-fields-source BeerTerm
|
||||
// or review the detail pages in the UI.
|
||||
$this->addFieldMapping('name', 'style');
|
||||
$this->addFieldMapping('description', 'details');
|
||||
|
||||
// Documenting your mappings makes it easier for the whole team to see
|
||||
// exactly what the status is when developing a migration process.
|
||||
$this->addFieldMapping('parent_name', 'style_parent')
|
||||
->description(t('The incoming style_parent field is the name of the term parent'));
|
||||
->description(t('The incoming style_parent field is the name of the term parent'));
|
||||
|
||||
// Mappings are assigned issue groups, by which they are grouped on the
|
||||
// migration info page when the migrate_ui module is enabled. The default
|
||||
// is 'Done', indicating active mappings which need no attention. A
|
||||
// suggested practice is to use groups of:
|
||||
// Do Not Migrate (or DNM) to indicate source fields which are not being used,
|
||||
// or destination fields not to be populated by migration.
|
||||
// Do Not Migrate (or DNM) to indicate source fields which are not being
|
||||
// used, or destination fields not to be populated by migration.
|
||||
// Client Issues to indicate input from the client is needed to determine
|
||||
// how a given field is to be migrated.
|
||||
// Implementor Issues to indicate that the client has provided all the
|
||||
// necessary information, and now the implementor needs to complete the work.
|
||||
// necessary information, and now the implementor needs to complete the
|
||||
// work.
|
||||
$this->addFieldMapping(NULL, 'hoppiness')
|
||||
->description(t('This info will not be maintained in Drupal'))
|
||||
->issueGroup(t('DNM'));
|
||||
->description(t('This info will not be maintained in Drupal'))
|
||||
->issueGroup(t('DNM'));
|
||||
|
||||
// Open mapping issues can be assigned priorities (the default is
|
||||
// MigrateFieldMapping::ISSUE_PRIORITY_OK). If you're using an issue
|
||||
// tracking system, and have defined issuePattern (see BasicExampleMigration
|
||||
// above), you can specify a ticket/issue number in the system on the
|
||||
// mapping and migrate_ui will link directory to it.
|
||||
// mapping and migrate_ui will link directly to it.
|
||||
$this->addFieldMapping(NULL, 'region')
|
||||
->description('Will a field be added to the vocabulary for this?')
|
||||
->issueGroup(t('Client Issues'))
|
||||
->issuePriority(MigrateFieldMapping::ISSUE_PRIORITY_MEDIUM)
|
||||
->issueNumber(770064);
|
||||
->description('Will a field be added to the vocabulary for this?')
|
||||
->issueGroup(t('Client Issues'))
|
||||
// This priority wil cause the mapping to be highlighted in the UI.
|
||||
->issuePriority(MigrateFieldMapping::ISSUE_PRIORITY_MEDIUM)
|
||||
->issueNumber(770064);
|
||||
|
||||
// It is good practice to account for all source and destination fields
|
||||
// explicitly - this makes sure that everyone understands exactly what is
|
||||
// being migrated and what is not. Also, migrate_ui highlights unmapped
|
||||
// fields, or mappings involving fields not in the source and destination,
|
||||
// so if (for example) a new field is added to the destination field it's
|
||||
// immediately visible, and you can find out if anything needs to be
|
||||
// so if (for example) a new field is added to the destination typ it's
|
||||
// immediately visible, and you can decide if anything needs to be
|
||||
// migrated into it.
|
||||
$this->addFieldMapping('format')
|
||||
->issueGroup(t('DNM'));
|
||||
->issueGroup(t('DNM'));
|
||||
$this->addFieldMapping('weight')
|
||||
->issueGroup(t('DNM'));
|
||||
->issueGroup(t('DNM'));
|
||||
$this->addFieldMapping('parent')
|
||||
->issueGroup(t('DNM'));
|
||||
->issueGroup(t('DNM'));
|
||||
|
||||
// We conditionally DNM these fields, so your field mappings will be clean
|
||||
// whether or not you have path and or pathauto enabled
|
||||
if (module_exists('path')) {
|
||||
// whether or not you have path and/or pathauto enabled.
|
||||
$destination_fields = $this->destination->fields();
|
||||
if (isset($destination_fields['path'])) {
|
||||
$this->addFieldMapping('path')
|
||||
->issueGroup(t('DNM'));
|
||||
if (module_exists('pathauto')) {
|
||||
->issueGroup(t('DNM'));
|
||||
if (isset($destination_fields['pathauto'])) {
|
||||
$this->addFieldMapping('pathauto')
|
||||
->issueGroup(t('DNM'));
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,31 +210,44 @@ class BeerTermMigration extends BasicExampleMigration {
|
||||
* transformations of the data.
|
||||
*/
|
||||
class BeerUserMigration extends BasicExampleMigration {
|
||||
public function __construct() {
|
||||
|
||||
public function __construct($arguments) {
|
||||
// The basic setup is similar to BeerTermMigraiton
|
||||
parent::__construct();
|
||||
parent::__construct($arguments);
|
||||
$this->description = t('Beer Drinkers of the world');
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array('aid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Account ID.'
|
||||
)
|
||||
),
|
||||
MigrateDestinationUser::getKeySchema()
|
||||
);
|
||||
$query = db_select('migrate_example_beer_account', 'mea')
|
||||
->fields('mea', array('aid', 'status', 'posted', 'name', 'nickname', 'password', 'mail', 'sex', 'beers'));
|
||||
->fields('mea', array(
|
||||
'aid',
|
||||
'status',
|
||||
'posted',
|
||||
'name',
|
||||
'nickname',
|
||||
'password',
|
||||
'mail',
|
||||
'sex',
|
||||
'beers',
|
||||
));
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
$this->destination = new MigrateDestinationUser();
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array(
|
||||
'aid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Account ID.',
|
||||
),
|
||||
),
|
||||
MigrateDestinationUser::getKeySchema()
|
||||
);
|
||||
|
||||
// One good way to organize your mappings is in three groups - mapped fields,
|
||||
// unmapped source fields, and unmapped destination fields
|
||||
// One good way to organize your mappings in the constructor is in three
|
||||
// groups - mapped fields, unmapped source fields, and unmapped destination
|
||||
// fields.
|
||||
|
||||
// Mapped fields
|
||||
|
||||
// This is a shortcut you can use when the source and destination field
|
||||
// names are identical (i.e., the email address field is named 'mail' in
|
||||
// names are identical (e.g., the email address field is named 'mail' in
|
||||
// both the source table and in Drupal).
|
||||
$this->addSimpleMappings(array('status', 'mail'));
|
||||
|
||||
@@ -222,9 +259,10 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
// 'test_1'.
|
||||
// dedupe() takes the Drupal table and column for determining uniqueness.
|
||||
$this->addFieldMapping('name', 'name')
|
||||
->dedupe('users', 'name');
|
||||
->dedupe('users', 'name');
|
||||
|
||||
// The migrate module automatically converts date/time strings to UNIX timestamps.
|
||||
// The migrate module automatically converts date/time strings to UNIX
|
||||
// timestamps.
|
||||
$this->addFieldMapping('created', 'posted');
|
||||
|
||||
$this->addFieldMapping('pass', 'password');
|
||||
@@ -234,7 +272,7 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
// value is provided in addition to a source field, the default value will
|
||||
// be applied to any rows where the source field is empty or NULL.
|
||||
$this->addFieldMapping('roles')
|
||||
->defaultValue(DRUPAL_AUTHENTICATED_RID);
|
||||
->defaultValue(DRUPAL_AUTHENTICATED_RID);
|
||||
|
||||
$this->addFieldMapping('field_migrate_example_gender', 'sex');
|
||||
|
||||
@@ -244,20 +282,34 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
// user.
|
||||
if (module_exists('node_reference')) {
|
||||
$this->addFieldMapping('field_migrate_example_favbeers', 'beers')
|
||||
->separator('|');
|
||||
->separator('|');
|
||||
}
|
||||
else {
|
||||
$this->addFieldMapping(NULL, 'beers')
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
|
||||
// Unmapped source fields
|
||||
$this->addFieldMapping(NULL, 'nickname')
|
||||
->issueGroup(t('DNM'));
|
||||
->issueGroup(t('DNM'));
|
||||
|
||||
// Unmapped destination fields
|
||||
|
||||
// This is a shortcut you can use to mark several destination fields as DNM
|
||||
// at once
|
||||
$this->addUnmigratedDestinations(array('theme', 'signature', 'access', 'login',
|
||||
'timezone', 'language', 'picture', 'is_new', 'signature_format', 'role_names',
|
||||
'init', 'data'));
|
||||
// at once.
|
||||
$this->addUnmigratedDestinations(array(
|
||||
'access',
|
||||
'data',
|
||||
'is_new',
|
||||
'language',
|
||||
'login',
|
||||
'picture',
|
||||
'role_names',
|
||||
'signature',
|
||||
'signature_format',
|
||||
'theme',
|
||||
'timezone',
|
||||
));
|
||||
|
||||
// Oops, we made a typo - this should have been 'init'! If you have
|
||||
// migrate_ui enabled, look at the BeerUser info page - you'll see that it
|
||||
@@ -265,14 +317,15 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
// list of destination fields", and also lists "1 unmapped" under Destination,
|
||||
// where it highlights "init" as unmapped.
|
||||
$this->addFieldMapping('int')
|
||||
->issueGroup(t('DNM'));
|
||||
->issueGroup(t('DNM'));
|
||||
|
||||
if (module_exists('path')) {
|
||||
$destination_fields = $this->destination->fields();
|
||||
if (isset($destination_fields['path'])) {
|
||||
$this->addFieldMapping('path')
|
||||
->issueGroup(t('DNM'));
|
||||
if (module_exists('pathauto')) {
|
||||
->issueGroup(t('DNM'));
|
||||
if (isset($destination_fields['pathauto'])) {
|
||||
$this->addFieldMapping('pathauto')
|
||||
->issueGroup(t('DNM'));
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,35 +336,29 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
* and creates Drupal nodes of type 'Beer' as destination.
|
||||
*/
|
||||
class BeerNodeMigration extends BasicExampleMigration {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
$this->description = t('Beers of the world');
|
||||
|
||||
// You may optionally declare dependencies for your migration - other migrations
|
||||
// which should run first. In this case, terms assigned to our nodes and
|
||||
// the authors of the nodes should be migrated before the nodes themselves.
|
||||
$this->dependencies = array('BeerTerm', 'BeerUser');
|
||||
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array(
|
||||
'bid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Beer ID.',
|
||||
'alias' => 'b',
|
||||
)
|
||||
),
|
||||
MigrateDestinationNode::getKeySchema()
|
||||
);
|
||||
|
||||
// We have a more complicated query. The Migration class fundamentally
|
||||
// depends on taking a single source row and turning it into a single
|
||||
// Drupal object, so how do we deal with zero or more terms attached to
|
||||
// each node? One way (demonstrated for MySQL) is to pull them into a single
|
||||
// each node? One way (valid for MySQL only) is to pull them into a single
|
||||
// comma-separated list.
|
||||
$query = db_select('migrate_example_beer_node', 'b')
|
||||
->fields('b', array('bid', 'name', 'body', 'excerpt', 'aid', 'countries',
|
||||
'image', 'image_alt', 'image_title', 'image_description'));
|
||||
->fields('b', array(
|
||||
'bid',
|
||||
'name',
|
||||
'body',
|
||||
'excerpt',
|
||||
'aid',
|
||||
'countries',
|
||||
'image',
|
||||
'image_alt',
|
||||
'image_title',
|
||||
'image_description',
|
||||
));
|
||||
$query->leftJoin('migrate_example_beer_topic_node', 'tb', 'b.bid = tb.bid');
|
||||
// Gives a single comma-separated list of related terms
|
||||
$query->groupBy('tb.bid');
|
||||
@@ -331,23 +378,26 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
// Set up our destination - nodes of type migrate_example_beer
|
||||
$this->destination = new MigrateDestinationNode('migrate_example_beer');
|
||||
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array(
|
||||
'bid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Beer ID.',
|
||||
'alias' => 'b',
|
||||
),
|
||||
),
|
||||
MigrateDestinationNode::getKeySchema()
|
||||
);
|
||||
|
||||
// Mapped fields
|
||||
$this->addFieldMapping('title', 'name')
|
||||
->description(t('Mapping beer name in source to node title'));
|
||||
->description(t('Mapping beer name in source to node title'));
|
||||
$this->addFieldMapping('sticky')
|
||||
->description(t('Should we default this to 0 or 1?'))
|
||||
->issueGroup(t('Client questions'))
|
||||
->issueNumber(765736)
|
||||
->issuePriority(MigrateFieldMapping::ISSUE_PRIORITY_LOW);
|
||||
|
||||
// To maintain node identities between the old and new systems (i.e., have
|
||||
// the same unique IDs), map the ID column from the old system to nid and
|
||||
// set is_new to TRUE. This works only if we're importing into a system that
|
||||
// has no existing nodes with the nids being imported.
|
||||
$this->addFieldMapping('nid', 'bid')
|
||||
->description(t('Preserve old beer ID as nid in Drupal'));
|
||||
$this->addFieldMapping('is_new')
|
||||
->defaultValue(TRUE);
|
||||
->description(t('Should we default this to 0 or 1?'))
|
||||
->issueGroup(t('Client questions'))
|
||||
->issueNumber(765736)
|
||||
->issuePriority(MigrateFieldMapping::ISSUE_PRIORITY_LOW);
|
||||
|
||||
// References to related objects (such as the author of the content) are
|
||||
// most likely going to be identifiers from the source data, not Drupal
|
||||
@@ -357,28 +407,40 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
// find a corresponding uid for the aid, the owner will be the administrative
|
||||
// account.
|
||||
$this->addFieldMapping('uid', 'aid')
|
||||
->sourceMigration('BeerUser')
|
||||
->defaultValue(1);
|
||||
// Note this is the machine name of the user migration.
|
||||
->sourceMigration('BeerUser')
|
||||
->defaultValue(1);
|
||||
|
||||
// This is a multi-value text field
|
||||
// This is a multi-value text field - in the source data the values are
|
||||
// separated by |, so we tell migrate to split it by that character.
|
||||
$this->addFieldMapping('field_migrate_example_country', 'countries')
|
||||
->separator('|');
|
||||
// These are related terms, which by default will be looked up by name
|
||||
->separator('|');
|
||||
// These are related terms, which by default will be looked up by name.
|
||||
$this->addFieldMapping('migrate_example_beer_styles', 'terms')
|
||||
->separator(',');
|
||||
->separator(',');
|
||||
|
||||
// Some fields may have subfields such as text formats or summaries
|
||||
// (equivalent to teasers in previous Drupal versions).
|
||||
// These can be individually mapped as we see here.
|
||||
// Some fields may have subfields such as text formats or summaries. These
|
||||
// can be individually mapped as we see here.
|
||||
$this->addFieldMapping('body', 'body');
|
||||
$this->addFieldMapping('body:summary', 'excerpt');
|
||||
|
||||
// Copy an image file, write DB record to files table, and save in Field storage.
|
||||
// We map the filename (relative to the source_dir below) to the field itself.
|
||||
// File fields are more complex - the file needs to be copied, a Drupal
|
||||
// file entity (file_managed table row) created, and the field populated.
|
||||
// There are several different options involved. It's usually best to do
|
||||
// migrate the files themselves in their own migration (see wine.inc for an
|
||||
// example), but they can also be brought over through the field mapping.
|
||||
|
||||
// We map the filename (relative to the source_dir below) to the field
|
||||
// itself.
|
||||
$this->addFieldMapping('field_migrate_example_image', 'image');
|
||||
// The file_class determines how the 'image' value is interpreted, and what
|
||||
// other options are available. In this case, MigrateFileUri indicates that
|
||||
// the 'image' value is a URI.
|
||||
$this->addFieldMapping('field_migrate_example_image:file_class')
|
||||
->defaultValue('MigrateFileUri');
|
||||
// Here we specify the directory containing the source files.
|
||||
$this->addFieldMapping('field_migrate_example_image:source_dir')
|
||||
->defaultValue(drupal_get_path('module', 'migrate_example'));
|
||||
->defaultValue(drupal_get_path('module', 'migrate_example'));
|
||||
// And we map the alt and title values in the database to those on the image.
|
||||
$this->addFieldMapping('field_migrate_example_image:alt', 'image_alt');
|
||||
$this->addFieldMapping('field_migrate_example_image:title', 'image_title');
|
||||
@@ -387,24 +449,44 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
$this->addUnmigratedSources(array('image_description'));
|
||||
|
||||
// Unmapped destination fields
|
||||
$this->addUnmigratedDestinations(array('created', 'changed', 'status',
|
||||
'promote', 'revision', 'language', 'revision_uid', 'log', 'tnid',
|
||||
'body:format', 'body:language', 'migrate_example_beer_styles:source_type',
|
||||
'migrate_example_beer_styles:create_term', 'field_migrate_example_image:destination_dir',
|
||||
'field_migrate_example_image:language', 'field_migrate_example_image:file_replace',
|
||||
'field_migrate_example_image:preserve_files', 'field_migrate_example_country:language', 'comment',
|
||||
'field_migrate_example_image:file_class', 'field_migrate_example_image:destination_file'));
|
||||
// Some conventions we use here: with a long list of fields to ignore, we
|
||||
// arrange them alphabetically, one distinct field per line (although
|
||||
// subfields of the same field may be grouped on the same line), and indent
|
||||
// subfields to distinguish them from top-level fields.
|
||||
$this->addUnmigratedDestinations(array(
|
||||
'body:format',
|
||||
'changed',
|
||||
'comment',
|
||||
'created',
|
||||
'field_migrate_example_image:destination_dir',
|
||||
'field_migrate_example_image:destination_file',
|
||||
'field_migrate_example_image:file_replace',
|
||||
'field_migrate_example_image:preserve_files',
|
||||
'field_migrate_example_image:urlencode',
|
||||
'is_new',
|
||||
'language',
|
||||
'log',
|
||||
'migrate_example_beer_styles:source_type',
|
||||
'migrate_example_beer_styles:create_term',
|
||||
'promote',
|
||||
'revision',
|
||||
'revision_uid',
|
||||
'status',
|
||||
'tnid',
|
||||
));
|
||||
|
||||
if (module_exists('path')) {
|
||||
$destination_fields = $this->destination->fields();
|
||||
if (isset($destination_fields['path'])) {
|
||||
$this->addFieldMapping('path')
|
||||
->issueGroup(t('DNM'));
|
||||
if (module_exists('pathauto')) {
|
||||
->issueGroup(t('DNM'));
|
||||
if (isset($destination_fields['pathauto'])) {
|
||||
$this->addFieldMapping('pathauto')
|
||||
->issueGroup(t('DNM'));
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
}
|
||||
if (module_exists('statistics')) {
|
||||
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
|
||||
$this->addUnmigratedDestinations(
|
||||
array('totalcount', 'daycount', 'timestamp'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -414,46 +496,79 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
* Drupal comment objects.
|
||||
*/
|
||||
class BeerCommentMigration extends BasicExampleMigration {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
$this->description = 'Comments about beers';
|
||||
$this->dependencies = array('BeerUser', 'BeerNode');
|
||||
|
||||
$query = db_select('migrate_example_beer_comment', 'mec')
|
||||
->fields('mec', array(
|
||||
'cid',
|
||||
'cid_parent',
|
||||
'name',
|
||||
'mail',
|
||||
'aid',
|
||||
'body',
|
||||
'bid',
|
||||
'subject',
|
||||
))
|
||||
->orderBy('cid_parent', 'ASC');
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
// Note that the machine name passed for comment migrations is
|
||||
// 'comment_node_' followed by the machine name of the node type these
|
||||
// comments are attached to.
|
||||
$this->destination =
|
||||
new MigrateDestinationComment('comment_node_migrate_example_beer');
|
||||
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array('cid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
)
|
||||
),
|
||||
array(
|
||||
'cid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
MigrateDestinationComment::getKeySchema()
|
||||
);
|
||||
$query = db_select('migrate_example_beer_comment', 'mec')
|
||||
->fields('mec', array('cid', 'cid_parent', 'name', 'mail', 'aid', 'body', 'bid', 'subject'))
|
||||
->orderBy('cid_parent', 'ASC');
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
$this->destination = new MigrateDestinationComment('comment_node_migrate_example_beer');
|
||||
|
||||
// Mapped fields
|
||||
$this->addSimpleMappings(array('name', 'subject', 'mail'));
|
||||
$this->addFieldMapping('status')
|
||||
->defaultValue(COMMENT_PUBLISHED);
|
||||
->defaultValue(COMMENT_PUBLISHED);
|
||||
|
||||
// We preserved bid => nid ids during BeerNode import so simple mapping suffices.
|
||||
$this->addFieldMapping('nid', 'bid');
|
||||
$this->addFieldMapping('nid', 'bid')
|
||||
->sourceMigration('BeerNode');
|
||||
|
||||
$this->addFieldMapping('uid', 'aid')
|
||||
->sourceMigration('BeerUser')
|
||||
->defaultValue(0);
|
||||
->sourceMigration('BeerUser')
|
||||
->defaultValue(0);
|
||||
|
||||
$this->addFieldMapping('pid', 'cid_parent')
|
||||
->sourceMigration('BeerComment')
|
||||
->description('Parent comment.');
|
||||
->sourceMigration('BeerComment')
|
||||
->description('Parent comment.');
|
||||
|
||||
$this->addFieldMapping('comment_body', 'body');
|
||||
|
||||
// No unmapped source fields
|
||||
|
||||
// Unmapped destination fields
|
||||
$this->addUnmigratedDestinations(array('hostname', 'created', 'changed',
|
||||
'thread', 'homepage', 'language', 'comment_body:format', 'comment_body:language'));
|
||||
$this->addUnmigratedDestinations(array(
|
||||
'changed',
|
||||
'comment_body:format',
|
||||
'created',
|
||||
'homepage',
|
||||
'hostname',
|
||||
'language',
|
||||
'thread',
|
||||
));
|
||||
|
||||
$destination_fields = $this->destination->fields();
|
||||
if (isset($destination_fields['path'])) {
|
||||
$this->addFieldMapping('path')
|
||||
->issueGroup(t('DNM'));
|
||||
if (isset($destination_fields['pathauto'])) {
|
||||
$this->addFieldMapping('pathauto')
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,22 +47,19 @@ function migrate_example_beer_uninstall() {
|
||||
}
|
||||
|
||||
function migrate_example_beer_disable() {
|
||||
Migration::deregisterMigration('BeerTerm');
|
||||
Migration::deregisterMigration('BeerUser');
|
||||
Migration::deregisterMigration('BeerNode');
|
||||
Migration::deregisterMigration('BeerComment');
|
||||
MigrateGroup::deregister('beer');
|
||||
}
|
||||
|
||||
function migrate_example_beer_schema_node() {
|
||||
return array(
|
||||
'description' => 'Beers of the world.',
|
||||
'fields' => array(
|
||||
'bid' => array(
|
||||
'bid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Beer ID.',
|
||||
),
|
||||
'name' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
@@ -123,7 +120,7 @@ function migrate_example_beer_schema_topic() {
|
||||
return array(
|
||||
'description' => 'Categories',
|
||||
'fields' => array(
|
||||
'style' => array(
|
||||
'style' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
@@ -160,12 +157,12 @@ function migrate_example_beer_schema_topic_node() {
|
||||
return array(
|
||||
'description' => 'Beers topic pairs.',
|
||||
'fields' => array(
|
||||
'bid' => array(
|
||||
'bid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Beer ID.',
|
||||
),
|
||||
'style' => array(
|
||||
'style' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
@@ -180,12 +177,12 @@ function migrate_example_beer_schema_comment() {
|
||||
return array(
|
||||
'description' => 'Beers comments.',
|
||||
'fields' => array(
|
||||
'cid' => array(
|
||||
'cid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Comment ID.',
|
||||
),
|
||||
'bid' => array(
|
||||
'bid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Beer ID that is being commented upon',
|
||||
@@ -233,12 +230,12 @@ function migrate_example_beer_schema_account() {
|
||||
return array(
|
||||
'description' => 'Beers accounts.',
|
||||
'fields' => array(
|
||||
'aid' => array(
|
||||
'aid' => array(
|
||||
'type' => 'serial',
|
||||
//'not null' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Account ID',
|
||||
),
|
||||
'status' => array(
|
||||
'status' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Blocked_Allowed',
|
||||
@@ -293,7 +290,7 @@ function migrate_example_beer_schema_legacy_urls() {
|
||||
return array(
|
||||
'description' => 'Stores legacy paths and destination ids for redirection.',
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'id' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Primary Key: ID.',
|
||||
@@ -478,8 +475,8 @@ function migrate_example_beer_image() {
|
||||
function migrate_example_beer_favs() {
|
||||
if (!field_info_field('field_migrate_example_favbeers')) {
|
||||
$field = array(
|
||||
'field_name' => 'field_migrate_example_favbeers',
|
||||
'type' => 'node_reference',
|
||||
'field_name' => 'field_migrate_example_favbeers',
|
||||
'type' => 'node_reference',
|
||||
'cardinality' => -1,
|
||||
'settings' => array(
|
||||
'referenceable_types' => array('migrate_example_beer'),
|
||||
@@ -509,9 +506,10 @@ function migrate_example_beer_gender() {
|
||||
'field_name' => 'field_migrate_example_gender',
|
||||
'type' => 'list_integer',
|
||||
'settings' => array(
|
||||
'allowed_values' =>
|
||||
"0|Male\n" .
|
||||
"1|Female\n",
|
||||
'allowed_values' => array(
|
||||
0 => 'Male',
|
||||
1 => 'Female'
|
||||
),
|
||||
),
|
||||
);
|
||||
field_create_field($field);
|
||||
@@ -560,7 +558,11 @@ function migrate_example_beer_country() {
|
||||
|
||||
function migrate_example_beer_content_type_delete() {
|
||||
$bundle = 'migrate_example_beer';
|
||||
$field_names = array('migrate_example_beer_styles', 'field_migrate_example_image', 'field_migrate_example_country');
|
||||
$field_names = array(
|
||||
'migrate_example_beer_styles',
|
||||
'field_migrate_example_image',
|
||||
'field_migrate_example_country',
|
||||
);
|
||||
foreach ($field_names as $field_name) {
|
||||
$instance = field_info_instance('node', $field_name, $bundle);
|
||||
field_delete_instance($instance);
|
||||
@@ -581,15 +583,58 @@ function migrate_example_beer_content_type_delete() {
|
||||
}
|
||||
|
||||
function migrate_example_beer_data_node() {
|
||||
$fields = array('bid', 'name', 'body', 'excerpt', 'countries', 'aid', 'image',
|
||||
'image_alt', 'image_title', 'image_description');
|
||||
$fields = array(
|
||||
'bid',
|
||||
'name',
|
||||
'body',
|
||||
'excerpt',
|
||||
'countries',
|
||||
'aid',
|
||||
'image',
|
||||
'image_alt',
|
||||
'image_title',
|
||||
'image_description',
|
||||
);
|
||||
$query = db_insert('migrate_example_beer_node')
|
||||
->fields($fields);
|
||||
->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 occassionally get something right', 'A treat', 'United Kingdom', 1, NULL, NULL, NULL, NULL),
|
||||
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 occassionally get something right',
|
||||
'A treat',
|
||||
'United Kingdom',
|
||||
1,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
),
|
||||
);
|
||||
foreach ($data as $row) {
|
||||
$query->values(array_combine($fields, $row));
|
||||
@@ -600,14 +645,59 @@ function migrate_example_beer_data_node() {
|
||||
// Note that alice has duplicate username. Exercies dedupe() method.
|
||||
// @TODO duplicate email also.
|
||||
function migrate_example_beer_data_account() {
|
||||
$fields = array('status', 'posted', 'name', 'nickname', 'password', 'mail', 'sex', 'beers');
|
||||
$fields = array(
|
||||
'status',
|
||||
'posted',
|
||||
'name',
|
||||
'nickname',
|
||||
'password',
|
||||
'mail',
|
||||
'sex',
|
||||
'beers',
|
||||
);
|
||||
$query = db_insert('migrate_example_beer_account')
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, '2010-03-30 10:31:05', 'alice', 'alice hot pants', 'alicepass', 'alice@example.com', '1', '99999999|99999998|99999997'),
|
||||
array(1, '2010-04-04 10:31:05', 'alice', 'alice dupe pants', 'alicepass', 'alice2@example.com', '1', '99999999|99999998|99999997'),
|
||||
array(0, '2007-03-15 10:31:05', 'bob', 'rebob', 'bobpass', 'bob@example.com', '1', '99999999|99999997'),
|
||||
array(1, '2004-02-29 10:31:05', 'charlie', 'charlie chocolate', 'mykids', 'charlie@example.com', '0', '99999999|99999998'),
|
||||
array(
|
||||
1,
|
||||
'2010-03-30 10:31:05',
|
||||
'alice',
|
||||
'alice hot pants',
|
||||
'alicepass',
|
||||
'alice@example.com',
|
||||
'1',
|
||||
'99999999|99999998|99999997',
|
||||
),
|
||||
array(
|
||||
1,
|
||||
'2010-04-04 10:31:05',
|
||||
'alice',
|
||||
'alice dupe pants',
|
||||
'alicepass',
|
||||
'alice2@example.com',
|
||||
'1',
|
||||
'99999999|99999998|99999997',
|
||||
),
|
||||
array(
|
||||
0,
|
||||
'2007-03-15 10:31:05',
|
||||
'bob',
|
||||
'rebob',
|
||||
'bobpass',
|
||||
'bob@example.com',
|
||||
'1',
|
||||
'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));
|
||||
@@ -616,15 +706,55 @@ function migrate_example_beer_data_account() {
|
||||
}
|
||||
|
||||
function migrate_example_beer_data_comment() {
|
||||
$fields = array('bid', 'cid_parent', 'subject', 'body', 'name', 'mail', 'aid');
|
||||
$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', 'hot body', 'alice', 'alice@example.com', 0),
|
||||
array(99999998, NULL, 'im second', 'hot body', 'alice', 'alice@example.com', 0),
|
||||
array(99999999, NULL, 'im parent', 'hot body', 'alice', 'alice@example.com', 0),
|
||||
array(
|
||||
99999998,
|
||||
NULL,
|
||||
'im first',
|
||||
'hot body',
|
||||
'alice',
|
||||
'alice@example.com',
|
||||
0,
|
||||
),
|
||||
array(
|
||||
99999998,
|
||||
NULL,
|
||||
'im second',
|
||||
'hot body',
|
||||
'alice',
|
||||
'alice@example.com',
|
||||
0,
|
||||
),
|
||||
array(
|
||||
99999999,
|
||||
NULL,
|
||||
'im parent',
|
||||
'hot body',
|
||||
'alice',
|
||||
'alice@example.com',
|
||||
0,
|
||||
),
|
||||
array(99999999, 1, 'im child', 'cold body', 'bob', NULL, 1),
|
||||
array(99999999, 2, 'im grandchild', 'bitter body', 'charlie@example.com', NULL, 1),
|
||||
array(
|
||||
99999999,
|
||||
2,
|
||||
'im grandchild',
|
||||
'bitter body',
|
||||
'charlie@example.com',
|
||||
NULL,
|
||||
1,
|
||||
),
|
||||
);
|
||||
foreach ($data as $row) {
|
||||
$query->values(array_combine($fields, $row));
|
||||
@@ -639,7 +769,13 @@ function migrate_example_beer_data_topic() {
|
||||
$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'),
|
||||
array(
|
||||
'pilsner',
|
||||
'refreshing',
|
||||
NULL,
|
||||
'Pilsen, Bohemia (now Czech Republic)',
|
||||
'Low',
|
||||
),
|
||||
);
|
||||
foreach ($data as $row) {
|
||||
$query->values(array_combine($fields, $row));
|
||||
@@ -663,13 +799,37 @@ function migrate_example_beer_data_topic_node() {
|
||||
}
|
||||
|
||||
function migrate_example_beer_data_urls() {
|
||||
$fields = array('id', 'migration_name', 'source_id', 'source_uri', 'modificationdatetime');
|
||||
$fields = array(
|
||||
'id',
|
||||
'migration_name',
|
||||
'source_id',
|
||||
'source_uri',
|
||||
'modificationdatetime',
|
||||
);
|
||||
$query = db_insert('migrate_example_beer_legacy_urls')
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, 'BeerNode', 99999997, 'the_boddington/main', strtotime('2010-04-12 08:32:06')),
|
||||
array(2, 'BeerNode', 99999998, 'Miller Lite taste', strtotime('2010-04-12 08:32:05')),
|
||||
array(3, 'BeerNode', 99999999, 'green wonder', strtotime('2010-04-12 08:32:03')),
|
||||
array(
|
||||
1,
|
||||
'BeerNode',
|
||||
99999997,
|
||||
'the_boddington/main',
|
||||
strtotime('2010-04-12 08:32:06'),
|
||||
),
|
||||
array(
|
||||
2,
|
||||
'BeerNode',
|
||||
99999998,
|
||||
'Miller Lite taste',
|
||||
strtotime('2010-04-12 08:32:05'),
|
||||
),
|
||||
array(
|
||||
3,
|
||||
'BeerNode',
|
||||
99999999,
|
||||
'green wonder',
|
||||
strtotime('2010-04-12 08:32:03'),
|
||||
),
|
||||
);
|
||||
foreach ($data as $row) {
|
||||
$query->values(array_combine($fields, $row));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
class RoleTableMigration extends Migration {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->dependencies = array();
|
||||
@@ -23,14 +24,15 @@ class RoleTableMigration extends Migration {
|
||||
$this->destination = new MigrateDestinationTableCopy('role_copy', $destination_key);
|
||||
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array('rid' => array(
|
||||
array(
|
||||
'rid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'alias' => 'r',
|
||||
)
|
||||
),
|
||||
$destination_key
|
||||
),
|
||||
),
|
||||
$destination_key
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name = "Migrate Example"
|
||||
description = "Example migration data."
|
||||
package = "Development"
|
||||
package = "Migration"
|
||||
core = 7.x
|
||||
dependencies[] = taxonomy
|
||||
dependencies[] = image
|
||||
@@ -12,16 +12,14 @@ dependencies[] = number
|
||||
;node_reference is useful but not required
|
||||
;dependencies[] = node_reference
|
||||
|
||||
files[] = migrate_example.module
|
||||
files[] = beer.inc
|
||||
files[] = wine.inc
|
||||
|
||||
; For testing table_copy plugin. Since is infrequently used, we comment it out.
|
||||
; files[] = example.table_copy.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-07
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2018-06-10
|
||||
version = "7.x-2.11"
|
||||
core = "7.x"
|
||||
project = "migrate"
|
||||
datestamp = "1352299007"
|
||||
|
||||
datestamp = "1528674486"
|
||||
|
||||
@@ -34,6 +34,7 @@ function migrate_example_install() {
|
||||
);
|
||||
$example_format = (object) $example_format;
|
||||
filter_format_save($example_format);
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function migrate_example_uninstall() {
|
||||
@@ -62,8 +63,8 @@ function migrate_example_update_7001() {
|
||||
);
|
||||
|
||||
$result = db_select('migrate_example_beer_legacy_urls', 'ms')
|
||||
->fields('ms', array('machine_name', 'modificationdate'))
|
||||
->execute();
|
||||
->fields('ms', array('machine_name', 'modificationdate'))
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$modificationdatetime = strtotime($row->modificationdate);
|
||||
db_update('migrate_example_beer_legacy_urls')
|
||||
@@ -84,24 +85,24 @@ function migrate_example_update_7001() {
|
||||
function migrate_example_update_7002() {
|
||||
$ret = array();
|
||||
db_add_field('migrate_example_beer_node', 'image_alt', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Image ALT',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Image ALT',
|
||||
)
|
||||
);
|
||||
db_add_field('migrate_example_beer_node', 'image_title', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Image title',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Image title',
|
||||
)
|
||||
);
|
||||
db_add_field('migrate_example_beer_node', 'image_description', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Image description',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Image description',
|
||||
)
|
||||
);
|
||||
db_update('migrate_example_beer_node')
|
||||
@@ -206,10 +207,10 @@ function migrate_example_update_7006() {
|
||||
function migrate_example_update_7007() {
|
||||
$ret = array();
|
||||
db_add_field('migrate_example_wine_account', 'positions', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Positions held',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Positions held',
|
||||
)
|
||||
);
|
||||
$query = db_update('migrate_example_wine_account')
|
||||
|
||||
+227
-28
@@ -9,39 +9,238 @@
|
||||
|
||||
/*
|
||||
* You must implement hook_migrate_api(), setting the API level to 2, if you are
|
||||
* implementing any migration classes. As of Migrate 2.5, you should also
|
||||
* register your migration and handler classes explicitly here - the former
|
||||
* method of letting them get automatically registered on a cache clear will
|
||||
* break in certain environments (see http://drupal.org/node/1778952).
|
||||
* implementing any migration classes. If your migration application is static -
|
||||
* that is, you know at implementation time exactly what migrations must be
|
||||
* instantiated - then you should register your migrations here. If your
|
||||
* application is more dynamic (for example, if selections in the UI determine
|
||||
* exactly what migrations need to be instantiated), then you would register
|
||||
* your migrations using registerMigration() - see migrate_example_baseball for
|
||||
* more information.
|
||||
*/
|
||||
function migrate_example_migrate_api() {
|
||||
// Usually field mappings are established by code in the migration constructor -
|
||||
// a call to addFieldMapping(). They may also be passed as arguments when
|
||||
// registering a migration - in this case, they are stored in the database
|
||||
// and override any mappings for the same field in the code. To do this,
|
||||
// construct the field mapping object and configure it similarly to when
|
||||
// you call addFieldMapping, and pass your mappings as an array below.
|
||||
$translate_mapping = new MigrateFieldMapping('translate', NULL);
|
||||
$translate_mapping->defaultValue(0);
|
||||
$ignore_mapping = new MigrateFieldMapping('migrate_example_beer_styles:ignore_case', NULL);
|
||||
$ignore_mapping->defaultValue(1);
|
||||
|
||||
$api = array(
|
||||
// Required - tells the Migrate module that you are implementing version 2
|
||||
// of the Migrate API.
|
||||
'api' => 2,
|
||||
// Migrations can be organized into groups. The key used here will be the
|
||||
// machine name of the group, which can be used in Drush:
|
||||
// drush migrate-import --group=wine
|
||||
// The title is a required argument which is displayed for the group in the
|
||||
// UI. You may also have additional arguments for any other data which is
|
||||
// common to all migrations in the group.
|
||||
'groups' => array(
|
||||
'beer' => array(
|
||||
'title' => t('Beer Imports'),
|
||||
),
|
||||
'wine' => array(
|
||||
'title' => t('Wine Imports'),
|
||||
),
|
||||
),
|
||||
|
||||
// Here we register the individual migrations. The keys (BeerTerm, BeerUser,
|
||||
// etc.) are the machine names of the migrations, and the class_name
|
||||
// argument is required. The group_name is optional (defaulting to 'default')
|
||||
// but specifying it is a best practice.
|
||||
'migrations' => array(
|
||||
'BeerTerm' => array('class_name' => 'BeerTermMigration'),
|
||||
'BeerUser' => array('class_name' => 'BeerUserMigration'),
|
||||
'BeerNode' => array('class_name' => 'BeerNodeMigration'),
|
||||
'BeerComment' => array('class_name' => 'BeerCommentMigration'),
|
||||
'WinePrep' => array('class_name' => 'WinePrepMigration'),
|
||||
'WineVariety' => array('class_name' => 'WineVarietyMigration'),
|
||||
'WineRegion' => array('class_name' => 'WineRegionMigration'),
|
||||
'WineBestWith' => array('class_name' => 'WineBestWithMigration'),
|
||||
'WineFileCopy' => array('class_name' => 'WineFileCopyMigration'),
|
||||
'WineFileBlob' => array('class_name' => 'WineFileBlobMigration'),
|
||||
'WineRole' => array('class_name' => 'WineRoleMigration'),
|
||||
'WineUser' => array('class_name' => 'WineUserMigration'),
|
||||
'WineProducer' => array('class_name' => 'WineProducerMigration'),
|
||||
'WineProducerXML' => array('class_name' => 'WineProducerXMLMigration'),
|
||||
'WineProducerMultiXML' => array('class_name' => 'WineProducerMultiXMLMigration'),
|
||||
'WineProducerXMLPull' => array('class_name' => 'WineProducerXMLPullMigration'),
|
||||
'WineWine' => array('class_name' => 'WineWineMigration'),
|
||||
'WineComment' => array('class_name' => 'WineCommentMigration'),
|
||||
'WineTable' => array('class_name' => 'WineTableMigration'),
|
||||
'WineFinish' => array('class_name' => 'WineFinishMigration'),
|
||||
'WineUpdates' => array('class_name' => 'WineUpdatesMigration'),
|
||||
'WineCommentUpdates' => array('class_name' => 'WineCommentUpdatesMigration'),
|
||||
'WineVarietyUpdates' => array('class_name' => 'WineVarietyUpdatesMigration'),
|
||||
'WineUserUpdates' => array('class_name' => 'WineUserUpdatesMigration'),
|
||||
'BeerTerm' => array(
|
||||
'class_name' => 'BeerTermMigration',
|
||||
'group_name' => 'beer',
|
||||
),
|
||||
'BeerUser' => array(
|
||||
'class_name' => 'BeerUserMigration',
|
||||
'group_name' => 'beer',
|
||||
),
|
||||
'BeerNode' => array(
|
||||
'class_name' => 'BeerNodeMigration',
|
||||
'group_name' => 'beer',
|
||||
// You may optionally declare dependencies for your migration - other
|
||||
// migrations which should run first. In this case, terms assigned to our
|
||||
// nodes and the authors of the nodes should be migrated before the nodes
|
||||
// themselves.
|
||||
'dependencies' => array(
|
||||
'BeerTerm',
|
||||
'BeerUser',
|
||||
),
|
||||
// Here is where we add field mappings which may override those
|
||||
// specified in the group constructor.
|
||||
'field_mappings' => array(
|
||||
$translate_mapping,
|
||||
$ignore_mapping,
|
||||
),
|
||||
),
|
||||
'BeerComment' => array(
|
||||
'class_name' => 'BeerCommentMigration',
|
||||
'group_name' => 'beer',
|
||||
'dependencies' => array(
|
||||
'BeerUser',
|
||||
'BeerNode',
|
||||
),
|
||||
),
|
||||
'WinePrep' => array(
|
||||
'class_name' => 'WinePrepMigration',
|
||||
'group_name' => 'wine',
|
||||
),
|
||||
'WineVariety' => array(
|
||||
'class_name' => 'WineVarietyMigration',
|
||||
'group_name' => 'wine',
|
||||
),
|
||||
'WineRegion' => array(
|
||||
'class_name' => 'WineRegionMigration',
|
||||
'group_name' => 'wine',
|
||||
),
|
||||
'WineBestWith' => array(
|
||||
'class_name' => 'WineBestWithMigration',
|
||||
'group_name' => 'wine',
|
||||
),
|
||||
'WineFileCopy' => array(
|
||||
'class_name' => 'WineFileCopyMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WinePrep'),
|
||||
),
|
||||
'WineFileBlob' => array(
|
||||
'class_name' => 'WineFileBlobMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WinePrep'),
|
||||
),
|
||||
'WineRole' => array(
|
||||
'class_name' => 'WineRoleMigration',
|
||||
'group_name' => 'wine',
|
||||
// TIP: Regular dependencies, besides enforcing (in the absence of
|
||||
// --force) the run order of migrations, affect the sorting of
|
||||
// migrations on display. You can use soft dependencies to affect just
|
||||
// the display order when the migrations aren't technically required to
|
||||
// run in a certain order. In this case, we want the role migration to
|
||||
// appear after the file migrations.
|
||||
'soft_dependencies' => array('WineFileCopy'),
|
||||
),
|
||||
'WineUser' => array(
|
||||
'class_name' => 'WineUserMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineFileCopy',
|
||||
'WineRole',
|
||||
),
|
||||
),
|
||||
'WineProducer' => array(
|
||||
'class_name' => 'WineProducerMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerXML' => array(
|
||||
'class_name' => 'WineProducerXMLMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerNamespaceXML' => array(
|
||||
'class_name' => 'WineProducerNamespaceXMLMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerMultiXML' => array(
|
||||
'class_name' => 'WineProducerMultiXMLMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerMultiNamespaceXML' => array(
|
||||
'class_name' => 'WineProducerMultiNamespaceXMLMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerXMLPull' => array(
|
||||
'class_name' => 'WineProducerXMLPullMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerNamespaceXMLPull' => array(
|
||||
'class_name' => 'WineProducerNamespaceXMLPullMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineWine' => array(
|
||||
'class_name' => 'WineWineMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineVariety',
|
||||
'WineBestWith',
|
||||
'WineUser',
|
||||
'WineProducer',
|
||||
),
|
||||
),
|
||||
'WineComment' => array(
|
||||
'class_name' => 'WineCommentMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineUser',
|
||||
'WineWine',
|
||||
),
|
||||
),
|
||||
'WineTable' => array(
|
||||
'class_name' => 'WineTableMigration',
|
||||
'group_name' => 'wine',
|
||||
'soft_dependencies' => array('WineComment'),
|
||||
),
|
||||
'WineFinish' => array(
|
||||
'class_name' => 'WineFinishMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineComment'),
|
||||
),
|
||||
'WineUpdates' => array(
|
||||
'class_name' => 'WineUpdatesMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineWine'),
|
||||
'soft_dependencies' => array('WineFinish'),
|
||||
),
|
||||
'WineCommentUpdates' => array(
|
||||
'class_name' => 'WineCommentUpdatesMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineComment'),
|
||||
'soft_dependencies' => array('WineUpdates'),
|
||||
),
|
||||
'WineVarietyUpdates' => array(
|
||||
'class_name' => 'WineVarietyUpdatesMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineVariety'),
|
||||
'soft_dependencies' => array('WineUpdates'),
|
||||
),
|
||||
'WineUserUpdates' => array(
|
||||
'class_name' => 'WineUserUpdatesMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineUser'),
|
||||
'soft_dependencies' => array('WineUpdates'),
|
||||
),
|
||||
),
|
||||
);
|
||||
return $api;
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* THIS SPACE INTENTIONALLY LEFT BLANK.
|
||||
* THIS FILE INTENTIONALLY LEFT BLANK.
|
||||
*
|
||||
* Yes, there is no code in the .module file. Migrate operates almost entirely
|
||||
* through classes, and by adding any files containing class definitions to the
|
||||
* .info file, those files are automatically included only when the classes they
|
||||
* contain are referenced. The one non-class piece you need to implement is
|
||||
* hook_migrate_api(), but because .migrate.inc is registered using hook_hook_info
|
||||
* by defining your implementation of that hook in mymodule.migrate.inc, it is
|
||||
* automatically invoked only when needed.
|
||||
* hook_migrate_api(), but because .migrate.inc is registered using
|
||||
* hook_hook_info, by defining your implementation of that hook in
|
||||
* example.migrate.inc, it is automatically invoked only when needed.
|
||||
*/
|
||||
|
||||
+4
-5
@@ -8,12 +8,11 @@ features[field][] = "node-migrate_example_oracle-field_mainimage"
|
||||
features[node][] = "migrate_example_oracle"
|
||||
files[] = "migrate_example_oracle.migrate.inc"
|
||||
name = "Migrate example - Oracle"
|
||||
package = "Migrate Examples"
|
||||
package = "Migration"
|
||||
project = "migrate_example_oracle"
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-07
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2018-06-10
|
||||
version = "7.x-2.11"
|
||||
core = "7.x"
|
||||
project = "migrate"
|
||||
datestamp = "1352299007"
|
||||
|
||||
datestamp = "1528674486"
|
||||
|
||||
+30
-28
@@ -123,7 +123,7 @@ function migrate_example_oracle_requirements($phase) {
|
||||
}
|
||||
|
||||
$sample_setting =
|
||||
'<pre>
|
||||
'<pre>
|
||||
$conf[\'oracle_db\'] = array(
|
||||
\'username\' => \'Oracle_username\',
|
||||
\'password\' => \'Oracle_password\',
|
||||
@@ -134,7 +134,7 @@ $conf[\'oracle_db\'] = array(
|
||||
$requirements['oracle_db'] = array('title' => $t('Oracle configuration'));
|
||||
global $conf;
|
||||
if (empty($conf['oracle_db']) || empty($conf['oracle_db']['username']) ||
|
||||
empty($conf['oracle_db']['password']) || empty($conf['oracle_db']['connection_string'])) {
|
||||
empty($conf['oracle_db']['password']) || empty($conf['oracle_db']['connection_string'])) {
|
||||
$requirements['oracle_db']['description'] = $t('You must define $conf[\'oracle_db\']
|
||||
(in your site\'s settings.php file) to point to the Oracle database where
|
||||
you want test data to be stored: ' . $sample_setting);
|
||||
@@ -150,8 +150,10 @@ $conf[\'oracle_db\'] = array(
|
||||
$e = oci_error();
|
||||
$requirements['oracle_connect']['description'] = $t('Could not connect to configured
|
||||
Oracle database at !conn_string. Oracle error message: !message',
|
||||
array('!conn_string' => $conf['oracle_db']['connection_string'],
|
||||
'!message' => $e['message']));
|
||||
array(
|
||||
'!conn_string' => $conf['oracle_db']['connection_string'],
|
||||
'!message' => $e['message'],
|
||||
));
|
||||
$requirements['oracle_connect']['severity'] = REQUIREMENT_ERROR;
|
||||
break;
|
||||
}
|
||||
@@ -159,35 +161,35 @@ $conf[\'oracle_db\'] = array(
|
||||
// Check for necessary privileges
|
||||
$requirements['oracle_privs'] = array('title' => $t('Necessary Oracle privileges are assigned'));
|
||||
$statement = oci_parse($connection, 'SELECT * FROM SESSION_PRIVS');
|
||||
if (!$statement) {
|
||||
$e = oci_error($connection);
|
||||
$requirements['oracle_connect']['description'] = $e['message'];
|
||||
$requirements['oracle_connect']['severity'] = REQUIREMENT_ERROR;
|
||||
if (!$statement) {
|
||||
$e = oci_error($connection);
|
||||
$requirements['oracle_connect']['description'] = $e['message'];
|
||||
$requirements['oracle_connect']['severity'] = REQUIREMENT_ERROR;
|
||||
break;
|
||||
}
|
||||
$result = oci_execute($statement);
|
||||
if (!$result) {
|
||||
$e = oci_error($statement);
|
||||
$requirements['oracle_connect']['description'] = $e['message'];
|
||||
$requirements['oracle_connect']['severity'] = REQUIREMENT_ERROR;
|
||||
break;
|
||||
}
|
||||
$ok = FALSE;
|
||||
while ($row = oci_fetch_object($statement)) {
|
||||
if ($row->PRIVILEGE == 'CREATE TABLE') {
|
||||
$ok = TRUE;
|
||||
break;
|
||||
}
|
||||
$result = oci_execute($statement);
|
||||
if (!$result) {
|
||||
$e = oci_error($statement);
|
||||
$requirements['oracle_connect']['description'] = $e['message'];
|
||||
$requirements['oracle_connect']['severity'] = REQUIREMENT_ERROR;
|
||||
break;
|
||||
}
|
||||
$ok = FALSE;
|
||||
while ($row = oci_fetch_object($statement)) {
|
||||
if ($row->PRIVILEGE == 'CREATE TABLE') {
|
||||
$ok = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$ok) {
|
||||
$requirements['oracle_privs']['description'] = $t('The specified
|
||||
if (!$ok) {
|
||||
$requirements['oracle_privs']['description'] = $t('The specified
|
||||
username !username does not have the CREATE TABLE privilege. This privilege
|
||||
is necessary to create test tables in the Oracle database.',
|
||||
array('!username' => $conf['oracle_db']['username']));
|
||||
$requirements['oracle_privs']['severity'] = REQUIREMENT_ERROR;
|
||||
break;
|
||||
}
|
||||
array('!username' => $conf['oracle_db']['username']));
|
||||
$requirements['oracle_privs']['severity'] = REQUIREMENT_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'update':
|
||||
break;
|
||||
|
||||
+28
-18
@@ -1,9 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Examples and test fodder for migration from Oracle sources. To use this example
|
||||
* (and to run the corresponding tests) you must define a connection to an Oracle database
|
||||
* in your settings.php. E.g.,
|
||||
* Examples and test fodder for migration from Oracle sources. To use this
|
||||
* example
|
||||
* (and to run the corresponding tests) you must define a connection to an
|
||||
* Oracle database in your settings.php. E.g.,
|
||||
*
|
||||
* $conf['oracle_db'] = array(
|
||||
* 'username' => 'DRUPAL',
|
||||
@@ -11,17 +12,18 @@
|
||||
* 'connection_string' => '//oracledb/orcl',
|
||||
* );
|
||||
*
|
||||
* The username must have the CREATE TABLE privilege, so test data can be stored for the
|
||||
* example to import.
|
||||
* The username must have the CREATE TABLE privilege, so test data can be
|
||||
* stored for the example to import.
|
||||
*
|
||||
* See http://us.php.net/manual/en/function.oci-connect.php for more information on
|
||||
* connection_string.
|
||||
* See http://us.php.net/manual/en/function.oci-connect.php for more
|
||||
* information on connection_string.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Migration class to test importing from Oracle into nodes.
|
||||
*/
|
||||
class MigrateExampleOracleNode extends Migration {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->description = t('Example migration from Oracle into nodes.');
|
||||
@@ -35,7 +37,7 @@ class MigrateExampleOracleNode extends Migration {
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Content ID',
|
||||
)
|
||||
),
|
||||
),
|
||||
MigrateDestinationNode::getKeySchema()
|
||||
);
|
||||
@@ -67,23 +69,31 @@ class MigrateExampleOracleNode extends Migration {
|
||||
// Basic fields
|
||||
$this->addFieldMapping('title', 'TITLE');
|
||||
$this->addFieldMapping('uid')
|
||||
->defaultValue(1);
|
||||
->defaultValue(1);
|
||||
$this->addFieldMapping('body', 'BODY');
|
||||
$this->addFieldMapping('field_mainimage', 'MAINIMAGE')
|
||||
->description('An image blob in the DB')
|
||||
->arguments(array(
|
||||
'file_function' => 'file_blob',
|
||||
// Alternatively, specify a column here for dynamic file name.
|
||||
'source_path' => 'druplicon.png',
|
||||
->description('An image blob in the DB')
|
||||
->arguments(array(
|
||||
'file_function' => 'file_blob',
|
||||
// Alternatively, specify a column here for dynamic file name.
|
||||
'source_path' => 'druplicon.png',
|
||||
|
||||
));
|
||||
));
|
||||
$this->addFieldMapping('created', 'CREATED');
|
||||
$this->addFieldMapping('changed', 'UPDATED');
|
||||
|
||||
|
||||
// Unmapped destination fields
|
||||
$this->addUnmigratedDestinations(array('is_new', 'status', 'promote',
|
||||
'revision', 'language', 'sticky', 'revision_uid', 'path'));
|
||||
$this->addUnmigratedDestinations(array(
|
||||
'is_new',
|
||||
'status',
|
||||
'promote',
|
||||
'revision',
|
||||
'language',
|
||||
'sticky',
|
||||
'revision_uid',
|
||||
'path',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +105,7 @@ function migrate_example_oracle_migrate_api() {
|
||||
'api' => 2,
|
||||
'migrations' => array(
|
||||
'MigrateExampleOracle' => array('class_name' => 'MigrateExampleOracleNode'),
|
||||
)
|
||||
),
|
||||
);
|
||||
return $api;
|
||||
}
|
||||
|
||||
+27
-11
@@ -20,20 +20,36 @@ function migrate_example_oracle_generate($length) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of data rows for testing Oracle import. Note that 4000 is a magic
|
||||
* number for Oracle LOB datatypes, so we testing lengths above and below that limit.
|
||||
* Return an array of data rows for testing Oracle import. Note that 4000 is a
|
||||
* magic number for Oracle LOB datatypes, so we testing lengths above and below
|
||||
* that limit.
|
||||
*/
|
||||
function migrate_example_oracle_sample_data() {
|
||||
$image = file_get_contents('misc/druplicon.png');
|
||||
return array(
|
||||
array('oid' => 3, 'title' => 'Sample title', 'body' => 'Sample body',
|
||||
'mainimage' => $image, 'created' => '2011/05/01 01:02:03',
|
||||
'updated' => '2011/06/30 04:05:06'),
|
||||
array('oid' => 5, 'title' => 'Another title', 'body' => migrate_example_oracle_generate(3900),
|
||||
'mainimage' => $image, 'created' => '2011/08/12 07:08:09',
|
||||
'updated' => '2011/12/25 10:11:12'),
|
||||
array('oid' => 7, 'title' => 'Yet another title', 'body' => migrate_example_oracle_generate(4500),
|
||||
'mainimage' => $image, 'created' => '2012/01/01 13:14:15',
|
||||
'updated' => '2012/03/14 16:17:18'),
|
||||
array(
|
||||
'oid' => 3,
|
||||
'title' => 'Sample title',
|
||||
'body' => 'Sample body',
|
||||
'mainimage' => $image,
|
||||
'created' => '2011/05/01 01:02:03',
|
||||
'updated' => '2011/06/30 04:05:06',
|
||||
),
|
||||
array(
|
||||
'oid' => 5,
|
||||
'title' => 'Another title',
|
||||
'body' => migrate_example_oracle_generate(3900),
|
||||
'mainimage' => $image,
|
||||
'created' => '2011/08/12 07:08:09',
|
||||
'updated' => '2011/12/25 10:11:12',
|
||||
),
|
||||
array(
|
||||
'oid' => 7,
|
||||
'title' => 'Yet another title',
|
||||
'body' => migrate_example_oracle_generate(4500),
|
||||
'mainimage' => $image,
|
||||
'created' => '2012/01/01 13:14:15',
|
||||
'updated' => '2012/03/14 16:17:18',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,39 +65,20 @@ function migrate_example_wine_uninstall() {
|
||||
}
|
||||
|
||||
function migrate_example_wine_disable() {
|
||||
MigrationBase::deregisterMigration('WinePrep');
|
||||
Migration::deregisterMigration('WineFileCopy');
|
||||
Migration::deregisterMigration('WineFileBlob');
|
||||
Migration::deregisterMigration('WineRegion');
|
||||
Migration::deregisterMigration('WineUser');
|
||||
Migration::deregisterMigration('WineVariety');
|
||||
Migration::deregisterMigration('WineBestWith');
|
||||
Migration::deregisterMigration('WineProducer');
|
||||
Migration::deregisterMigration('WineProducerXML');
|
||||
Migration::deregisterMigration('WineProducerXMLPull');
|
||||
Migration::deregisterMigration('WineProducerMultiXML');
|
||||
Migration::deregisterMigration('WineWine');
|
||||
Migration::deregisterMigration('WineComment');
|
||||
MigrationBase::deregisterMigration('WineFinish');
|
||||
Migration::deregisterMigration('WineUpdates');
|
||||
Migration::deregisterMigration('WineUserUpdates');
|
||||
Migration::deregisterMigration('WineVarietyUpdates');
|
||||
Migration::deregisterMigration('WineCommentUpdates');
|
||||
Migration::deregisterMigration('WineRole');
|
||||
Migration::deregisterMigration('WineTable');
|
||||
MigrateGroup::deregister('wine');
|
||||
}
|
||||
|
||||
function migrate_example_wine_schema_wine() {
|
||||
return array(
|
||||
'description' => 'Wines of the world',
|
||||
'fields' => array(
|
||||
'wineid' => array(
|
||||
'wineid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Wine ID',
|
||||
),
|
||||
'name' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
@@ -159,7 +140,7 @@ function migrate_example_wine_schema_updates() {
|
||||
return array(
|
||||
'description' => 'Updated wine ratings',
|
||||
'fields' => array(
|
||||
'wineid' => array(
|
||||
'wineid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -180,13 +161,13 @@ function migrate_example_wine_schema_producer() {
|
||||
return array(
|
||||
'description' => 'Wine producers of the world',
|
||||
'fields' => array(
|
||||
'producerid' => array(
|
||||
'producerid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Producer ID',
|
||||
),
|
||||
'name' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
@@ -230,7 +211,7 @@ function migrate_example_wine_schema_categories() {
|
||||
'not null' => TRUE,
|
||||
'description' => 'Type of category: variety, region, best_with',
|
||||
),
|
||||
'name' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
@@ -261,12 +242,12 @@ function migrate_example_wine_schema_vintages() {
|
||||
return array(
|
||||
'description' => 'Wine vintages',
|
||||
'fields' => array(
|
||||
'wineid' => array(
|
||||
'wineid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Wine ID',
|
||||
),
|
||||
'vintage' => array(
|
||||
'vintage' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -301,12 +282,12 @@ function migrate_example_wine_schema_category_wine() {
|
||||
return array(
|
||||
'description' => 'Wine category assignments',
|
||||
'fields' => array(
|
||||
'wineid' => array(
|
||||
'wineid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Wine ID',
|
||||
),
|
||||
'categoryid' => array(
|
||||
'categoryid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -321,12 +302,12 @@ function migrate_example_wine_schema_category_producer() {
|
||||
return array(
|
||||
'description' => 'Producer category assignments',
|
||||
'fields' => array(
|
||||
'producerid' => array(
|
||||
'producerid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Producer ID',
|
||||
),
|
||||
'categoryid' => array(
|
||||
'categoryid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -341,13 +322,13 @@ function migrate_example_wine_schema_comment() {
|
||||
return array(
|
||||
'description' => 'Wine comments',
|
||||
'fields' => array(
|
||||
'commentid' => array(
|
||||
'commentid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Comment ID',
|
||||
),
|
||||
'wineid' => array(
|
||||
'wineid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -422,7 +403,7 @@ function migrate_example_wine_schema_comment_updates() {
|
||||
return array(
|
||||
'description' => 'Wine comment updates',
|
||||
'fields' => array(
|
||||
'commentid' => array(
|
||||
'commentid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -443,12 +424,12 @@ function migrate_example_wine_schema_account() {
|
||||
return array(
|
||||
'description' => 'Wine accounts.',
|
||||
'fields' => array(
|
||||
'accountid' => array(
|
||||
'accountid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Account ID',
|
||||
),
|
||||
'status' => array(
|
||||
'status' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Blocked_Allowed',
|
||||
@@ -507,7 +488,7 @@ function migrate_example_wine_schema_account() {
|
||||
'not null' => TRUE,
|
||||
'description' => 'Signature for comments',
|
||||
),
|
||||
'imageid' => array(
|
||||
'imageid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => FALSE,
|
||||
@@ -528,7 +509,7 @@ function migrate_example_wine_schema_account_updates() {
|
||||
return array(
|
||||
'description' => 'Wine account updates',
|
||||
'fields' => array(
|
||||
'accountid' => array(
|
||||
'accountid' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Account ID',
|
||||
@@ -548,7 +529,7 @@ function migrate_example_wine_schema_blobs() {
|
||||
return array(
|
||||
'description' => 'Wine blobs to be migrated to file entities',
|
||||
'fields' => array(
|
||||
'imageid' => array(
|
||||
'imageid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -568,31 +549,31 @@ function migrate_example_wine_schema_files() {
|
||||
return array(
|
||||
'description' => 'Wine and account files',
|
||||
'fields' => array(
|
||||
'imageid' => array(
|
||||
'imageid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Image ID',
|
||||
),
|
||||
'url' => array(
|
||||
'url' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Image URL',
|
||||
),
|
||||
'image_alt' => array(
|
||||
'image_alt' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Image alt',
|
||||
),
|
||||
'image_title' => array(
|
||||
'image_title' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Image title',
|
||||
),
|
||||
'wineid' => array(
|
||||
'wineid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => FALSE,
|
||||
@@ -607,19 +588,19 @@ function migrate_example_wine_schema_table_source() {
|
||||
return array(
|
||||
'description' => 'Source data to go into a custom Drupal table',
|
||||
'fields' => array(
|
||||
'fooid' => array(
|
||||
'fooid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Primary key',
|
||||
),
|
||||
'field1' => array(
|
||||
'field1' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'First field',
|
||||
),
|
||||
'field2' => array(
|
||||
'field2' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -634,19 +615,19 @@ function migrate_example_wine_schema_table_dest() {
|
||||
return array(
|
||||
'description' => 'Custom Drupal table to receive source data directly',
|
||||
'fields' => array(
|
||||
'recordid' => array(
|
||||
'recordid' => array(
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Primary key',
|
||||
),
|
||||
'drupal_text' => array(
|
||||
'drupal_text' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'First field',
|
||||
),
|
||||
'drupal_int' => array(
|
||||
'drupal_int' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
@@ -963,9 +944,12 @@ function migrate_example_wine_fields() {
|
||||
|
||||
function migrate_example_wine_content_type_delete() {
|
||||
$bundle = 'migrate_example_wine';
|
||||
$field_names = array('migrate_example_wine_varieties',
|
||||
'migrate_example_wine_regions', 'migrate_example_wine_best_with',
|
||||
'field_migrate_example_image');
|
||||
$field_names = array(
|
||||
'migrate_example_wine_varieties',
|
||||
'migrate_example_wine_regions',
|
||||
'migrate_example_wine_best_with',
|
||||
'field_migrate_example_image',
|
||||
);
|
||||
foreach ($field_names as $field_name) {
|
||||
$instance = field_info_instance('node', $field_name, $bundle);
|
||||
field_delete_instance($instance);
|
||||
@@ -983,15 +967,45 @@ function migrate_example_wine_content_type_delete() {
|
||||
}
|
||||
|
||||
function migrate_example_wine_data_wine() {
|
||||
$fields = array('wineid', 'name', 'body', 'excerpt', 'accountid',
|
||||
'posted', 'last_changed', 'variety', 'region', 'rating');
|
||||
$fields = array(
|
||||
'wineid',
|
||||
'name',
|
||||
'body',
|
||||
'excerpt',
|
||||
'accountid',
|
||||
'posted',
|
||||
'last_changed',
|
||||
'variety',
|
||||
'region',
|
||||
'rating',
|
||||
);
|
||||
$query = db_insert('migrate_example_wine')
|
||||
->fields($fields);
|
||||
->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),
|
||||
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));
|
||||
@@ -1002,7 +1016,7 @@ function migrate_example_wine_data_wine() {
|
||||
function migrate_example_wine_data_updates() {
|
||||
$fields = array('wineid', 'rating');
|
||||
$query = db_insert('migrate_example_wine_updates')
|
||||
->fields($fields);
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, 93),
|
||||
array(2, NULL),
|
||||
@@ -1016,7 +1030,7 @@ function migrate_example_wine_data_updates() {
|
||||
function migrate_example_wine_data_producer() {
|
||||
$fields = array('producerid', 'name', 'body', 'excerpt', 'accountid');
|
||||
$query = db_insert('migrate_example_wine_producer')
|
||||
->fields($fields);
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, 'Montes', 'Fine Chilean winery', 'Great!', 9),
|
||||
array(2, 'Archeo', 'Sicilia!', NULL, 3),
|
||||
@@ -1028,20 +1042,69 @@ function migrate_example_wine_data_producer() {
|
||||
}
|
||||
|
||||
function migrate_example_wine_data_account() {
|
||||
$fields = array('accountid', 'status', 'posted', 'last_access', 'last_login',
|
||||
'name', 'sex', 'password', 'mail', 'original_mail', 'sig', 'imageid', 'positions');
|
||||
$fields = array(
|
||||
'accountid',
|
||||
'status',
|
||||
'posted',
|
||||
'last_access',
|
||||
'last_login',
|
||||
'name',
|
||||
'sex',
|
||||
'password',
|
||||
'mail',
|
||||
'original_mail',
|
||||
'sig',
|
||||
'imageid',
|
||||
'positions',
|
||||
);
|
||||
$query = db_insert('migrate_example_wine_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'),
|
||||
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));
|
||||
@@ -1052,7 +1115,7 @@ function migrate_example_wine_data_account() {
|
||||
function migrate_example_wine_data_account_updates() {
|
||||
$fields = array('accountid', 'sex');
|
||||
$query = db_insert('migrate_example_wine_account_updates')
|
||||
->fields($fields);
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, NULL),
|
||||
array(3, 'M'),
|
||||
@@ -1065,26 +1128,93 @@ function migrate_example_wine_data_account_updates() {
|
||||
}
|
||||
|
||||
function migrate_example_wine_data_comment() {
|
||||
$fields = array('commentid', 'wineid', 'comment_parent', 'subject', 'body',
|
||||
'name', 'mail', 'accountid', 'commenthost', 'userpage', 'posted', 'lastchanged');
|
||||
$fields = array(
|
||||
'commentid',
|
||||
'wineid',
|
||||
'comment_parent',
|
||||
'subject',
|
||||
'body',
|
||||
'name',
|
||||
'mail',
|
||||
'accountid',
|
||||
'commenthost',
|
||||
'userpage',
|
||||
'posted',
|
||||
'lastchanged',
|
||||
);
|
||||
$query = db_insert('migrate_example_wine_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')),
|
||||
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));
|
||||
@@ -1095,7 +1225,7 @@ function migrate_example_wine_data_comment() {
|
||||
function migrate_example_wine_data_comment_updates() {
|
||||
$fields = array('commentid', 'subject');
|
||||
$query = db_insert('migrate_example_wine_comment_updates')
|
||||
->fields($fields);
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, 'I am first'),
|
||||
array(2, 'I am second'),
|
||||
@@ -1110,12 +1240,33 @@ function migrate_example_wine_data_comment_updates() {
|
||||
}
|
||||
|
||||
function migrate_example_wine_data_categories() {
|
||||
$fields = array('categoryid', 'type', 'name', 'category_parent', 'details', 'ordering');
|
||||
$fields = array(
|
||||
'categoryid',
|
||||
'type',
|
||||
'name',
|
||||
'category_parent',
|
||||
'details',
|
||||
'ordering',
|
||||
);
|
||||
$query = db_insert('migrate_example_wine_categories')
|
||||
->fields($fields);
|
||||
->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(
|
||||
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),
|
||||
@@ -1147,7 +1298,7 @@ function migrate_example_wine_data_categories() {
|
||||
function migrate_example_wine_data_vintages() {
|
||||
$fields = array('wineid', 'vintage');
|
||||
$query = db_insert('migrate_example_wine_vintages')
|
||||
->fields($fields);
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, 2006),
|
||||
array(1, 2007),
|
||||
@@ -1162,7 +1313,7 @@ function migrate_example_wine_data_vintages() {
|
||||
function migrate_example_wine_data_variety_updates() {
|
||||
$fields = array('categoryid', 'details');
|
||||
$query = db_insert('migrate_example_wine_variety_updates')
|
||||
->fields($fields);
|
||||
->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'),
|
||||
@@ -1212,10 +1363,28 @@ function migrate_example_wine_data_files() {
|
||||
$query = db_insert('migrate_example_wine_files')
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, 'http://drupal.org/sites/all/modules/drupalorg/drupalorg/images/association-individual.png', 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),
|
||||
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));
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<pr:producer xmlns:pr="http://www.wine.org/wine-producers">
|
||||
<pr:name>Château Latour</pr:name>
|
||||
<pr:description>Makers of grand vin Chateau Latour, Les Forts de Latour and Pauillac</pr:description>
|
||||
<pr:authorid>3</pr:authorid>
|
||||
<pr:region>Bordeaux</pr:region>
|
||||
</pr:producer>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<wn:content xmlns:wn="http://www.wine.org/wine">
|
||||
<wn:sourceid>0002</wn:sourceid>
|
||||
</wn:content>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<pr:producers xmlns:pr="http://www.wine.org/wine-producers">
|
||||
<pr:producer>
|
||||
<pr:sourceid>0009</pr:sourceid>
|
||||
<pr:name>Château Feytit Clinet</pr:name>
|
||||
<pr:description>Good things, they say, come in small packages; this is certainly the case at Château Feyit Clinet, the maker of thelabel Pomerol.</pr:description>
|
||||
<pr:authorid>1</pr:authorid>
|
||||
<pr:region>Pomerol</pr:region>
|
||||
</pr:producer>
|
||||
<pr:producer>
|
||||
<pr:sourceid>0010</pr:sourceid>
|
||||
<pr:name>Château Doisy-Védrines</pr:name>
|
||||
<pr:description>Blessed with ancient vines, the fruit here is often subject to high levels of Botrytis (or noble rot), which shrinks the grapes and concentrates sugar levels in the remaining juice.</pr:description>
|
||||
<pr:authorid>3</pr:authorid>
|
||||
<pr:region>Barsac</pr:region>
|
||||
</pr:producer>
|
||||
</pr:producers>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<pr:producers xmlns:pr="http://www.wine.org/wine-producers">
|
||||
<pr:producer>
|
||||
<pr:sourceid>0009</pr:sourceid>
|
||||
<pr:name>Château Bourgneuf</pr:name>
|
||||
<pr:description>Showing attractive red and dark fruits, cherry and plum, it is rich and even slightly racy, before concluding with classic Pomerol concentration and focus.</pr:description>
|
||||
<pr:authorid>3</pr:authorid>
|
||||
<pr:region>Pomerol</pr:region>
|
||||
</pr:producer>
|
||||
<pr:producer>
|
||||
<pr:sourceid>0010</pr:sourceid>
|
||||
<pr:name>Château Doisy-Daëne</pr:name>
|
||||
<pr:description>Medium bodied, with elegance rather than density there is a wide spectrum of flavours; apples, peaches, lemongrass and a touch of white spice. Delicious now.</pr:description>
|
||||
<pr:authorid>9</pr:authorid>
|
||||
<pr:region>Barsac</pr:region>
|
||||
</pr:producer>
|
||||
</pr:producers>
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
function migrate_example_baseball_node_info() {
|
||||
$items = array(
|
||||
'migrate_example_baseball' => array(
|
||||
'name' => t('migrate_example_baseball'),
|
||||
'name' => t('Migrate example - Baseball'),
|
||||
'base' => 'node_content',
|
||||
'description' => t('A baseball box score'),
|
||||
'has_title' => '1',
|
||||
|
||||
+4
-5
@@ -21,12 +21,11 @@ features[field][] = "node-migrate_example_baseball-field_visiting_team"
|
||||
features[node][] = "migrate_example_baseball"
|
||||
files[] = "migrate_example_baseball.migrate.inc"
|
||||
name = "migrate_example_baseball"
|
||||
package = "Migrate Examples"
|
||||
package = "Migration"
|
||||
php = "5.2.4"
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-07
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2018-06-10
|
||||
version = "7.x-2.11"
|
||||
core = "7.x"
|
||||
project = "migrate"
|
||||
datestamp = "1352299007"
|
||||
|
||||
datestamp = "1528674486"
|
||||
|
||||
+21
-17
@@ -8,11 +8,11 @@
|
||||
function migrate_example_baseball_enable() {
|
||||
$path = dirname(__FILE__) . '/data';
|
||||
migrate_example_baseball_get_files($path);
|
||||
for ($i=0; $i<=9; $i++) {
|
||||
$file = 'gl200' . $i . '.txt';
|
||||
for ($i = 0; $i <= 9; $i++) {
|
||||
$file = 'GL200' . $i . '.TXT';
|
||||
Migration::registerMigration('GameBaseball',
|
||||
pathinfo($file, PATHINFO_FILENAME),
|
||||
array('source_file' => $path . '/' . $file));
|
||||
array('source_file' => $path . '/' . $file, 'group_name' => 'baseball'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ function migrate_example_baseball_get_files($path) {
|
||||
if (!file_exists("$path/GL2000.TXT") && !file_exists("$path/gl2000.txt")) {
|
||||
file_prepare_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
|
||||
$result = copy('http://www.retrosheet.org/gamelogs/gl2000_09.zip',
|
||||
$path . '/gl2000_09.zip');
|
||||
$path . '/gl2000_09.zip');
|
||||
if ($result) {
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($path . '/gl2000_09.zip');
|
||||
@@ -39,11 +39,22 @@ function migrate_example_baseball_get_files($path) {
|
||||
|
||||
function migrate_example_baseball_uninstall() {
|
||||
$bundle = 'migrate_example_baseball';
|
||||
$field_names = array('field_park', 'field_home_team', 'field_home_game_number',
|
||||
'field_home_score', 'field_visiting_score', 'field_outs', 'field_attendance',
|
||||
'field_duration', 'field_home_batters', 'field_visiting_batters',
|
||||
'field_home_pitcher', 'field_visiting_pitcher', 'field_visiting_team',
|
||||
'field_start_date');
|
||||
$field_names = array(
|
||||
'field_park',
|
||||
'field_home_team',
|
||||
'field_home_game_number',
|
||||
'field_home_score',
|
||||
'field_visiting_score',
|
||||
'field_outs',
|
||||
'field_attendance',
|
||||
'field_duration',
|
||||
'field_home_batters',
|
||||
'field_visiting_batters',
|
||||
'field_home_pitcher',
|
||||
'field_visiting_pitcher',
|
||||
'field_visiting_team',
|
||||
'field_start_date',
|
||||
);
|
||||
foreach ($field_names as $field_name) {
|
||||
$instance = field_info_instance('node', $field_name, $bundle);
|
||||
field_delete_instance($instance);
|
||||
@@ -53,14 +64,7 @@ function migrate_example_baseball_uninstall() {
|
||||
}
|
||||
|
||||
function migrate_example_baseball_disable() {
|
||||
for ($i=0; $i<=9; $i++) {
|
||||
$file = 'gl200' . $i . '.txt';
|
||||
Migration::deregisterMigration(pathinfo($file, PATHINFO_FILENAME));
|
||||
$filename = dirname(__FILE__) . '/data/' . $file;
|
||||
if (file_exists($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
}
|
||||
MigrateGroup::deregister('baseball');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+53
-42
@@ -13,39 +13,47 @@
|
||||
function migrate_example_baseball_migrate_api() {
|
||||
$api = array(
|
||||
'api' => 2,
|
||||
'groups' => array(
|
||||
'baseball' => array(
|
||||
'title' => t('Baseball'),
|
||||
),
|
||||
),
|
||||
);
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* A dynamic migration that is reused for each source CSV file.
|
||||
* A migration that is reused for each source CSV file.
|
||||
*/
|
||||
class GameBaseball extends DynamicMigration {
|
||||
public function __construct(array $arguments) {
|
||||
$this->arguments = $arguments;
|
||||
parent::__construct();
|
||||
class GameBaseball extends Migration {
|
||||
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
$this->description = t('Import box scores from CSV file.');
|
||||
|
||||
// Create a map object for tracking the relationships between source rows
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array(
|
||||
'start_date' => array('type' => 'varchar',
|
||||
'length' => 8,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Start date',
|
||||
),
|
||||
'home_team' => array('type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Home team',
|
||||
),
|
||||
'home_game_number' => array('type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Home team game number',
|
||||
),
|
||||
array(
|
||||
'start_date' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 8,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Start date',
|
||||
),
|
||||
MigrateDestinationNode::getKeySchema()
|
||||
);
|
||||
'home_team' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Home team',
|
||||
),
|
||||
'home_game_number' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Home team game number',
|
||||
),
|
||||
),
|
||||
MigrateDestinationNode::getKeySchema()
|
||||
);
|
||||
|
||||
// Create a MigrateSource object, which manages retrieving the input data.
|
||||
$this->source = new MigrateSourceCSV($arguments['source_file'], $this->csvcolumns(), array(), $this->fields());
|
||||
@@ -53,7 +61,7 @@ class GameBaseball extends DynamicMigration {
|
||||
$this->destination = new MigrateDestinationNode('migrate_example_baseball');
|
||||
|
||||
$this->addFieldMapping('title', 'title')
|
||||
->description('See prepareRow().');
|
||||
->description('See prepareRow().');
|
||||
$this->addFieldMapping('field_start_date', 'start_date');
|
||||
$this->addFieldMapping('field_park', 'park_id');
|
||||
$this->addFieldMapping('field_visiting_team', 'visiting_team');
|
||||
@@ -64,24 +72,24 @@ class GameBaseball extends DynamicMigration {
|
||||
$this->addFieldMapping('field_outs', 'outs');
|
||||
$this->addFieldMapping('field_attendance', 'attendance');
|
||||
$this->addFieldMapping('field_duration', 'duration')
|
||||
->defaultValue(NULL);
|
||||
->defaultValue(NULL);
|
||||
$this->addFieldMapping('field_home_pitcher', 'home_pitcher');
|
||||
$this->addFieldMapping('field_visiting_pitcher', 'visiting_pitcher');
|
||||
$this->addFieldMapping('field_home_batters', 'home_batters')
|
||||
->separator(',')
|
||||
->description('See prepareRow().');
|
||||
->separator(',')
|
||||
->description('See prepareRow().');
|
||||
$this->addFieldMapping('field_visiting_batters', 'visiting_batters')
|
||||
->separator(',')
|
||||
->description('See prepareRow().');
|
||||
for ($i=1; $i <= 9; $i++ ) {
|
||||
->separator(',')
|
||||
->description('See prepareRow().');
|
||||
for ($i = 1; $i <= 9; $i++) {
|
||||
$this->addFieldMapping(NULL, "visiting_batter_$i")
|
||||
->description('Not needed since we use the multi-value field: visiting_batters.');
|
||||
->description('Not needed since we use the multi-value field: visiting_batters.');
|
||||
$this->addFieldMapping(NULL, "home_batter_$i")
|
||||
->description('Not needed since we use the multi-value field: home_batters.');
|
||||
->description('Not needed since we use the multi-value field: home_batters.');
|
||||
}
|
||||
}
|
||||
|
||||
function csvcolumns() {
|
||||
protected function csvcolumns() {
|
||||
// Note: Remember to subtract 1 from column number at http://www.retrosheet.org/gamelogs/glfields.txt
|
||||
$columns[0] = array('start_date', 'Date of game');
|
||||
$columns[3] = array('visiting_team', 'Visiting team');
|
||||
@@ -93,22 +101,25 @@ class GameBaseball extends DynamicMigration {
|
||||
$columns[16] = array('park_id', 'Ballpark ID');
|
||||
$columns[17] = array('attendance', 'Attendance');
|
||||
$columns[18] = array('duration', 'Duration in minutes');
|
||||
for ($i=1; $i <= 9; $i++ ) {
|
||||
$columns[103+3*$i] = array("visiting_batter_$i", "Visiting batter $i");
|
||||
$columns[130+3*$i] = array("home_batter_$i", "Home batter $i");
|
||||
for ($i = 1; $i <= 9; $i++) {
|
||||
$columns[103 + 3 * $i] = array(
|
||||
"visiting_batter_$i",
|
||||
"Visiting batter $i",
|
||||
);
|
||||
$columns[130 + 3 * $i] = array("home_batter_$i", "Home batter $i");
|
||||
}
|
||||
$columns[102] = array('visiting_pitcher', 'Visiting starting pitcher');
|
||||
$columns[104] = array('home_pitcher', 'Home starting pitcher');
|
||||
return $columns;
|
||||
}
|
||||
|
||||
function prepareRow($row) {
|
||||
public function prepareRow($row) {
|
||||
// Collect all the batters into one multi-value field.
|
||||
for ($i=1; $i <= 9; $i++ ) {
|
||||
for ($i = 1; $i <= 9; $i++) {
|
||||
$key = "visiting_batter_$i";
|
||||
$visiting_batters[] = $row->$key;
|
||||
$visiting_batters[] = $row->{$key};
|
||||
$key = "home_batter_$i";
|
||||
$home_batters[] = $row->$key;
|
||||
$home_batters[] = $row->{$key};
|
||||
}
|
||||
$row->visiting_batters = implode(',', $visiting_batters);
|
||||
$row->home_batters = implode(',', $home_batters);
|
||||
@@ -120,14 +131,14 @@ class GameBaseball extends DynamicMigration {
|
||||
*/
|
||||
protected function generateMachineName($class_name = NULL) {
|
||||
return drupal_strtolower(pathinfo($this->arguments['source_file'],
|
||||
PATHINFO_FILENAME));
|
||||
PATHINFO_FILENAME));
|
||||
}
|
||||
|
||||
function fields() {
|
||||
public function fields() {
|
||||
return array(
|
||||
'title' => 'A composite field made during prepareRow().',
|
||||
'home_batters' => 'An array of batters, populated during prepareRow().',
|
||||
'visiting_batters' => 'An array of batters, populated during prepareRow().',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
|
||||
table.migrate-dashboard tr.migrate-running {
|
||||
background-color: #CFC;
|
||||
background-color: #cfc;
|
||||
}
|
||||
|
||||
.migrate-running {
|
||||
background-color: #CFC;
|
||||
background-color: #cfc;
|
||||
}
|
||||
|
||||
.migrate-option-separator {
|
||||
margin-bottom:0.2em;
|
||||
margin-bottom: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
#migrate-migration-info td.migrate-error {
|
||||
background: #ffc9c9;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
name = "Migrate UI"
|
||||
description = "UI for managing migration processes"
|
||||
package = "Development"
|
||||
;configure = admin/config/development/migrate
|
||||
package = "Migration"
|
||||
configure = admin/content/migrate/configure
|
||||
core = 7.x
|
||||
dependencies[] = migrate
|
||||
files[] = migrate_ui.module
|
||||
files[] = migrate_ui.wizard.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-07
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2018-06-10
|
||||
version = "7.x-2.11"
|
||||
core = "7.x"
|
||||
project = "migrate"
|
||||
datestamp = "1352299007"
|
||||
|
||||
datestamp = "1528674486"
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Install/update function for migrate_ui.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function migrate_ui_install() {
|
||||
migrate_ui_set_weight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function migrate_ui_uninstall() {
|
||||
variable_del('migrate_import_method');
|
||||
variable_del('migrate_drush_path');
|
||||
variable_del('migrate_drush_mail');
|
||||
variable_del('migrate_drush_mail_subject');
|
||||
variable_del('migrate_drush_mail_body');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we have a higher weight than node.
|
||||
*/
|
||||
function migrate_ui_update_7201() {
|
||||
migrate_ui_set_weight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the weight of migrate_ui higher than node, so Import links come after
|
||||
* "Add content" at admin/content.
|
||||
*/
|
||||
function migrate_ui_set_weight() {
|
||||
$node_weight = db_select('system', 's')
|
||||
->fields('s', array('weight'))
|
||||
->condition('name', 'node')
|
||||
->execute()
|
||||
->fetchField();
|
||||
db_update('system')
|
||||
->fields(array('weight' => $node_weight + 1))
|
||||
->condition('name', 'migrate_ui')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* If WordPress Migrate has background imports via drush enabled, copy the
|
||||
* configuration to the new general Migrate support.
|
||||
*/
|
||||
function migrate_ui_update_7202() {
|
||||
$drush_command = variable_get('wordpress_migrate_drush', '');
|
||||
if ($drush_command) {
|
||||
variable_set('migrate_drush_path', $drush_command);
|
||||
// Consolidate these two variables into import method - 0 means immediate
|
||||
// only, 1 means drush only, 2 means offer both options.
|
||||
$import_method = variable_get('wordpress_migrate_import_method', 0);
|
||||
$force_drush = variable_get('wordpress_migrate_force_drush', FALSE);
|
||||
if (!$force_drush) {
|
||||
$import_method = 2;
|
||||
}
|
||||
variable_set('migrate_import_method', $import_method);
|
||||
variable_set('migrate_drush_mail',
|
||||
variable_get('wordpress_migrate_notification', 0));
|
||||
variable_set('migrate_drush_mail_subject',
|
||||
variable_get('wordpress_migrate_notification_subject', ''));
|
||||
variable_set('migrate_drush_mail_body',
|
||||
variable_get('wordpress_migrate_notification_body', ''));
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,12 @@
|
||||
<?php
|
||||
|
||||
define('MIGRATE_ACCESS_BASIC', 'migration information');
|
||||
|
||||
function migrate_ui_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/migrate':
|
||||
return t('The current status of each migration defined in this system. Click on a migration name for details on its configuration.');
|
||||
case 'admin/content/migrate':
|
||||
return t('The current status of each migration group defined in this system. Click on a group name for details on its configuration.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_permission().
|
||||
*/
|
||||
function migrate_ui_permission() {
|
||||
return array(
|
||||
MIGRATE_ACCESS_BASIC => array(
|
||||
'title' => t('Basic access to migration information'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_menu().
|
||||
*/
|
||||
@@ -27,67 +14,171 @@ function migrate_ui_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['admin/content/migrate'] = array(
|
||||
'title' => 'Migrate',
|
||||
'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM,
|
||||
'description' => 'Monitor the creation of Drupal content from source data',
|
||||
'page callback' => 'migrate_ui_dashboard',
|
||||
'title' => 'Migrate',
|
||||
'description' => 'Manage importing of data into your Drupal site',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('migrate_ui_migrate_dashboard'),
|
||||
'access arguments' => array(MIGRATE_ACCESS_BASIC),
|
||||
'access callback' => 'user_access',
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
);
|
||||
$items['admin/content/migrate/dashboard'] = array(
|
||||
'title' => 'Migrate',
|
||||
$items['admin/content/migrate/groups'] = array(
|
||||
'title' => 'Dashboard',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'weight' => -10,
|
||||
);
|
||||
$items['admin/content/migrate/registration'] = array(
|
||||
'title' => 'Registration',
|
||||
|
||||
$items['admin/content/migrate/configure'] = array(
|
||||
'title' => 'Configuration',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'description' => 'Configure class registration',
|
||||
'page callback' => 'migrate_ui_registration',
|
||||
'access arguments' => array(MIGRATE_ACCESS_BASIC),
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
'weight' => 5,
|
||||
);
|
||||
$items['admin/content/migrate/handlers'] = array(
|
||||
'title' => 'Handlers',
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'description' => 'Configure migration handlers',
|
||||
'page callback' => 'migrate_ui_handlers',
|
||||
'access arguments' => array(MIGRATE_ACCESS_BASIC),
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
'weight' => 10,
|
||||
);
|
||||
$items['admin/content/migrate/messages/%migration'] = array(
|
||||
'title callback' => '_migrate_ui_title',
|
||||
'title arguments' => array(4),
|
||||
'description' => 'View messages from a migration',
|
||||
'page callback' => 'migrate_ui_messages',
|
||||
'page arguments' => array(4),
|
||||
'access arguments' => array(MIGRATE_ACCESS_BASIC),
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
);
|
||||
$items['admin/content/migrate/%migration'] = array(
|
||||
'title callback' => '_migrate_ui_title',
|
||||
'title arguments' => array(3),
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('migrate_migration_info', 3),
|
||||
'access arguments' => array(MIGRATE_ACCESS_BASIC),
|
||||
'description' => 'Configure migration settings',
|
||||
'page callback' => 'migrate_ui_configure',
|
||||
'access arguments' => array(MIGRATE_ACCESS_ADVANCED),
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
'weight' => 100,
|
||||
);
|
||||
|
||||
// Add tabs for each implemented migration wizard.
|
||||
$wizards = migrate_ui_wizards();
|
||||
foreach ($wizards as $wizard_class => $wizard) {
|
||||
$items["admin/content/migrate/new/$wizard_class"] = array(
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'title' => 'Import from @source_title',
|
||||
'title arguments' => array('@source_title' => $wizard->getSourceName()),
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('migrate_ui_wizard', $wizard_class),
|
||||
'access arguments' => array(MIGRATE_ACCESS_BASIC),
|
||||
'file' => 'migrate_ui.wizard.inc',
|
||||
);
|
||||
}
|
||||
|
||||
$items['admin/content/migrate/groups/%'] =
|
||||
array(
|
||||
'title callback' => 'migrate_ui_migrate_group_title',
|
||||
'title arguments' => array(4),
|
||||
'type' => MENU_NORMAL_ITEM,
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('migrate_ui_migrate_group', 4),
|
||||
'access arguments' => array(MIGRATE_ACCESS_BASIC),
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
);
|
||||
|
||||
$items['admin/content/migrate/groups/%/%'] =
|
||||
array(
|
||||
'title callback' => 'migrate_ui_migrate_migration_title',
|
||||
'title arguments' => array(5),
|
||||
'type' => MENU_NORMAL_ITEM,
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('migrate_migration_info', 4, 5),
|
||||
'access arguments' => array(MIGRATE_ACCESS_BASIC),
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
);
|
||||
|
||||
$items['admin/content/migrate/groups/%/%/view'] =
|
||||
array(
|
||||
'title' => 'View',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
'weight' => -10,
|
||||
);
|
||||
|
||||
$items['admin/content/migrate/groups/%/%/edit'] =
|
||||
array(
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'title' => 'Edit',
|
||||
'description' => 'Edit migration mappings',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('migrate_ui_edit_mappings', 4, 5),
|
||||
'access arguments' => array(MIGRATE_ACCESS_ADVANCED),
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
);
|
||||
|
||||
$items['admin/content/migrate/groups/%/%/messages'] =
|
||||
array(
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'title' => 'Messages',
|
||||
'description' => 'View messages from a migration',
|
||||
'page callback' => 'migrate_ui_messages',
|
||||
'page arguments' => array(4, 5),
|
||||
'access arguments' => array(MIGRATE_ACCESS_ADVANCED),
|
||||
'file' => 'migrate_ui.pages.inc',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
// A menu load callback.
|
||||
function migration_load($machine_name) {
|
||||
if ($machine_name) {
|
||||
return Migration::getInstance($machine_name);
|
||||
}
|
||||
/**
|
||||
* Title callback for the migrate group view page.
|
||||
*/
|
||||
function migrate_ui_migrate_group_title($group_name) {
|
||||
$group = MigrateGroup::getInstance($group_name);
|
||||
return $group->getTitle();
|
||||
}
|
||||
|
||||
function _migrate_ui_title($migration) {
|
||||
if (is_string($migration)) {
|
||||
$migration = migration_load($migration);
|
||||
}
|
||||
return $migration->getMachineName();
|
||||
/**
|
||||
* Title callback for the migration view page.
|
||||
*/
|
||||
function migrate_ui_migrate_migration_title($migration_name) {
|
||||
return $migration_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function migrate_ui_theme() {
|
||||
return array(
|
||||
'migrate_ui_field_mapping_form' => array(
|
||||
'arguments' => array('field_mappings' => NULL),
|
||||
'render element' => 'field_mappings',
|
||||
'file' => '/migrate_ui.pages.inc',
|
||||
),
|
||||
'migrate_ui_field_mapping_dependencies' => array(
|
||||
'arguments' => array('dependencies' => NULL),
|
||||
'render element' => 'dependencies',
|
||||
'file' => '/migrate_ui.pages.inc',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_mail().
|
||||
*
|
||||
* @param $key
|
||||
* @param $message
|
||||
* @param $params
|
||||
*/
|
||||
function migrate_ui_mail($key, &$message, $params) {
|
||||
$options['language'] = $message['language'];
|
||||
user_mail_tokens($variables, array(), $options);
|
||||
$langcode = $message['language']->language;
|
||||
$subject = variable_get('migrate_drush_mail_subject', '');
|
||||
$message['subject'] = t($subject, array(), array('langcode' => $langcode));
|
||||
$body = variable_get('migrate_drush_mail_body', '');
|
||||
$body .= "\n" . $params['output'];
|
||||
$message['body'][] = t($body, array(), array('langcode' => $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info on all modules supporting a migration wizard.
|
||||
*
|
||||
* @return array
|
||||
* key: machine_name for a particular wizard implementation. Used in the menu
|
||||
* link.
|
||||
* value: Wizard configuration array containing:
|
||||
* source_title -
|
||||
*/
|
||||
function migrate_ui_wizards() {
|
||||
$module_apis = migrate_get_module_apis();
|
||||
$wizards = array();
|
||||
foreach ($module_apis as $info) {
|
||||
if (isset($info['wizard classes']) && is_array($info['wizard classes'])) {
|
||||
foreach ($info['wizard classes'] as $wizard_class) {
|
||||
$wizard_class = strtolower($wizard_class);
|
||||
$wizards[$wizard_class] = new $wizard_class;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $wizards;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,679 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Migration wizard framework.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The primary formbuilder function for the wizard form.
|
||||
*
|
||||
* This form has two defined submit handlers to process the different steps:
|
||||
* - Previous: handles the way to get back one step in the wizard.
|
||||
* - Next: handles each step form submission,
|
||||
*
|
||||
* The third handler, the finish button handler, is the default form _submit
|
||||
* handler used to process the information.
|
||||
*
|
||||
* @param string $class_name
|
||||
* Name of the MigrateUIWizard clsas for this wizard.
|
||||
*/
|
||||
function migrate_ui_wizard($form, &$form_state, $class_name = '') {
|
||||
// Rather than track state in $form_state, we simply keep our wizard
|
||||
// instance there, and it encapsulates all the state. We just need
|
||||
// to create the instance the first time in, and it will be serialized
|
||||
// between steps.
|
||||
/** @var MigrateUIWizard $wizard */
|
||||
if (empty($form_state['wizard'])) {
|
||||
$wizard = new $class_name();
|
||||
|
||||
// Add any extenders.
|
||||
$module_apis = migrate_get_module_apis();
|
||||
// Need a second pass at this to add wizard extenders.
|
||||
foreach ($module_apis as $module => $info) {
|
||||
// Add any extenders.
|
||||
// @todo: consider allowing extender classes to declare dependencies on
|
||||
// other extender classes, to ensure they work in the correct order?
|
||||
if (isset($info['wizard extenders'])) {
|
||||
foreach ($info['wizard extenders'] as $wizard_class => $extender_classes) {
|
||||
// Note that $class_name is in lower case, so we can't just use isset()
|
||||
// to find our wizard.
|
||||
if (strtolower($wizard_class) == $class_name) {
|
||||
foreach ($extender_classes as $extender_class) {
|
||||
$wizard->addExtender($extender_class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form_state['wizard'] = $wizard;
|
||||
}
|
||||
else {
|
||||
$wizard = $form_state['wizard'];
|
||||
}
|
||||
|
||||
// Fetch the form for the wizard's current step.
|
||||
$form = $wizard->form($form_state);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the "previous" button. Moves the wizard back to the
|
||||
* previous step, and retrieves the values that were submitted on that step.
|
||||
*
|
||||
* @todo: Can we remove steps that were dynamically added?
|
||||
*/
|
||||
function migrate_ui_wizard_previous_submit($form, &$form_state) {
|
||||
/** @var MigrateUIWizard $wizard */
|
||||
$wizard = $form_state['wizard'];
|
||||
$wizard->gotoPreviousStep($form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate handler for the 'next' button. Dispatches to the wizard's current
|
||||
* step for validation.
|
||||
*/
|
||||
function migrate_ui_wizard_next_validate($form, &$form_state) {
|
||||
/** @var MigrateUIWizard $wizard */
|
||||
$wizard = $form_state['wizard'];
|
||||
$wizard->formValidate($form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the 'next' button. Saves the form values for the step
|
||||
* we're leaving, so Previous can pick them up, and moves the wizard to the
|
||||
* next step.
|
||||
*/
|
||||
function migrate_ui_wizard_next_submit($form, &$form_state) {
|
||||
/** @var MigrateUIWizard $wizard */
|
||||
$wizard = $form_state['wizard'];
|
||||
$wizard->gotoNextStep($form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the Save settings button. Register the migrations that
|
||||
* were
|
||||
* (implicitly) defined along the way and redirect to the Migrate dashboard.
|
||||
*/
|
||||
function migrate_ui_wizard_submit($form, &$form_state) {
|
||||
/** @var MigrateUIWizard $wizard */
|
||||
$wizard = $form_state['wizard'];
|
||||
$wizard->formSaveSettings();
|
||||
$form_state['redirect'] = 'admin/content/migrate/groups/' .
|
||||
$wizard->getGroupName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the "Save settings and import" button. Register the
|
||||
* migrations that were (implicitly) defined along the way, run the import, and
|
||||
* redirect to the Migrate dashboard.
|
||||
*/
|
||||
function migrate_ui_wizard_migrate_submit($form, &$form_state) {
|
||||
/** @var MigrateUIWizard $wizard */
|
||||
$wizard = $form_state['wizard'];
|
||||
$wizard->formSaveSettings();
|
||||
$wizard->formPerformImport();
|
||||
$form_state['redirect'] = 'admin/content/migrate/groups/' .
|
||||
$wizard->getGroupName();
|
||||
}
|
||||
|
||||
/**
|
||||
* The base class for migration wizards. Extend this class to implement a
|
||||
* wizard UI for importing into Drupal from a given source format (Drupal,
|
||||
* WordPress, etc.).
|
||||
*/
|
||||
abstract class MigrateUIWizard {
|
||||
|
||||
/**
|
||||
* We maintain a doubly-linked list of wizard steps, both to support
|
||||
* previous/next, and to easily insert steps dynamically.
|
||||
*
|
||||
* The first step of the wizard, which has no predecessor. Will generally be
|
||||
* an overview/introductory page.
|
||||
*
|
||||
* @var MigrateUIStep
|
||||
*/
|
||||
protected $firstStep;
|
||||
|
||||
/**
|
||||
* The last step of the wizard, which has no successor. Will generally be a
|
||||
* review page.
|
||||
*
|
||||
* @var MigrateUIStep
|
||||
*/
|
||||
protected $lastStep;
|
||||
|
||||
/**
|
||||
* Get the list of steps currently defined.
|
||||
*
|
||||
* @return
|
||||
* An array of MigrateUIStep objects, in the order defined, keyed by the step
|
||||
* name.
|
||||
*/
|
||||
protected function getSteps() {
|
||||
$steps = array();
|
||||
|
||||
$steps[$this->firstStep->getName()] = $this->firstStep;
|
||||
$next_step = $this->firstStep->nextStep;
|
||||
while (!is_null($next_step)) {
|
||||
$steps[$next_step->getName()] = $next_step;
|
||||
|
||||
$next_step = $next_step->nextStep;
|
||||
}
|
||||
|
||||
return $steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current step of the wizard (the one being shown in the UI, and the one
|
||||
* whose button is being clicked on).
|
||||
*
|
||||
* @var MigrateUIStep
|
||||
*/
|
||||
protected $currentStep;
|
||||
|
||||
/**
|
||||
* The step number, used in the page title.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $stepNumber = 1;
|
||||
|
||||
/**
|
||||
* The group name to assign to any Migration instances created.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $groupName = 'default';
|
||||
|
||||
public function getGroupName() {
|
||||
return $this->groupName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user-visible title of the group.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $groupTitle = 'default';
|
||||
|
||||
/**
|
||||
* Any arguments that apply to all migrations in the group.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $groupArguments = array();
|
||||
|
||||
/**
|
||||
* Array of Migration argument arrays, keyed by machine name. On Finish, used
|
||||
* to register Migrations.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migrations = array();
|
||||
|
||||
/**
|
||||
* Array of MigrateUIWizardExtender objects that extend this wizard.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $extenders = array();
|
||||
|
||||
public function getExtender($extender_class) {
|
||||
if (isset($this->extenders[$extender_class])) {
|
||||
return $this->extenders[$extender_class];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translatable name representing the source of the data (e.g.,
|
||||
* "Drupal", "WordPress", etc.).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getSourceName();
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a wizard extender.
|
||||
*
|
||||
* This initializes the new extender and adds it to our internal list.
|
||||
*
|
||||
* @param $extender_class
|
||||
* The name of an extender class.
|
||||
*/
|
||||
public function addExtender($extender_class) {
|
||||
$steps = $this->getSteps();
|
||||
|
||||
$extender = new $extender_class($this, $steps);
|
||||
$this->extenders[$extender_class] = $extender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a step to the wizard, using a step name and method.
|
||||
*
|
||||
* @param string $name
|
||||
* Translatable name for the step, to be used in the page title.
|
||||
* @param callable $form_method
|
||||
* Callable returning the form array for the step. This can be either the
|
||||
* name of a MigrateUIWizard method, or a callable array specifying a method
|
||||
* on a wizard extender. The validation method is formed from the method's
|
||||
* name with the suffix 'Validate' added.
|
||||
* @param MigrateUIStep $after
|
||||
* Optional step after which to insert the new step. If omitted, add it at
|
||||
* the end.
|
||||
* @param mixed $context
|
||||
* Optional data to be used by this step's form.
|
||||
*
|
||||
* @return MigrateUIStep
|
||||
*/
|
||||
public function addStep($name, $form_method, MigrateUIStep $after = NULL, $context = NULL) {
|
||||
if (!is_array($form_method)) {
|
||||
$form_method = array($this, $form_method);
|
||||
}
|
||||
|
||||
$new_step = new MigrateUIStep($name, $form_method, $context);
|
||||
|
||||
// There were no steps, so this is the only one.
|
||||
if (is_null($this->firstStep)) {
|
||||
$this->firstStep = $this->lastStep = $this->currentStep = $new_step;
|
||||
}
|
||||
else {
|
||||
// If no insertion point is specified, append to the end.
|
||||
if (is_null($after)) {
|
||||
$after = $this->lastStep;
|
||||
}
|
||||
// Do the insert, rewriting the links appropriately.
|
||||
$new_step->nextStep = $after->nextStep;
|
||||
|
||||
if (is_null($new_step->nextStep)) {
|
||||
$this->lastStep = $new_step;
|
||||
}
|
||||
else {
|
||||
$new_step->nextStep->previousStep = $new_step;
|
||||
}
|
||||
$new_step->previousStep = $after;
|
||||
$after->nextStep = $new_step;
|
||||
}
|
||||
return $new_step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the named step from the wizard.
|
||||
*
|
||||
* @param $name
|
||||
*/
|
||||
protected function removeStep($name) {
|
||||
for ($current_step = $this->firstStep; !is_null($current_step); $current_step = $current_step->nextStep) {
|
||||
if ($current_step->getName() == $name) {
|
||||
if (is_null($current_step->previousStep)) {
|
||||
$this->firstStep = $current_step->nextStep;
|
||||
}
|
||||
else {
|
||||
$current_step->previousStep->nextStep = $current_step->nextStep;
|
||||
}
|
||||
if (is_null($current_step->nextStep)) {
|
||||
$this->lastStep = $current_step->previousStep;
|
||||
}
|
||||
else {
|
||||
$current_step->nextStep->previousStep = $current_step->previousStep;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the wizard to the next step in line (if any), first squirreling away
|
||||
* the current step's form values.
|
||||
*/
|
||||
public function gotoNextStep(&$form_state) {
|
||||
if ($this->currentStep && $this->currentStep->nextStep) {
|
||||
$this->currentStep->setFormValues($form_state['values']);
|
||||
$form_state['rebuild'] = TRUE;
|
||||
$this->currentStep = $this->currentStep->nextStep;
|
||||
$this->stepNumber++;
|
||||
|
||||
// Ensure a page reload remains on the current step.
|
||||
$current_step_form_values = $this->currentStep->getFormValues();
|
||||
if (!empty($current_step_form_values)) {
|
||||
$form_state['values'] = $current_step_form_values;
|
||||
}
|
||||
else {
|
||||
$form_state['values'] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the wizard to the previous step in line (if any), restoring its
|
||||
* form values.
|
||||
*/
|
||||
public function gotoPreviousStep(&$form_state) {
|
||||
if ($this->currentStep && $this->currentStep->previousStep) {
|
||||
$this->currentStep = $this->currentStep->previousStep;
|
||||
$this->stepNumber--;
|
||||
$form_state['values'] = $this->currentStep->getFormValues();
|
||||
$form_state['rebuild'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the form for the current step.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function form(&$form_state) {
|
||||
drupal_set_title(t('Import from @source_title',
|
||||
array('@source_title' => $this->getSourceName())));
|
||||
|
||||
$form_method = $this->currentStep->getFormMethod();
|
||||
$form['title'] = array(
|
||||
'#prefix' => '<h2>',
|
||||
'#markup' => t('Step @step: @step_name',
|
||||
array(
|
||||
'@step' => $this->stepNumber,
|
||||
'@step_name' => $this->currentStep->getName(),
|
||||
)),
|
||||
'#suffix' => '</h2>',
|
||||
);
|
||||
|
||||
$form += call_user_func_array($form_method, array(&$form_state));
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
|
||||
// Show the 'previous' button if appropriate. Note that #submit is set to
|
||||
// a special submit handler, and that we use #limit_validation_errors to
|
||||
// skip all complaints about validation when using the back button. The
|
||||
// values entered will be discarded, but they will not be validated, which
|
||||
// would be annoying in a "back" button.
|
||||
if ($this->currentStep != $this->firstStep) {
|
||||
$form['actions']['prev'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Previous'),
|
||||
'#name' => 'prev',
|
||||
'#submit' => array('migrate_ui_wizard_previous_submit'),
|
||||
'#limit_validation_errors' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
// Show the Next button only if there are more steps defined.
|
||||
if ($this->currentStep == $this->lastStep) {
|
||||
$form['actions']['finish'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save import settings'),
|
||||
);
|
||||
|
||||
$form['actions']['migrate'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save import settings and run import'),
|
||||
'#submit' => array('migrate_ui_wizard_migrate_submit'),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['actions']['next'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Next'),
|
||||
'#name' => 'next',
|
||||
'#submit' => array('migrate_ui_wizard_next_submit'),
|
||||
'#validate' => array('migrate_ui_wizard_next_validate'),
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the validation function for the current form (which has the same
|
||||
* name of the form function with 'Validate' appended).
|
||||
*
|
||||
* @param array $form_state
|
||||
*/
|
||||
public function formValidate(&$form_state) {
|
||||
$validate_method = $this->currentStep->getFormMethod();
|
||||
|
||||
// This is an array for a method, or a function name.
|
||||
if (is_array($validate_method)) {
|
||||
$validate_method[1] .= 'Validate';
|
||||
}
|
||||
else {
|
||||
$validate_method .= 'Validate';
|
||||
}
|
||||
|
||||
if (is_callable($validate_method)) {
|
||||
call_user_func_array($validate_method, array(&$form_state));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the information we've accumulated throughout the wizard, and create
|
||||
* the Migrations to perform the import.
|
||||
*/
|
||||
public function formSaveSettings() {
|
||||
MigrateGroup::register($this->groupName, $this->groupTitle, $this->groupArguments);
|
||||
$info['arguments']['group_name'] = $this->groupName;
|
||||
foreach ($this->migrations as $machine_name => $info) {
|
||||
// Call the right registerMigration implementation. Note that this means
|
||||
// that classes that override registerMigration() must handle registration
|
||||
// themselves, they cannot leave it to us and expect their extension to be
|
||||
// called.
|
||||
if (is_subclass_of($info['class_name'], 'Migration')) {
|
||||
Migration::registerMigration($info['class_name'], $machine_name,
|
||||
$info['arguments']);
|
||||
}
|
||||
else {
|
||||
MigrationBase::registerMigration($info['class_name'], $machine_name,
|
||||
$info['arguments']);
|
||||
}
|
||||
};
|
||||
menu_rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the import process for the migration group we've defined.
|
||||
*/
|
||||
public function formPerformImport() {
|
||||
$migrations = migrate_migrations();
|
||||
$operations = array();
|
||||
/** @var Migration $migration */
|
||||
foreach ($migrations as $migration) {
|
||||
$group_name = $migration->getGroup()->getName();
|
||||
if ($group_name == $this->groupName) {
|
||||
$operations[] = array(
|
||||
'migrate_ui_batch',
|
||||
array('import', $migration->getMachineName(), NULL, 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($operations) > 0) {
|
||||
$batch = array(
|
||||
'operations' => $operations,
|
||||
'title' => t('Import processing'),
|
||||
'file' => drupal_get_path('module', 'migrate_ui') . '/migrate_ui.pages.inc',
|
||||
'init_message' => t('Starting import process'),
|
||||
'progress_message' => t(''),
|
||||
'error_message' => t('An error occurred. Some or all of the import processing has failed.'),
|
||||
'finished' => 'migrate_ui_batch_finish',
|
||||
);
|
||||
batch_set($batch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record all the information necessary to register a migration when this is
|
||||
* all over.
|
||||
*
|
||||
* @param string $machine_name
|
||||
* Machine name for the migration class.
|
||||
* @param string $class_name
|
||||
* Name of the Migration class to instantiate.
|
||||
* @param array $arguments
|
||||
* Further information configuring the migration.
|
||||
*/
|
||||
public function addMigration($machine_name, $class_name, $arguments) {
|
||||
// Give extenders an opportunity to modify or reject this migration.
|
||||
foreach ($this->extenders as $extender) {
|
||||
if (!$extender->addMigrationAlter($machine_name, $class_name, $arguments, $this)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
$machine_name = $this->groupName . $machine_name;
|
||||
if (isset($arguments['dependencies'])) {
|
||||
foreach ($arguments['dependencies'] as $index => $dependency) {
|
||||
$arguments['dependencies'][$index] = $this->groupName . $dependency;
|
||||
}
|
||||
}
|
||||
if (isset($arguments['soft_dependencies'])) {
|
||||
foreach ($arguments['soft_dependencies'] as $index => $dependency) {
|
||||
$arguments['soft_dependencies'][$index] = $this->groupName . $dependency;
|
||||
}
|
||||
}
|
||||
$arguments += array(
|
||||
'group_name' => $this->groupName,
|
||||
'machine_name' => $machine_name,
|
||||
);
|
||||
$this->migrations[$machine_name] = array(
|
||||
'class_name' => $class_name,
|
||||
'arguments' => $arguments,
|
||||
);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing one step of a wizard.
|
||||
*/
|
||||
class MigrateUIStep {
|
||||
|
||||
/**
|
||||
* A translatable string briefly describing this step, to be used in the page
|
||||
* title for the step form.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callable that returns the form array for this step.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $formMethod;
|
||||
|
||||
public function getFormMethod() {
|
||||
return $this->formMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* The form values ($form_state['values']) submitted for this step, saved in
|
||||
* case we need to restore them on a Previous action.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $formValues;
|
||||
|
||||
public function getFormValues() {
|
||||
return $this->formValues;
|
||||
}
|
||||
|
||||
public function setFormValues($form_values) {
|
||||
$this->formValues = $form_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any contextual data needed by the form for this step. For example, a
|
||||
* field mapping form would need to know the source and destination content
|
||||
* types so it can determine what fields to expose.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
public function getContext() {
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* The step object is a node in a doubly-linked list - it links to its
|
||||
* predecessor and successor steps.
|
||||
*
|
||||
* @var MigrateUIStep
|
||||
*/
|
||||
public $nextStep;
|
||||
|
||||
/**
|
||||
* @var MigrateUIStep
|
||||
*/
|
||||
public $previousStep;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param $name
|
||||
* The machine name of the wizard step.
|
||||
* @param $form_method
|
||||
* A callable for the form array for this step. The validation method is
|
||||
* formed from the method name with the suffix 'Validate' added, regardless
|
||||
* of which object it is on.
|
||||
* @param $context = NULL
|
||||
* Contextual data needed by the form for this step.
|
||||
*/
|
||||
public function __construct($name, $form_method, $context = NULL) {
|
||||
$this->name = $name;
|
||||
$this->formMethod = $form_method;
|
||||
$this->context = $context;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
abstract class MigrateUIWizardExtender {
|
||||
|
||||
/**
|
||||
* Reference to the wizard object that this extender applies to.
|
||||
*/
|
||||
protected $wizard;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* Wizard extenders should override this to add their steps to the wizard.
|
||||
*/
|
||||
public function __construct(MigrateUIWizard $wizard, array $wizard_steps) {
|
||||
$this->wizard = $wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the arguments to a migration before it is registered, or potentially
|
||||
* reject it.
|
||||
*
|
||||
* @param string $machine_name
|
||||
* Machine name for the migration class.
|
||||
* @param string $class_name
|
||||
* Name of the Migration class to instantiate.
|
||||
* @param array $arguments
|
||||
* Further information configuring the migration.
|
||||
* @param MigrateUIWizard $wizard
|
||||
* The wizard class performing the registration.
|
||||
*
|
||||
* @return bool
|
||||
* Return FALSE to prevent registration of this migration.
|
||||
*/
|
||||
public function addMigrationAlter($machine_name, $class_name, &$arguments, $wizard) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Support for custom block destinations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Destination class implementing migration into {block_custom}.
|
||||
*/
|
||||
class MigrateDestinationCustomBlock extends MigrateDestination {
|
||||
|
||||
static public function getKeySchema() {
|
||||
return array(
|
||||
'bid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'description' => 'ID of destination custom block',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
$output = t('Custom blocks');
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fields available to be mapped for custom blocks.
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
*/
|
||||
public function fields($migration = NULL) {
|
||||
$fields = array(
|
||||
'bid' => t('The custom block ID (bid).'),
|
||||
'body' => t('Block contents.'),
|
||||
'info' => t('Block description.'),
|
||||
'format' => t('The {filter_format}.format of the block body.'),
|
||||
);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single row.
|
||||
*
|
||||
* @param $block
|
||||
* Custom block object to build. Prefilled with any fields mapped in the
|
||||
* Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields of the object that was saved if
|
||||
* successful. FALSE on failure.
|
||||
*/
|
||||
public function import(stdClass $block, stdClass $row) {
|
||||
// Updating previously-migrated content
|
||||
if (isset($row->migrate_map_destid1)) {
|
||||
$block->bid = $row->migrate_map_destid1;
|
||||
}
|
||||
|
||||
// Load old values if necessary.
|
||||
$migration = Migration::currentMigration();
|
||||
if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
|
||||
if (!isset($block->bid)) {
|
||||
throw new MigrateException(t('System-of-record is DESTINATION, but no destination bid provided'));
|
||||
}
|
||||
if (!$old_block = $this->loadCustomBlock($block->bid)) {
|
||||
throw new MigrateException(t('System-of-record is DESTINATION, and the provided bid could not be found'));
|
||||
}
|
||||
$block_to_update = (object) $old_block;
|
||||
foreach ($old_block as $key => $value) {
|
||||
if (!isset($block->{$key})) {
|
||||
$block->{$key} = $old_block[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke migration prepare handlers
|
||||
$this->prepare($block, $row);
|
||||
|
||||
// Custom blocks are handled as arrays, so clone the object to an array.
|
||||
$item = clone $block;
|
||||
$item = (array) $item;
|
||||
|
||||
migrate_instrument_start('block_custom_save');
|
||||
|
||||
// Check to see if this is a new custom block.
|
||||
$update = FALSE;
|
||||
if (isset($item['bid'])) {
|
||||
$update = TRUE;
|
||||
$bid = $this->saveCustomBlock($item);
|
||||
}
|
||||
else {
|
||||
$bid = $this->saveCustomBlock($item);
|
||||
}
|
||||
migrate_instrument_stop('block_custom_save');
|
||||
|
||||
// Return the new id or FALSE on failure.
|
||||
if (!empty($bid)) {
|
||||
// Increment the count if the save succeeded.
|
||||
if ($update) {
|
||||
$this->numUpdated++;
|
||||
}
|
||||
else {
|
||||
$this->numCreated++;
|
||||
}
|
||||
// Return the primary key to the mapping table.
|
||||
$return = array($bid);
|
||||
}
|
||||
else {
|
||||
$return = FALSE;
|
||||
}
|
||||
|
||||
// Invoke migration complete handlers.
|
||||
$block = (object) $this->loadCustomBlock($bid);
|
||||
$this->complete($block, $row);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateDestination::prepare().
|
||||
*/
|
||||
public function prepare($block, stdClass $row) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
$block->migrate = array(
|
||||
'machineName' => $migration->getMachineName(),
|
||||
);
|
||||
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('block_custom', 'prepare', $block, $row);
|
||||
// Then call any prepare handler for this specific Migration.
|
||||
if (method_exists($migration, 'prepare')) {
|
||||
$migration->prepare($block, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateDestination::complete().
|
||||
*/
|
||||
public function complete($block, stdClass $row) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
$block->migrate = array(
|
||||
'machineName' => $migration->getMachineName(),
|
||||
);
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('block_custom', 'complete', $block, $row);
|
||||
// Then call any complete handler for this specific Migration.
|
||||
if (method_exists($migration, 'complete')) {
|
||||
$migration->complete($block, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a batch of custom blocks at once.
|
||||
*
|
||||
* @param $bids
|
||||
* Array of custom block IDs to be deleted.
|
||||
*/
|
||||
public function bulkRollback(array $bids) {
|
||||
migrate_instrument_start('block_custom_delete_multiple');
|
||||
$this->prepareRollback($bids);
|
||||
$this->deleteMultipleCustomBlocks($bids);
|
||||
$this->completeRollback($bids);
|
||||
migrate_instrument_stop('block_custom_delete_multiple');
|
||||
}
|
||||
|
||||
/**
|
||||
* Give handlers a shot at cleaning up before a block has been rolled back.
|
||||
*
|
||||
* @param $bid
|
||||
* ID of the custom block about to be deleted.
|
||||
*/
|
||||
public function prepareRollback($bid) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('block_custom', 'prepareRollback', $bid);
|
||||
// Then call any complete handler for this specific Migration.
|
||||
if (method_exists($migration, 'prepareRollback')) {
|
||||
$migration->prepareRollback($bid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Give handlers a shot at cleaning up after a block has been rolled back.
|
||||
*
|
||||
* @param $bid
|
||||
* ID of the custom block which has been deleted.
|
||||
*/
|
||||
public function completeRollback($bid) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('block_custom', 'completeRollback', $bid);
|
||||
// Then call any complete handler for this specific Migration.
|
||||
if (method_exists($migration, 'completeRollback')) {
|
||||
$migration->completeRollback($bid);
|
||||
}
|
||||
}
|
||||
|
||||
public function loadCustomBlock($bid) {
|
||||
return block_custom_block_get($bid);
|
||||
}
|
||||
|
||||
public function saveCustomBlock($block) {
|
||||
drupal_alter('block_custom', $block);
|
||||
if (!empty($block['bid'])) {
|
||||
drupal_write_record('block_custom', $block, array('bid'));
|
||||
module_invoke_all('block_custom_update', $block);
|
||||
}
|
||||
else {
|
||||
drupal_write_record('block_custom', $block);
|
||||
module_invoke_all('block_custom_insert', $block);
|
||||
}
|
||||
return $block['bid'];
|
||||
}
|
||||
|
||||
public function deleteCustomBlock($bid) {
|
||||
$this->deleteMultipleCustomBlocks(array($bid));
|
||||
}
|
||||
|
||||
public function deleteMultipleCustomBlocks(array $bids) {
|
||||
db_delete('block_custom')->condition('bid', $bids, 'IN')->execute();
|
||||
db_delete('block')
|
||||
->condition('module', 'block')
|
||||
->condition('delta', $bids, 'IN')
|
||||
->execute();
|
||||
db_delete('block_role')
|
||||
->condition('module', 'block')
|
||||
->condition('delta', $bids, 'IN')
|
||||
->execute();
|
||||
db_delete('block_node_type')
|
||||
->condition('module', 'block')
|
||||
->condition('delta', $bids, 'IN')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
* Destination class implementing migration into comments.
|
||||
*/
|
||||
class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
|
||||
static public function getKeySchema() {
|
||||
return array(
|
||||
'cid' => array(
|
||||
@@ -24,6 +25,7 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
|
||||
/**
|
||||
* Save the original setting of comment_maintain_node_statistics
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $maintainNodeStatistics;
|
||||
@@ -58,6 +60,7 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -65,34 +68,34 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
public function fields($migration = NULL) {
|
||||
$fields = array();
|
||||
// First the core (comment table) properties
|
||||
$fields['cid'] = t('Comment: <a href="@doc">Existing comment ID</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#cid'));
|
||||
$fields['nid'] = t('Comment: <a href="@doc">Node (by Drupal ID)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#nid'));
|
||||
$fields['uid'] = t('Comment: <a href="@doc">User (by Drupal ID)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#uid'));
|
||||
$fields['pid'] = t('Comment: <a href="@doc">Parent (by Drupal ID)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#pid'));
|
||||
$fields['subject'] = t('Comment: <a href="@doc">Subject</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#subject'));
|
||||
$fields['created'] = t('Comment: <a href="@doc">Created timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#created'));
|
||||
$fields['changed'] = t('Comment: <a href="@doc">Modified timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#changed'));
|
||||
$fields['status'] = t('Comment: <a href="@doc">Status</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#status'));
|
||||
$fields['hostname'] = t('Comment: <a href="@doc">Hostname/IP address</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#hostname'));
|
||||
$fields['name'] = t('Comment: <a href="@doc">User name (not username)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#name'));
|
||||
$fields['mail'] = t('Comment: <a href="@doc">Email address</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#mail'));
|
||||
$fields['homepage'] = t('Comment: <a href="@doc">Homepage</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#homepage'));
|
||||
$fields['language'] = t('Comment: <a href="@doc">Language</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#language'));
|
||||
$fields['thread'] = t('Comment: <a href="@doc">Thread</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#thread'));
|
||||
$fields['cid'] = t('<a href="@doc">Existing comment ID</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#cid'));
|
||||
$fields['nid'] = t('<a href="@doc">Node (by Drupal ID)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#nid'));
|
||||
$fields['uid'] = t('<a href="@doc">User (by Drupal ID)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#uid'));
|
||||
$fields['pid'] = t('<a href="@doc">Parent (by Drupal ID)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#pid'));
|
||||
$fields['subject'] = t('<a href="@doc">Subject</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#subject'));
|
||||
$fields['created'] = t('<a href="@doc">Created timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#created'));
|
||||
$fields['changed'] = t('<a href="@doc">Modified timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#changed'));
|
||||
$fields['status'] = t('<a href="@doc">Status</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#status'));
|
||||
$fields['hostname'] = t('<a href="@doc">Hostname/IP address</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#hostname'));
|
||||
$fields['name'] = t('<a href="@doc">User name (not username)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#name'));
|
||||
$fields['mail'] = t('<a href="@doc">Email address</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#mail'));
|
||||
$fields['homepage'] = t('<a href="@doc">Homepage</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#homepage'));
|
||||
$fields['language'] = t('<a href="@doc">Language</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#language'));
|
||||
$fields['thread'] = t('<a href="@doc">Thread</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349714#thread'));
|
||||
|
||||
// Then add in anything provided by handlers
|
||||
$fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType, $this->bundle, $migration);
|
||||
@@ -120,12 +123,14 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
* Import a single comment.
|
||||
*
|
||||
* @param $comment
|
||||
* Comment object to build. Prefilled with any fields mapped in the Migration.
|
||||
* Comment object to build. Prefilled with any fields mapped in the
|
||||
* Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields (cid only in this case) of the comment that was saved if
|
||||
* successful. FALSE on failure.
|
||||
* Array of key fields (cid only in this case) of the comment that was saved
|
||||
* if successful. FALSE on failure.
|
||||
*/
|
||||
public function import(stdClass $comment, stdClass $row) {
|
||||
$migration = Migration::currentMigration();
|
||||
@@ -134,7 +139,10 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
if (isset($comment->cid)) {
|
||||
if ($comment->cid != $row->migrate_map_destid1) {
|
||||
throw new MigrateException(t("Incoming cid !cid and map destination nid !destid1 don't match",
|
||||
array('!cid' => $comment->cid, '!destid1' => $row->migrate_map_destid1)));
|
||||
array(
|
||||
'!cid' => $comment->cid,
|
||||
'!destid1' => $row->migrate_map_destid1,
|
||||
)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -149,6 +157,10 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
$comment->changed = MigrationBase::timestamp($comment->changed);
|
||||
}
|
||||
|
||||
if (!isset($comment->node_type)) {
|
||||
$comment->node_type = $this->bundle;
|
||||
}
|
||||
|
||||
if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
|
||||
if (!isset($comment->cid)) {
|
||||
throw new MigrateException(t('System-of-record is DESTINATION, but no destination cid provided'));
|
||||
@@ -170,7 +182,7 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
}
|
||||
$this->prepare($comment, $row);
|
||||
foreach ($rawcomment as $field => $value) {
|
||||
$old_comment->$field = $comment->$field;
|
||||
$old_comment->{$field} = $comment->{$field};
|
||||
}
|
||||
$comment = $old_comment;
|
||||
}
|
||||
@@ -186,8 +198,8 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
'pid' => 0,
|
||||
);
|
||||
foreach ($defaults as $field => $value) {
|
||||
if (!isset($comment->$field)) {
|
||||
$comment->$field = $value;
|
||||
if (!isset($comment->{$field})) {
|
||||
$comment->{$field} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,6 +222,10 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
else {
|
||||
$updating = FALSE;
|
||||
}
|
||||
|
||||
// Validate field data prior to saving.
|
||||
MigrateDestinationEntity::fieldAttachValidate('comment', $comment);
|
||||
|
||||
migrate_instrument_start('comment_save');
|
||||
comment_save($comment);
|
||||
migrate_instrument_stop('comment_save');
|
||||
@@ -265,8 +281,8 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
|
||||
/**
|
||||
* Updating node statistics on every comment imported or rolled back is
|
||||
* expensive. We disable node statistics while performing imports and rollbacks,
|
||||
* then re-enable and compute them in bulk when done.
|
||||
* expensive. We disable node statistics while performing imports and
|
||||
* rollbacks, then re-enable and compute them in bulk when done.
|
||||
*/
|
||||
protected function disableStatistics() {
|
||||
// If maintaining node statistics is enabled, temporarily disable it
|
||||
@@ -291,16 +307,47 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
// Empty table
|
||||
db_truncate('node_comment_statistics')->execute();
|
||||
|
||||
// TODO: DBTNG. Ignore keyword is Mysql only? Is only used in the rare case when
|
||||
// two comments on the same node share same timestamp.
|
||||
$sql = "
|
||||
INSERT IGNORE INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (
|
||||
SELECT c.nid, c.cid, c.created, c.name, c.uid, c2.comment_count FROM {comment} c
|
||||
JOIN (
|
||||
SELECT c.nid, MAX(c.created) AS created, COUNT(*) AS comment_count FROM {comment} c WHERE status=:published GROUP BY c.nid
|
||||
) AS c2 ON c.nid = c2.nid AND c.created=c2.created
|
||||
)";
|
||||
db_query($sql, array(':published' => COMMENT_PUBLISHED));
|
||||
// DBTNG. IGNORE keyword is not compatible with Postgres. SQLite?
|
||||
switch (db_driver()) {
|
||||
case 'pgsql':
|
||||
// We still want to run this under Postgres. On the very rare occasion
|
||||
// when we have 2 comments on the same node with the same timestamp
|
||||
// we will lose data.
|
||||
$sql = "
|
||||
INSERT INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (
|
||||
SELECT c.nid, c.cid, c.created, c.name, c.uid, c2.comment_count
|
||||
FROM {comment} c
|
||||
JOIN (
|
||||
SELECT c.nid, MAX(c.created) AS created, COUNT(*) AS comment_count
|
||||
FROM {comment} c
|
||||
WHERE status=:published
|
||||
GROUP BY c.nid
|
||||
) AS c2 ON c.nid = c2.nid AND c.created=c2.created
|
||||
)";
|
||||
break;
|
||||
|
||||
default:
|
||||
$sql = "
|
||||
INSERT IGNORE INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (
|
||||
SELECT c.nid, c.cid, c.created, c.name, c.uid, c2.comment_count
|
||||
FROM {comment} c
|
||||
JOIN (
|
||||
SELECT c.nid, MAX(c.created) AS created, COUNT(*) AS comment_count
|
||||
FROM {comment} c
|
||||
WHERE status=:published
|
||||
GROUP BY c.nid
|
||||
) AS c2 ON c.nid = c2.nid AND c.created=c2.created
|
||||
)";
|
||||
}
|
||||
try {
|
||||
db_query($sql, array(':published' => COMMENT_PUBLISHED));
|
||||
} catch (Exception $e) {
|
||||
// Our edge case has been hit. A Postgres migration has likely just
|
||||
// lost data. Let the user know.
|
||||
Migration::displayMessage(t('Failed to update node comment statistics: !message',
|
||||
array('!message' => $e->getMessage())
|
||||
));
|
||||
}
|
||||
|
||||
// Insert records into the node_comment_statistics for nodes that are missing.
|
||||
$query = db_select('node', 'n');
|
||||
@@ -320,11 +367,15 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
|
||||
}
|
||||
|
||||
class MigrateCommentNodeHandler extends MigrateDestinationHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('node'));
|
||||
}
|
||||
|
||||
public function fields($entity_type, $bundle) {
|
||||
/**
|
||||
* Implementation of MigrateDestinationHandler::fields().
|
||||
*/
|
||||
public function fields($entity_type, $bundle, $migration = NULL) {
|
||||
$fields = array();
|
||||
$fields['comment'] = t('Whether comments may be posted to the node');
|
||||
return $fields;
|
||||
|
||||
@@ -10,12 +10,14 @@
|
||||
* Field API-related functions.
|
||||
*/
|
||||
abstract class MigrateDestinationEntity extends MigrateDestination {
|
||||
|
||||
/**
|
||||
* The entity type (node, user, taxonomy_term, etc.) of the destination.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
public function getEntityType() {
|
||||
return $this->entityType;
|
||||
}
|
||||
@@ -26,6 +28,7 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
public function getBundle() {
|
||||
return $this->bundle;
|
||||
}
|
||||
@@ -36,6 +39,7 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
|
||||
* @var string
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
public function getLanguage() {
|
||||
return $this->language;
|
||||
}
|
||||
@@ -46,6 +50,7 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
|
||||
* @var int
|
||||
*/
|
||||
protected $textFormat;
|
||||
|
||||
public function getTextFormat() {
|
||||
return $this->textFormat;
|
||||
}
|
||||
@@ -125,12 +130,12 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
|
||||
// Add source keys for debugging and identification of migrated data by hooks.
|
||||
/* TODO: Restore
|
||||
foreach ($migration->sourceKeyMap() as $field_name => $key_name) {
|
||||
$keys[$key_name] = $source_row->$field_name;
|
||||
$keys[$key_name] = $source_row->{$field_name};
|
||||
}
|
||||
*/
|
||||
$migration = Migration::currentMigration();
|
||||
$entity->migrate = array(
|
||||
// 'source_keys' => $keys,
|
||||
// 'source_keys' => $keys,
|
||||
'machineName' => $migration->getMachineName(),
|
||||
);
|
||||
|
||||
@@ -138,6 +143,11 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
|
||||
migrate_handler_invoke_all('Entity', 'prepare', $entity, $source_row);
|
||||
// Then call any entity-specific handlers
|
||||
migrate_handler_invoke_all($this->entityType, 'prepare', $entity, $source_row);
|
||||
|
||||
// Apply defaults, removing empty fields from the entity object.
|
||||
$form = $form_state = array();
|
||||
_field_invoke_default('submit', $this->entityType, $entity, $form, $form_state);
|
||||
|
||||
// Then call any prepare handler for this specific Migration.
|
||||
if (method_exists($migration, 'prepare')) {
|
||||
$migration->prepare($entity, $source_row);
|
||||
@@ -163,12 +173,38 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
|
||||
if (method_exists($migration, 'complete')) {
|
||||
try {
|
||||
$migration->complete($entity, $source_row);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
// If we catch any errors here, save the messages without letting
|
||||
// the exception prevent the saving of the entity being recorded.
|
||||
$migration->saveMessage($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform field validation against the field data in an entity. Wraps
|
||||
* field_attach_validate to handle exceptions cleanly and provide maximum
|
||||
* information for identifying the cause of validation errors.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of $entity; e.g. 'node' or 'user'.
|
||||
* @param $entity
|
||||
* The entity with fields to validate.
|
||||
*/
|
||||
static public function fieldAttachValidate($entity_type, $entity) {
|
||||
try {
|
||||
field_attach_validate($entity_type, $entity);
|
||||
} catch (FieldValidationException $e) {
|
||||
$migration = Migration::currentMigration();
|
||||
foreach ($e->errors as $field_name => $error_list) {
|
||||
if (is_array($error_list)) {
|
||||
foreach ($error_list as $index => $error) {
|
||||
$message = $error['message'];
|
||||
$migration->saveMessage(t('Field validation error for !field_name: !message',
|
||||
array('!field_name' => $field_name, '!message' => $message)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,13 @@
|
||||
*/
|
||||
|
||||
class MigrateFieldsEntityHandler extends MigrateDestinationHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('entity'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateDestinationHandler::fields().
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type (node, user, etc.) for which to list fields.
|
||||
* @param $bundle
|
||||
* The bundle (article, blog, etc.), if any, for which to list fields.
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration providing the context.
|
||||
* @return array
|
||||
* An array keyed by field name, with field descriptions as values.
|
||||
*/
|
||||
public function fields($entity_type, $bundle, $migration = NULL) {
|
||||
$fields = array();
|
||||
@@ -29,22 +21,39 @@ class MigrateFieldsEntityHandler extends MigrateDestinationHandler {
|
||||
$field_info = field_info_field($machine_name);
|
||||
$type = $field_info['type'];
|
||||
|
||||
$fields[$machine_name] = t('Field:') . ' ' . $instance['label'] .
|
||||
' (' . $field_info['type'] . ')';
|
||||
if (user_access(MIGRATE_ACCESS_ADVANCED)) {
|
||||
$fields[$machine_name] = $instance['label'] . ' (' . $field_info['type'] . ')';
|
||||
}
|
||||
else {
|
||||
$fields[$machine_name] = $instance['label'];
|
||||
}
|
||||
|
||||
// Look for subfields
|
||||
$class_list = _migrate_class_list('MigrateFieldHandler');
|
||||
$disabled = unserialize(variable_get('migrate_disabled_handlers', serialize(array())));
|
||||
$fields_found = FALSE;
|
||||
foreach ($class_list as $class_name => $handler) {
|
||||
if (!in_array($class_name, $disabled) && $handler->handlesType($type)
|
||||
&& method_exists($handler, 'fields')) {
|
||||
&& method_exists($handler, 'fields')) {
|
||||
migrate_instrument_start($class_name . '->fields');
|
||||
$subfields = call_user_func(array($handler, 'fields'), $type,
|
||||
$instance, $migration);
|
||||
$instance, $migration);
|
||||
migrate_instrument_stop($class_name . '->fields');
|
||||
foreach ($subfields as $subfield_name => $subfield_label) {
|
||||
$fields[$machine_name . ':' . $subfield_name] = $subfield_label;
|
||||
}
|
||||
$fields_found = TRUE;
|
||||
}
|
||||
}
|
||||
if (!$fields_found) {
|
||||
// Check the default field handler last.
|
||||
migrate_instrument_start('MigrateDefaultFieldHandler->fields');
|
||||
$subfields = call_user_func(
|
||||
array(new MigrateDefaultFieldHandler, 'fields'), $type, $instance,
|
||||
$migration);
|
||||
migrate_instrument_stop('MigrateDefaultFieldHandler->fields');
|
||||
foreach ($subfields as $subfield_name => $subfield_label) {
|
||||
$fields[$machine_name . ':' . $subfield_name] = $subfield_label;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,12 +72,12 @@ class MigrateFieldsEntityHandler extends MigrateDestinationHandler {
|
||||
foreach ($instances as $machine_name => $instance) {
|
||||
if (property_exists($entity, $machine_name)) {
|
||||
// Normalize to an array
|
||||
if (!is_array($entity->$machine_name)) {
|
||||
$entity->$machine_name = array($entity->$machine_name);
|
||||
if (!is_array($entity->{$machine_name})) {
|
||||
$entity->{$machine_name} = array($entity->{$machine_name});
|
||||
}
|
||||
$field_info = field_info_field($machine_name);
|
||||
$entity->$machine_name = migrate_field_handler_invoke_all($entity, $field_info,
|
||||
$instance, $entity->$machine_name);
|
||||
$entity->{$machine_name} = migrate_field_handler_invoke_all($entity, $field_info,
|
||||
$instance, $entity->{$machine_name});
|
||||
}
|
||||
}
|
||||
migrate_instrument_stop('MigrateDestinationEntity->prepareFields');
|
||||
@@ -85,12 +94,12 @@ class MigrateFieldsEntityHandler extends MigrateDestinationHandler {
|
||||
foreach ($instances as $machine_name => $instance) {
|
||||
if (property_exists($entity, $machine_name)) {
|
||||
// Normalize to an array
|
||||
if (!is_array($entity->$machine_name)) {
|
||||
$entity->$machine_name = array($entity->$machine_name);
|
||||
if (!is_array($entity->{$machine_name})) {
|
||||
$entity->{$machine_name} = array($entity->{$machine_name});
|
||||
}
|
||||
$field_info = field_info_field($machine_name);
|
||||
migrate_field_handler_invoke_all($entity, $field_info,
|
||||
$instance, $entity->$machine_name, 'complete');
|
||||
$instance, $entity->{$machine_name}, 'complete');
|
||||
}
|
||||
}
|
||||
migrate_instrument_stop('MigrateDestinationEntity->completeFields');
|
||||
@@ -98,6 +107,7 @@ class MigrateFieldsEntityHandler extends MigrateDestinationHandler {
|
||||
}
|
||||
|
||||
abstract class MigrateFieldHandler extends MigrateHandler {
|
||||
|
||||
// Derived classes are expected to implement one or both of the prepare/complete
|
||||
// handlers.
|
||||
|
||||
@@ -110,12 +120,14 @@ abstract class MigrateFieldHandler extends MigrateHandler {
|
||||
* @param $entity
|
||||
* @param $field_info
|
||||
* @param $arguments
|
||||
*
|
||||
* @return string language code
|
||||
*/
|
||||
function getFieldLanguage($entity, $field_info, array $arguments) {
|
||||
$migration = Migration::currentMigration();
|
||||
switch (TRUE) {
|
||||
case !field_is_translatable($migration->getDestination()->getEntityType(), $field_info):
|
||||
case !field_is_translatable($migration->getDestination()
|
||||
->getEntityType(), $field_info):
|
||||
return LANGUAGE_NONE;
|
||||
case isset($arguments['language']):
|
||||
return $arguments['language'];
|
||||
@@ -128,11 +140,103 @@ abstract class MigrateFieldHandler extends MigrateHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fallback field handler to do basic copying of field data.
|
||||
*/
|
||||
class MigrateDefaultFieldHandler extends MigrateFieldHandler {
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements MigrateFieldHandler::fields().
|
||||
*
|
||||
* @param $field_type
|
||||
* @param $field_instance
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields($field_type, $field_instance) {
|
||||
$field_info = field_info_field($field_instance['field_name']);
|
||||
$fields = array();
|
||||
$first = TRUE;
|
||||
foreach ($field_info['columns'] as $column_name => $column_info) {
|
||||
// The first column is the primary value, which is mapped directly to
|
||||
// the field name - so, don't include it here among the subfields.
|
||||
if ($first) {
|
||||
$first = FALSE;
|
||||
}
|
||||
else {
|
||||
$fields[$column_name] = empty($column_info['description']) ?
|
||||
$column_name : $column_info['description'];
|
||||
}
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements MigrateFieldHandler::prepare().
|
||||
*
|
||||
* @param $entity
|
||||
* @param array $field_info
|
||||
* @param array $instance
|
||||
* @param array $values
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function prepare($entity, array $field_info, array $instance,
|
||||
array $values) {
|
||||
$arguments = array();
|
||||
if (isset($values['arguments'])) {
|
||||
$arguments = array_filter($values['arguments']);
|
||||
unset($values['arguments']);
|
||||
}
|
||||
$language = $this->getFieldLanguage($entity, $field_info, $arguments);
|
||||
|
||||
// Get the name of the primary (first) column, which is mapped separately.
|
||||
reset($field_info['columns']);
|
||||
$primary_column = key($field_info['columns']);
|
||||
|
||||
// Setup the standard Field API array for saving.
|
||||
$delta = 0;
|
||||
foreach ($values as $value) {
|
||||
// Handle multivalue arguments (especially for subfields).
|
||||
$delta_arguments = array();
|
||||
foreach ($arguments as $name => $argument) {
|
||||
if (is_array($argument) && array_key_exists($delta, $argument)) {
|
||||
$delta_arguments[$name] = $argument[$delta];
|
||||
}
|
||||
else {
|
||||
$delta_arguments[$name] = $argument;
|
||||
}
|
||||
}
|
||||
$return[$language][$delta] = array($primary_column => $value) +
|
||||
array_intersect_key($delta_arguments, $field_info['columns']);
|
||||
$delta++;
|
||||
}
|
||||
|
||||
return isset($return) ? $return : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides MigrateHandler::handlesType().
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handlesType($type) {
|
||||
// We claim to handle any type.
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for creating field handlers for fields with a single value.
|
||||
*
|
||||
* To use this class just extend it and pass key where the field's value should
|
||||
* be stored to the constructor, then register the type(s):
|
||||
*
|
||||
* @code
|
||||
* class MigrateLinkFieldHandler extends MigrateSimpleFieldHandler {
|
||||
* public function __construct() {
|
||||
@@ -143,6 +247,7 @@ abstract class MigrateFieldHandler extends MigrateHandler {
|
||||
* @endcode
|
||||
*/
|
||||
abstract class MigrateSimpleFieldHandler extends MigrateFieldHandler {
|
||||
|
||||
protected $fieldValueKey = 'value';
|
||||
|
||||
protected $skipEmpty = FALSE;
|
||||
@@ -197,6 +302,7 @@ abstract class MigrateSimpleFieldHandler extends MigrateFieldHandler {
|
||||
* Returns TRUE only for values which are not NULL.
|
||||
*
|
||||
* @param $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function notNull($value) {
|
||||
@@ -205,6 +311,7 @@ abstract class MigrateSimpleFieldHandler extends MigrateFieldHandler {
|
||||
}
|
||||
|
||||
class MigrateTextFieldHandler extends MigrateFieldHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('text', 'text_long', 'text_with_summary'));
|
||||
}
|
||||
@@ -233,20 +340,28 @@ class MigrateTextFieldHandler extends MigrateFieldHandler {
|
||||
* @param Migration $migration
|
||||
* The migration context for the parent field. We can look at the mappings
|
||||
* and determine which subfields are relevant.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields($type, $instance, $migration = NULL) {
|
||||
$fields = array();
|
||||
if ($type == 'text_with_summary') {
|
||||
$fields['summary'] = t('Subfield: Summary of field contents');
|
||||
$fields['summary'] = t('Subfield: <a href="@doc">Summary of field contents</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#summary'));
|
||||
}
|
||||
if ($instance['settings']['text_processing']) {
|
||||
$fields['format'] = t('Subfield: Text format for the field');
|
||||
$fields['format'] = t('Subfield: <a href="@doc">Text format for the field</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#format'));
|
||||
}
|
||||
$field = field_info_field($instance['field_name']);
|
||||
if (field_is_translatable($instance['entity_type'], $field)) {
|
||||
$fields['language'] = t('Subfield: <a href="@doc">Language for the field</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#language'));
|
||||
}
|
||||
$fields['language'] = t('Subfield: Language for the field');
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
public function prepare($entity, array $field_info, array $instance, array $values) {
|
||||
if (isset($values['arguments'])) {
|
||||
$arguments = $values['arguments'];
|
||||
@@ -278,7 +393,7 @@ class MigrateTextFieldHandler extends MigrateFieldHandler {
|
||||
if (is_array($arguments['format'])) {
|
||||
$format = $arguments['format'][$delta];
|
||||
}
|
||||
else{
|
||||
else {
|
||||
$format = $arguments['format'];
|
||||
}
|
||||
}
|
||||
@@ -289,6 +404,11 @@ class MigrateTextFieldHandler extends MigrateFieldHandler {
|
||||
$format = NULL;
|
||||
}
|
||||
$item['format'] = $item['value_format'] = $format;
|
||||
// If the value is an array, which it might be if the destination entity
|
||||
// was loaded via entity_load(), ensure we're handling a string.
|
||||
if (is_array($value) && !empty($value[0]) && array_key_exists('value', $value[0])) {
|
||||
$value = $value[0]['value'];
|
||||
}
|
||||
// Make sure the value will fit
|
||||
if ($max_length) {
|
||||
$item['value'] = drupal_substr($value, 0, $max_length);
|
||||
@@ -297,8 +417,11 @@ class MigrateTextFieldHandler extends MigrateFieldHandler {
|
||||
if ($value_length > $max_length) {
|
||||
$migration->saveMessage(
|
||||
t('Value for field !field exceeds max length of !max_length, actual length is !length',
|
||||
array('!field' => $instance['field_name'], '!max_length' => $max_length,
|
||||
'!length' => $value_length)),
|
||||
array(
|
||||
'!field' => $instance['field_name'],
|
||||
'!max_length' => $max_length,
|
||||
'!length' => $value_length,
|
||||
)),
|
||||
Migration::MESSAGE_INFORMATIONAL);
|
||||
}
|
||||
}
|
||||
@@ -322,17 +445,28 @@ class MigrateTextFieldHandler extends MigrateFieldHandler {
|
||||
}
|
||||
|
||||
class MigrateValueFieldHandler extends MigrateSimpleFieldHandler {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(array(
|
||||
'value_key' => 'value',
|
||||
'skip_empty' => FALSE,
|
||||
));
|
||||
$this->registerTypes(array('value', 'list', 'list_boolean', 'list_integer',
|
||||
'list_float', 'list_text', 'number_integer', 'number_decimal', 'number_float'));
|
||||
$this->registerTypes(array(
|
||||
'value',
|
||||
'list',
|
||||
'list_boolean',
|
||||
'list_integer',
|
||||
'list_float',
|
||||
'list_text',
|
||||
'number_integer',
|
||||
'number_decimal',
|
||||
'number_float',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('taxonomy_term_reference'));
|
||||
}
|
||||
@@ -347,13 +481,17 @@ class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
|
||||
* @param Migration $migration
|
||||
* The migration context for the parent field. We can look at the mappings
|
||||
* and determine which subfields are relevant.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields($type, $instance, $migration = NULL) {
|
||||
return array(
|
||||
'source_type' => t('Option: Set to \'tid\' when the value is a source ID'),
|
||||
'create_term' => t('Option: Set to TRUE to create referenced terms when necessary'),
|
||||
'ignore_case' => t('Option: Set to TRUE to ignore case differences between source data and existing term names'),
|
||||
'source_type' => t('Option: <a href="@doc">Set to \'tid\' when the value is a source ID</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#source_type')),
|
||||
'create_term' => t('Option: <a href="@doc">Set to TRUE to create referenced terms when necessary</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#create_term')),
|
||||
'ignore_case' => t('Option: <a href="@doc">Set to TRUE to ignore case differences between source data and existing term names</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#ignore_case')),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -365,7 +503,7 @@ class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
|
||||
else {
|
||||
$arguments = array();
|
||||
}
|
||||
if (empty($values[0])) {
|
||||
if (count($values) == 1 && empty($values[0])) {
|
||||
$values = array();
|
||||
}
|
||||
|
||||
@@ -375,6 +513,7 @@ class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
|
||||
$tids = $values;
|
||||
}
|
||||
elseif ($values) {
|
||||
$vocab_name = $field_info['settings']['allowed_values'][0]['vocabulary'];
|
||||
$names = taxonomy_vocabulary_get_names();
|
||||
|
||||
// Get the vocabulary for this term
|
||||
@@ -382,10 +521,12 @@ class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
|
||||
$vid = $field_info['settings']['allowed_values'][0]['vid'];
|
||||
}
|
||||
else {
|
||||
$vocab_name = $field_info['settings']['allowed_values'][0]['vocabulary'];
|
||||
$vid = $names[$vocab_name]->vid;
|
||||
}
|
||||
|
||||
// Remove leading and trailing spaces in term names
|
||||
$values = array_map('trim', $values);
|
||||
|
||||
// Cannot use taxonomy_term_load_multiple() since we have an array of names.
|
||||
// It wants a singular value. This query may return case-insensitive
|
||||
// matches.
|
||||
@@ -398,17 +539,33 @@ class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
|
||||
// If we're ignoring case, change both the matched term name keys and the
|
||||
// source values to lowercase.
|
||||
if (isset($arguments['ignore_case']) && $arguments['ignore_case']) {
|
||||
$ignore_case = TRUE;
|
||||
$existing_terms = array_change_key_case($existing_terms);
|
||||
$values = array_map('strtolower', $values);
|
||||
foreach ($values as $value) {
|
||||
$lower_values[$value] = strtolower($value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$ignore_case = FALSE;
|
||||
}
|
||||
foreach ($values as $value) {
|
||||
if (isset($existing_terms[$value])) {
|
||||
$tids[] = $existing_terms[$value];
|
||||
}
|
||||
elseif ($ignore_case && isset($existing_terms[$lower_values[$value]])) {
|
||||
$tids[] = $existing_terms[$lower_values[$value]];
|
||||
}
|
||||
elseif (!empty($arguments['create_term'])) {
|
||||
$new_term = new stdClass();
|
||||
$new_term->vid = $vid;
|
||||
$new_term->name = $value;
|
||||
$new_term->vocabulary_machine_name = $vocab_name;
|
||||
|
||||
// This term is being created with no fields, but we should still call
|
||||
// field_attach_validate() before saving, as that invokes
|
||||
// hook_field_attach_validate().
|
||||
MigrateDestinationEntity::fieldAttachValidate('taxonomy_term', $new_term);
|
||||
|
||||
taxonomy_term_save($new_term);
|
||||
$tids[] = $new_term->tid;
|
||||
// Add newly created term to existing array.
|
||||
@@ -449,6 +606,7 @@ class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
|
||||
* dealing with the file entity to an embedded MigrateFileInterface instance.
|
||||
*/
|
||||
abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
|
||||
|
||||
/**
|
||||
* Implementation of MigrateFieldHandler::fields().
|
||||
*
|
||||
@@ -459,15 +617,20 @@ abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
|
||||
* @param Migration $migration
|
||||
* The migration context for the parent field. We can look at the mappings
|
||||
* and determine which subfields are relevant.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields($type, $instance, $migration = NULL) {
|
||||
$fields = array(
|
||||
'file_class' => t('Option: <a href="@doc">Implementation of MigrateFile to use</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#file_class')),
|
||||
'language' => t('Subfield: Language for the field'),
|
||||
);
|
||||
|
||||
$field = field_info_field($instance['field_name']);
|
||||
if (field_is_translatable($instance['entity_type'], $field)) {
|
||||
$fields['language'] = t('Subfield: Language for the field');
|
||||
}
|
||||
|
||||
// If we can identify the file class mapped to this field, pick up the
|
||||
// subfields specific to that class.
|
||||
if ($migration) {
|
||||
@@ -478,7 +641,7 @@ abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
|
||||
$file_class = $mapping->getDefaultValue();
|
||||
}
|
||||
}
|
||||
if (!isset($file_class)) {
|
||||
if (empty($file_class)) {
|
||||
$file_class = 'MigrateFileUri';
|
||||
}
|
||||
$fields += call_user_func(array($file_class, 'fields'));
|
||||
@@ -502,7 +665,7 @@ abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
|
||||
$arguments = array();
|
||||
}
|
||||
|
||||
$language = $this->getFieldLanguage($entity, $field_info, $arguments);
|
||||
$default_language = $this->getFieldLanguage($entity, $field_info, $arguments);
|
||||
$migration = Migration::currentMigration();
|
||||
|
||||
// One can override the source class via CLI or drushrc.php (the
|
||||
@@ -543,8 +706,12 @@ abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
|
||||
}
|
||||
else {
|
||||
$migration->saveMessage(
|
||||
t('No data for subfield %key at row %delta for field %field',
|
||||
array('%key' => $key, '%delta' => $delta, '%field' => $field_info['field_name'])),
|
||||
t('No data for subfield %key at row %delta for field %field',
|
||||
array(
|
||||
'%key' => $key,
|
||||
'%delta' => $delta,
|
||||
'%field' => $field_info['field_name'],
|
||||
)),
|
||||
Migration::MESSAGE_WARNING);
|
||||
}
|
||||
}
|
||||
@@ -561,6 +728,10 @@ abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
|
||||
// MigrateFile class has saved a message indicating why.
|
||||
if ($file) {
|
||||
$field_array = array('fid' => $file->fid);
|
||||
$language = isset($instance_arguments['language']) ? $instance_arguments['language'] : $default_language;
|
||||
if (is_array($language)) {
|
||||
$language = $language[$delta];
|
||||
}
|
||||
$return[$language][] = $this->buildFieldArray($field_array, $instance_arguments, $delta);
|
||||
}
|
||||
}
|
||||
@@ -576,6 +747,7 @@ abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
|
||||
* Field API info on the general field.
|
||||
* @param $instance
|
||||
* Field API info on the field instance for this entity type.
|
||||
*
|
||||
* @return string
|
||||
* Directory relative to the Drupal public files directory.
|
||||
*/
|
||||
@@ -609,6 +781,7 @@ abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
|
||||
* Handle for file fields.
|
||||
*/
|
||||
class MigrateFileFieldHandler extends MigrateFileFieldBaseHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('file'));
|
||||
}
|
||||
@@ -624,13 +797,16 @@ class MigrateFileFieldHandler extends MigrateFileFieldBaseHandler {
|
||||
* @param Migration $migration
|
||||
* The migration context for the parent field. We can look at the mappings
|
||||
* and determine which subfields are relevant.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields($type, $instance, $migration = NULL) {
|
||||
$fields = parent::fields($type, $instance, $migration);
|
||||
$fields += array(
|
||||
'description' => t('Subfield: String to be used as the description value'),
|
||||
'display' => t('Subfield: String to be used as the display value'),
|
||||
'description' => t('Subfield: <a href="@doc">String to be used as the description value</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#description')),
|
||||
'display' => t('Subfield: <a href="@doc">String to be used as the display value</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#display')),
|
||||
);
|
||||
return $fields;
|
||||
}
|
||||
@@ -670,6 +846,7 @@ class MigrateFileFieldHandler extends MigrateFileFieldBaseHandler {
|
||||
* Handle for image fields;
|
||||
*/
|
||||
class MigrateImageFieldHandler extends MigrateFileFieldBaseHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('image'));
|
||||
}
|
||||
@@ -685,13 +862,16 @@ class MigrateImageFieldHandler extends MigrateFileFieldBaseHandler {
|
||||
* @param Migration $migration
|
||||
* The migration context for the parent field. We can look at the mappings
|
||||
* and determine which subfields are relevant.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fields($type, $instance, $migration = NULL) {
|
||||
$fields = parent::fields($type, $instance, $migration);
|
||||
$fields += array(
|
||||
'alt' => t('Subfield: String to be used as the alt value'),
|
||||
'title' => t('Subfield: String to be used as the title value'),
|
||||
'alt' => t('Subfield: <a href="@doc">String to be used as the alt value</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#alt')),
|
||||
'title' => t('Subfield: <a href="@doc">String to be used as the title value</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1224042#title')),
|
||||
);
|
||||
return $fields;
|
||||
}
|
||||
@@ -721,6 +901,7 @@ class MigrateImageFieldHandler extends MigrateFileFieldBaseHandler {
|
||||
}
|
||||
|
||||
class MigrateNodeReferenceFieldHandler extends MigrateSimpleFieldHandler {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(array(
|
||||
'value_key' => 'nid',
|
||||
@@ -735,6 +916,7 @@ class MigrateNodeReferenceFieldHandler extends MigrateSimpleFieldHandler {
|
||||
}
|
||||
|
||||
class MigrateUserReferenceFieldHandler extends MigrateSimpleFieldHandler {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(array(
|
||||
'value_key' => 'uid',
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* a Drupal file entity (creating the entity if necessary).
|
||||
*/
|
||||
interface MigrateFileInterface {
|
||||
|
||||
/**
|
||||
* Return a list of subfields and options specific to this implementation,
|
||||
* keyed by name.
|
||||
@@ -35,13 +36,112 @@ interface MigrateFileInterface {
|
||||
}
|
||||
|
||||
abstract class MigrateFileBase implements MigrateFileInterface {
|
||||
|
||||
/**
|
||||
* Extension of the core FILE_EXISTS_* constants, offering an alternative to
|
||||
* reuse the existing file if present as-is (core only offers the options of
|
||||
* replacing it or renaming to avoid collision).
|
||||
*/
|
||||
const FILE_EXISTS_REUSE = -1;
|
||||
|
||||
/**
|
||||
* An optional file object to use as a default starting point for building the
|
||||
* file entity.
|
||||
*
|
||||
* @var stdClass
|
||||
*/
|
||||
protected $defaultFile;
|
||||
|
||||
/**
|
||||
* How to handle destination filename collisions.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $fileReplace = FILE_EXISTS_RENAME;
|
||||
|
||||
/**
|
||||
* Set to TRUE to prevent file deletion on rollback.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $preserveFiles = FALSE;
|
||||
|
||||
public function __construct($arguments = array(), $default_file = NULL) {
|
||||
if (isset($arguments['preserve_files'])) {
|
||||
$this->preserveFiles = $arguments['preserve_files'];
|
||||
}
|
||||
if (isset($arguments['file_replace'])) {
|
||||
$this->fileReplace = $arguments['file_replace'];
|
||||
}
|
||||
if ($default_file) {
|
||||
$this->defaultFile = $default_file;
|
||||
}
|
||||
else {
|
||||
$this->defaultFile = new stdClass;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of MigrateFileInterface::fields().
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static public function fields() {
|
||||
return array();
|
||||
return array(
|
||||
'preserve_files' => t('Option: <a href="@doc">Boolean indicating whether files should be preserved or deleted on rollback</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#preserve_files')),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a file entity object suitable for saving.
|
||||
*
|
||||
* @param $destination
|
||||
* Path to the Drupal copy of the file.
|
||||
* @param $owner
|
||||
* Uid of the file owner.
|
||||
*
|
||||
* @return stdClass
|
||||
* A file object ready to be saved.
|
||||
*/
|
||||
protected function createFileEntity($destination, $owner) {
|
||||
$file = clone $this->defaultFile;
|
||||
$file->uri = $destination;
|
||||
$file->uid = $owner;
|
||||
if (!isset($file->filename)) {
|
||||
$file->filename = drupal_basename($destination);
|
||||
}
|
||||
if (!isset($file->filemime)) {
|
||||
$file->filemime = file_get_mimetype(urldecode($destination));
|
||||
}
|
||||
if (!isset($file->status)) {
|
||||
$file->status = FILE_STATUS_PERMANENT;
|
||||
}
|
||||
|
||||
if (empty($file->type) || $file->type == 'file') {
|
||||
// Try to determine the file type.
|
||||
if (module_exists('file_entity')) {
|
||||
$type = file_get_type($file);
|
||||
}
|
||||
elseif ($slash_pos = strpos($file->filemime, '/')) {
|
||||
$type = substr($file->filemime, 0, $slash_pos);
|
||||
}
|
||||
$file->type = isset($type) ? $type : 'file';
|
||||
}
|
||||
|
||||
// If we are replacing or reusing an existing filesystem entry,
|
||||
// also re-use its database record.
|
||||
if ($this->fileReplace == FILE_EXISTS_REPLACE ||
|
||||
$this->fileReplace == self::FILE_EXISTS_REUSE) {
|
||||
$existing_files = file_load_multiple(array(), array('uri' => $destination));
|
||||
if (count($existing_files)) {
|
||||
$existing = reset($existing_files);
|
||||
$file->fid = $existing->fid;
|
||||
$file->filename = $existing->filename;
|
||||
}
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,10 +168,24 @@ abstract class MigrateFileBase implements MigrateFileInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The simplest possible file class - where the value is a remote URI which
|
||||
* simply needs to be saved as the URI on the destination side, with no attempt
|
||||
* to copy or otherwise use it.
|
||||
*/
|
||||
class MigrateFileUriAsIs extends MigrateFileBase {
|
||||
|
||||
public function processFile($value, $owner) {
|
||||
$file = file_save($this->createFileEntity($value, $owner));
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the degenerate case where we already have a file ID.
|
||||
*/
|
||||
class MigrateFileFid extends MigrateFileBase {
|
||||
|
||||
/**
|
||||
* Implementation of MigrateFileInterface::processFile().
|
||||
*
|
||||
@@ -79,6 +193,7 @@ class MigrateFileFid extends MigrateFileBase {
|
||||
* An existing file entity ID (fid).
|
||||
* @param $owner
|
||||
* User ID (uid) to be the owner of the file. Ignored in this case.
|
||||
*
|
||||
* @return int
|
||||
* The file entity corresponding to the fid that was passed in.
|
||||
*/
|
||||
@@ -92,12 +207,6 @@ class MigrateFileFid extends MigrateFileBase {
|
||||
* Base class for creating core file entities.
|
||||
*/
|
||||
abstract class MigrateFile extends MigrateFileBase {
|
||||
/**
|
||||
* Extension of the core FILE_EXISTS_* constants, offering an alternative to
|
||||
* reuse the existing file if present as-is (core only offers the options of
|
||||
* replacing it or renaming to avoid collision).
|
||||
*/
|
||||
const FILE_EXISTS_REUSE = -1;
|
||||
|
||||
/**
|
||||
* The destination directory within Drupal.
|
||||
@@ -113,47 +222,14 @@ abstract class MigrateFile extends MigrateFileBase {
|
||||
*/
|
||||
protected $destinationFile = '';
|
||||
|
||||
/**
|
||||
* How to handle destination filename collisions.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $fileReplace = FILE_EXISTS_RENAME;
|
||||
|
||||
/**
|
||||
* Set to TRUE to prevent file deletion on rollback.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $preserveFiles = FALSE;
|
||||
|
||||
/**
|
||||
* An optional file object to use as a default starting point for building the
|
||||
* file entity.
|
||||
*
|
||||
* @var stdClass
|
||||
*/
|
||||
protected $defaultFile;
|
||||
|
||||
public function __construct($arguments = array(), $default_file = NULL) {
|
||||
parent::__construct($arguments, $default_file);
|
||||
if (isset($arguments['destination_dir'])) {
|
||||
$this->destinationDir = $arguments['destination_dir'];
|
||||
}
|
||||
if (isset($arguments['destination_file'])) {
|
||||
$this->destinationFile = $arguments['destination_file'];
|
||||
}
|
||||
if (isset($arguments['file_replace'])) {
|
||||
$this->fileReplace = $arguments['file_replace'];
|
||||
}
|
||||
if (isset($arguments['preserve_files'])) {
|
||||
$this->preserveFiles = $arguments['preserve_files'];
|
||||
}
|
||||
if ($default_file) {
|
||||
$this->defaultFile = $default_file;
|
||||
}
|
||||
else {
|
||||
$this->defaultFile = new stdClass;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,53 +238,14 @@ abstract class MigrateFile extends MigrateFileBase {
|
||||
* @return array
|
||||
*/
|
||||
static public function fields() {
|
||||
return array(
|
||||
'destination_dir' => t('Subfield: <a href="@doc">Path within Drupal files directory to store file</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#destination_dir')),
|
||||
'destination_file' => t('Subfield: <a href="@doc">Path within destination_dir to store the file.</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#destination_file')),
|
||||
'file_replace' => t('Option: <a href="@doc">Value of $replace in that file function. Does not apply to file_fast(). Defaults to FILE_EXISTS_RENAME.</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#file_replace')),
|
||||
'preserve_files' => t('Option: <a href="@doc">Boolean indicating whether files should be preserved or deleted on rollback</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#preserve_files')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a file entity object suitable for saving.
|
||||
*
|
||||
* @param $destination
|
||||
* Path to the Drupal copy of the file.
|
||||
* @param $owner
|
||||
* Uid of the file owner.
|
||||
* @return stdClass
|
||||
* A file object ready to be saved.
|
||||
*/
|
||||
protected function createFileEntity($destination, $owner) {
|
||||
$file = clone $this->defaultFile;
|
||||
$file->uri = $destination;
|
||||
$file->uid = $owner;
|
||||
if (!isset($file->filename)) {
|
||||
$file->filename = drupal_basename($destination);
|
||||
}
|
||||
if (!isset($file->filemime)) {
|
||||
$file->filemime = file_get_mimetype($destination);
|
||||
}
|
||||
if (!isset($file->status)) {
|
||||
$file->status = FILE_STATUS_PERMANENT;
|
||||
}
|
||||
// If we are replacing or reusing an existing filesystem entry,
|
||||
// also re-use its database record.
|
||||
if ($this->fileReplace == FILE_EXISTS_REPLACE ||
|
||||
$this->fileReplace == self::FILE_EXISTS_REUSE) {
|
||||
$existing_files = file_load_multiple(array(), array('uri' => $destination));
|
||||
if (count($existing_files)) {
|
||||
$existing = reset($existing_files);
|
||||
$file->fid = $existing->fid;
|
||||
$file->filename = $existing->filename;
|
||||
}
|
||||
}
|
||||
return $file;
|
||||
return parent::fields() + array(
|
||||
'destination_dir' => t('Subfield: <a href="@doc">Path within Drupal files directory to store file</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#destination_dir')),
|
||||
'destination_file' => t('Subfield: <a href="@doc">Path within destination_dir to store the file.</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#destination_file')),
|
||||
'file_replace' => t('Option: <a href="@doc">Value of $replace in that file function. Defaults to FILE_EXISTS_RENAME.</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#file_replace')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,6 +253,7 @@ abstract class MigrateFile extends MigrateFileBase {
|
||||
*
|
||||
* @param $destination
|
||||
* Destination path within Drupal.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the file is successfully saved, FALSE otherwise.
|
||||
*/
|
||||
@@ -228,6 +266,7 @@ abstract class MigrateFile extends MigrateFileBase {
|
||||
* The URI or local filespec of a file to be imported.
|
||||
* @param $owner
|
||||
* User ID (uid) to be the owner of the file.
|
||||
*
|
||||
* @return object
|
||||
* The file entity being created or referenced.
|
||||
*/
|
||||
@@ -242,13 +281,10 @@ abstract class MigrateFile extends MigrateFileBase {
|
||||
// Our own file_replace behavior - if the file exists, use it without
|
||||
// replacing it
|
||||
if ($this->fileReplace == self::FILE_EXISTS_REUSE) {
|
||||
// See if we this file already (we'll reuse a file entity if it exists).
|
||||
// See if we this file already (we'll reuse and resave a file entity if it exists).
|
||||
if (file_exists($destination)) {
|
||||
$file = $this->createFileEntity($destination, $owner);
|
||||
// File entity didn't already exist, create it
|
||||
if (empty($file->fid)) {
|
||||
$file = file_save($file);
|
||||
}
|
||||
$file = file_save($file);
|
||||
$this->markForPreservation($file->fid);
|
||||
return $file;
|
||||
}
|
||||
@@ -257,18 +293,18 @@ abstract class MigrateFile extends MigrateFileBase {
|
||||
}
|
||||
|
||||
// Prepare the destination directory.
|
||||
if (!file_prepare_directory(drupal_dirname($destination),
|
||||
FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
|
||||
$destdir = drupal_dirname($destination);
|
||||
if (!file_prepare_directory($destdir,
|
||||
FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
|
||||
$migration->saveMessage(t('Could not create destination directory for !dest',
|
||||
array('!dest' => $destination)));
|
||||
array('!dest' => $destination)));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Determine whether we can perform this operation based on overwrite rules.
|
||||
$destination = file_destination($destination, $this->fileReplace);
|
||||
if ($destination === FALSE) {
|
||||
$migration->saveMessage(t('The file could not be copied because ' .
|
||||
'file %dest already exists in the destination directory.',
|
||||
$migration->saveMessage(t('The file could not be copied because file %dest already exists in the destination directory.',
|
||||
array('%dest' => $destination)));
|
||||
return FALSE;
|
||||
}
|
||||
@@ -303,6 +339,7 @@ abstract class MigrateFile extends MigrateFileBase {
|
||||
* to be imported to Drupal.
|
||||
*/
|
||||
class MigrateFileUri extends MigrateFile {
|
||||
|
||||
/**
|
||||
* The source directory for the file, relative to which the value (source
|
||||
* file) will be taken.
|
||||
@@ -318,11 +355,19 @@ class MigrateFileUri extends MigrateFile {
|
||||
*/
|
||||
protected $sourcePath = '';
|
||||
|
||||
/**
|
||||
* Whether to apply rawurlencode to the components of an incoming file path.
|
||||
*/
|
||||
protected $urlEncode = TRUE;
|
||||
|
||||
public function __construct($arguments = array(), $default_file = NULL) {
|
||||
parent::__construct($arguments, $default_file);
|
||||
if (isset($arguments['source_dir'])) {
|
||||
$this->sourceDir = rtrim($arguments['source_dir'], "/\\");
|
||||
}
|
||||
if (isset($arguments['urlencode'])) {
|
||||
$this->urlEncode = $arguments['urlencode'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,6 +380,8 @@ class MigrateFileUri extends MigrateFile {
|
||||
array(
|
||||
'source_dir' => t('Subfield: <a href="@doc">Path to source file.</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#source_dir')),
|
||||
'urlencode' => t('Option: <a href="@doc">Encode all segments of the incoming path (defaults to TRUE).</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1540106#urlencode')),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -348,17 +395,27 @@ class MigrateFileUri extends MigrateFile {
|
||||
* TRUE if the copy succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function copyFile($destination) {
|
||||
// Perform the copy operation, with a cleaned-up path.
|
||||
$this->sourcePath = self::urlencode($this->sourcePath);
|
||||
if (!@copy($this->sourcePath, $destination)) {
|
||||
$migration = Migration::currentMigration();
|
||||
$migration->saveMessage(t('The specified file %file could not be copied to ' .
|
||||
'%destination.',
|
||||
array('%file' => $this->sourcePath, '%destination' => $destination)));
|
||||
return FALSE;
|
||||
if ($this->urlEncode) {
|
||||
// Perform the copy operation, with a cleaned-up path.
|
||||
$this->sourcePath = self::urlencode($this->sourcePath);
|
||||
}
|
||||
else {
|
||||
return TRUE;
|
||||
try {
|
||||
$copied = copy($this->sourcePath, $destination);
|
||||
if ($copied == FALSE) {
|
||||
$migration = Migration::currentMigration();
|
||||
$migration->saveMessage(t('The specified file %file could not be copied to %destination',
|
||||
array('%file' => $this->sourcePath, '%destination' => $destination)));
|
||||
}
|
||||
return $copied;
|
||||
} catch (Exception $e) {
|
||||
$migration = Migration::currentMigration();
|
||||
$migration->saveMessage(t('The specified file %file could not be copied to %destination: "%exception_msg"',
|
||||
array(
|
||||
'%file' => $this->sourcePath,
|
||||
'%destination' => $destination,
|
||||
'%exception_msg' => $e->getMessage(),
|
||||
)));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,8 +434,10 @@ class MigrateFileUri extends MigrateFile {
|
||||
$components[$key] = rawurlencode($component);
|
||||
}
|
||||
$filename = implode('/', $components);
|
||||
// Actually, we don't want colons encoded
|
||||
// Actually, we don't want certain characters encoded
|
||||
$filename = str_replace('%3A', ':', $filename);
|
||||
$filename = str_replace('%3F', '?', $filename);
|
||||
$filename = str_replace('%26', '&', $filename);
|
||||
}
|
||||
return $filename;
|
||||
}
|
||||
@@ -390,6 +449,7 @@ class MigrateFileUri extends MigrateFile {
|
||||
* The URI or local filespec of a file to be imported.
|
||||
* @param $owner
|
||||
* User ID (uid) to be the owner of the file.
|
||||
*
|
||||
* @return object
|
||||
* The file entity being created or referenced.
|
||||
*/
|
||||
@@ -419,6 +479,7 @@ class MigrateFileUri extends MigrateFile {
|
||||
* such as image data) to be stored as a real file in Drupal.
|
||||
*/
|
||||
class MigrateFileBlob extends MigrateFile {
|
||||
|
||||
/**
|
||||
* The file contents we will be writing to a real file.
|
||||
*
|
||||
@@ -431,6 +492,7 @@ class MigrateFileBlob extends MigrateFile {
|
||||
*
|
||||
* @param $destination
|
||||
* Drupal destination path.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the file contents were successfully written, FALSE otherwise.
|
||||
*/
|
||||
@@ -441,7 +503,7 @@ class MigrateFileBlob extends MigrateFile {
|
||||
else {
|
||||
$migration = Migration::currentMigration();
|
||||
$migration->saveMessage(t('Failed to write blob data to %destination',
|
||||
array('%destination' => $destination)));
|
||||
array('%destination' => $destination)));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -453,6 +515,7 @@ class MigrateFileBlob extends MigrateFile {
|
||||
* The file contents to be saved as a file.
|
||||
* @param $owner
|
||||
* User ID (uid) to be the owner of the file.
|
||||
*
|
||||
* @return object
|
||||
* File entity being created or referenced.
|
||||
*/
|
||||
@@ -467,6 +530,7 @@ class MigrateFileBlob extends MigrateFile {
|
||||
* Destination class implementing migration into the files table.
|
||||
*/
|
||||
class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
|
||||
/**
|
||||
* File class (MigrateFileUri etc.) doing the dirty wrk.
|
||||
*
|
||||
@@ -474,6 +538,10 @@ class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
*/
|
||||
protected $fileClass;
|
||||
|
||||
public function setFileClass($file_class) {
|
||||
$this->fileClass = $file_class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean indicating whether we should avoid deleting the actual file on
|
||||
* rollback.
|
||||
@@ -510,10 +578,12 @@ class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fields available to be mapped for the entity type (bundle)
|
||||
* Returns a list of fields available to be mapped for the entity type
|
||||
* (bundle)
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -521,10 +591,10 @@ class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
public function fields($migration = NULL) {
|
||||
$fields = array();
|
||||
// First the core properties
|
||||
$fields['fid'] = t('File: Existing file ID');
|
||||
$fields['uid'] = t('File: Uid of user associated with file');
|
||||
$fields['value'] = t('File: Representation of the source file (usually a URI)');
|
||||
$fields['timestamp'] = t('File: UNIX timestamp for the date the file was added');
|
||||
$fields['fid'] = t('Existing file ID');
|
||||
$fields['uid'] = t('Uid of user associated with file');
|
||||
$fields['value'] = t('Representation of the source file (usually a URI)');
|
||||
$fields['timestamp'] = t('UNIX timestamp for the date the file was added');
|
||||
|
||||
// Then add in anything provided by handlers
|
||||
$fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType, $this->bundle, $migration);
|
||||
@@ -559,12 +629,14 @@ class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
else {
|
||||
$preserve_files = FALSE;
|
||||
}
|
||||
$this->prepareRollback($fid);
|
||||
if ($preserve_files) {
|
||||
$this->fileDelete($file);
|
||||
}
|
||||
else {
|
||||
file_delete($file, TRUE);
|
||||
}
|
||||
$this->completeRollback($fid);
|
||||
migrate_instrument_stop('file_delete');
|
||||
}
|
||||
}
|
||||
@@ -589,6 +661,7 @@ class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
* File object to build. Prefilled with any fields mapped in the Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields (fid only in this case) of the file that was saved if
|
||||
* successful. FALSE on failure.
|
||||
@@ -600,7 +673,10 @@ class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
if (isset($file->fid)) {
|
||||
if ($file->fid != $row->migrate_map_destid1) {
|
||||
throw new MigrateException(t("Incoming fid !fid and map destination fid !destid1 don't match",
|
||||
array('!fid' => $file->fid, '!destid1' => $row->migrate_map_destid1)));
|
||||
array(
|
||||
'!fid' => $file->fid,
|
||||
'!destid1' => $row->migrate_map_destid1,
|
||||
)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -616,6 +692,19 @@ class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
$old_file = file_load($file->fid);
|
||||
}
|
||||
|
||||
// 'type' is the bundle property on file entities. It must be set here for
|
||||
// the sake of the prepare handlers, although it may be overridden later
|
||||
// based on the detected mime type.
|
||||
if (empty($file->type)) {
|
||||
// If a bundle was specified in the constructor we use it for filetype.
|
||||
if ($this->bundle != 'file') {
|
||||
$file->type = $this->bundle;
|
||||
}
|
||||
else {
|
||||
$file->type = 'file';
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke migration prepare handlers
|
||||
$this->prepare($file, $row);
|
||||
|
||||
@@ -641,7 +730,7 @@ class MigrateDestinationFile extends MigrateDestinationEntity {
|
||||
$file->preserve_files = FALSE;
|
||||
|
||||
$file_class = $this->fileClass;
|
||||
$source = new $file_class((array)$file, $file);
|
||||
$source = new $file_class((array) $file, $file);
|
||||
$file = $source->processFile($file->value, $file->uid);
|
||||
|
||||
if (is_object($file) && isset($file->fid)) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Destination class implementing migration into {menu_custom}.
|
||||
*/
|
||||
class MigrateDestinationMenu extends MigrateDestination {
|
||||
|
||||
static public function getKeySchema() {
|
||||
return array(
|
||||
'menu_name' => array(
|
||||
@@ -35,6 +36,7 @@ class MigrateDestinationMenu extends MigrateDestination {
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -55,6 +57,7 @@ class MigrateDestinationMenu extends MigrateDestination {
|
||||
* Menu object to build. Prefilled with any fields mapped in the Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields of the object that was saved if
|
||||
* successful. FALSE on failure.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Destination class implementing migration into {menu_links}.
|
||||
*/
|
||||
class MigrateDestinationMenuLinks extends MigrateDestination {
|
||||
|
||||
static public function getKeySchema() {
|
||||
return array(
|
||||
'mlid' => array(
|
||||
@@ -19,6 +20,7 @@ class MigrateDestinationMenuLinks extends MigrateDestination {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
@@ -33,6 +35,7 @@ class MigrateDestinationMenuLinks extends MigrateDestination {
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -72,9 +75,11 @@ class MigrateDestinationMenuLinks extends MigrateDestination {
|
||||
* Import a single row.
|
||||
*
|
||||
* @param $menu_link
|
||||
* Menu link object to build. Prefilled with any fields mapped in the Migration.
|
||||
* Menu link object to build. Prefilled with any fields mapped in the
|
||||
* Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields of the object that was saved if
|
||||
* successful. FALSE on failure.
|
||||
@@ -95,9 +100,9 @@ class MigrateDestinationMenuLinks extends MigrateDestination {
|
||||
throw new MigrateException(t('System-of-record is DESTINATION, and the provided mlid could not be found'));
|
||||
}
|
||||
$menu_link_to_update = (object) $old_menu_link;
|
||||
foreach($old_menu_link as $key => $value) {
|
||||
if (!isset($menu_link->$key)) {
|
||||
$menu_link->$key = $old_menu_link[$key];
|
||||
foreach ($old_menu_link as $key => $value) {
|
||||
if (!isset($menu_link->{$key})) {
|
||||
$menu_link->{$key} = $old_menu_link[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
* Destination class implementing migration into nodes.
|
||||
*/
|
||||
class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
|
||||
protected $bypassDestIdCheck = FALSE;
|
||||
|
||||
static public function getKeySchema() {
|
||||
return array(
|
||||
'nid' => array(
|
||||
@@ -51,6 +54,7 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -59,39 +63,39 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
$fields = array();
|
||||
// First the core (node table) properties
|
||||
$fields['nid'] = t('Node: <a href="@doc">Existing node ID</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#nid'));
|
||||
array('@doc' => 'http://drupal.org/node/1349696#nid'));
|
||||
$node_type = node_type_load($this->bundle);
|
||||
if ($node_type->has_title) {
|
||||
$fields['title'] = t('Node: <a href="@doc">',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#title'))
|
||||
. $node_type->title_label . '</a>';
|
||||
array('@doc' => 'http://drupal.org/node/1349696#title'))
|
||||
. $node_type->title_label . '</a>';
|
||||
}
|
||||
$fields['uid'] = t('Node: <a href="@doc">Authored by (uid)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#uid'));
|
||||
$fields['created'] = t('Node: <a href="@doc">Created timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#created'));
|
||||
$fields['changed'] = t('Node: <a href="@doc">Modified timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#changed'));
|
||||
$fields['status'] = t('Node: <a href="@doc">Published</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#status'));
|
||||
$fields['promote'] = t('Node: <a href="@doc">Promoted to front page</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#promote'));
|
||||
$fields['sticky'] = t('Node: <a href="@doc">Sticky at top of lists</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#sticky'));
|
||||
$fields['revision'] = t('Node: <a href="@doc">Create new revision</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#revision'));
|
||||
$fields['log'] = t('Node: <a href="@doc">Revision Log message</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#log'));
|
||||
$fields['language'] = t('Node: <a href="@doc">Language (fr, en, ...)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#language'));
|
||||
$fields['tnid'] = t('Node: <a href="@doc">The translation set id for this node</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#tnid'));
|
||||
$fields['translate'] = t('Node: <a href="@doc">A boolean indicating whether this translation page needs to be updated</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#translate'));
|
||||
$fields['revision_uid'] = t('Node: <a href="@doc">Modified (uid)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#revision_uid'));
|
||||
$fields['uid'] = t('<a href="@doc">Authored by (uid)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#uid'));
|
||||
$fields['created'] = t('<a href="@doc">Created timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#created'));
|
||||
$fields['changed'] = t('<a href="@doc">Modified timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#changed'));
|
||||
$fields['status'] = t('<a href="@doc">Published</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#status'));
|
||||
$fields['promote'] = t('<a href="@doc">Promoted to front page</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#promote'));
|
||||
$fields['sticky'] = t('<a href="@doc">Sticky at top of lists</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#sticky'));
|
||||
$fields['revision'] = t('<a href="@doc">Create new revision</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#revision'));
|
||||
$fields['log'] = t('<a href="@doc">Revision Log message</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#log'));
|
||||
$fields['language'] = t('<a href="@doc">Language (fr, en, ...)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#language'));
|
||||
$fields['tnid'] = t('<a href="@doc">The translation set id for this node</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#tnid'));
|
||||
$fields['translate'] = t('<a href="@doc">A boolean indicating whether this translation page needs to be updated</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#translate'));
|
||||
$fields['revision_uid'] = t('<a href="@doc">Modified (uid)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#revision_uid'));
|
||||
$fields['is_new'] = t('Option: <a href="@doc">Indicates a new node with the specified nid should be created</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349696#is_new'));
|
||||
array('@doc' => 'http://drupal.org/node/1349696#is_new'));
|
||||
|
||||
// Then add in anything provided by handlers
|
||||
$fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType, $this->bundle, $migration);
|
||||
@@ -121,6 +125,7 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
* Node object to build. Prefilled with any fields mapped in the Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields (nid only in this case) of the node that was saved if
|
||||
* successful. FALSE on failure.
|
||||
@@ -128,13 +133,16 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
public function import(stdClass $node, stdClass $row) {
|
||||
// Updating previously-migrated content?
|
||||
$migration = Migration::currentMigration();
|
||||
if (isset($row->migrate_map_destid1)) {
|
||||
if (isset($row->migrate_map_destid1) && !$this->bypassDestIdCheck) {
|
||||
// Make sure is_new is off
|
||||
$node->is_new = FALSE;
|
||||
if (isset($node->nid)) {
|
||||
if ($node->nid != $row->migrate_map_destid1) {
|
||||
throw new MigrateException(t("Incoming nid !nid and map destination nid !destid1 don't match",
|
||||
array('!nid' => $node->nid, '!destid1' => $row->migrate_map_destid1)));
|
||||
array(
|
||||
'!nid' => $node->nid,
|
||||
'!destid1' => $row->migrate_map_destid1,
|
||||
)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -142,13 +150,13 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
}
|
||||
// Get the existing vid, tnid so updates don't generate notices
|
||||
$values = db_select('node', 'n')
|
||||
->fields('n', array('vid', 'tnid'))
|
||||
->condition('nid', $node->nid)
|
||||
->execute()
|
||||
->fetchAssoc();
|
||||
->fields('n', array('vid', 'tnid'))
|
||||
->condition('nid', $node->nid)
|
||||
->execute()
|
||||
->fetchAssoc();
|
||||
if (empty($values)) {
|
||||
throw new MigrateException(t("Incoming node ID !nid no longer exists",
|
||||
array('!nid' => $node->nid)));
|
||||
array('!nid' => $node->nid)));
|
||||
}
|
||||
$node->vid = $values['vid'];
|
||||
if (empty($node->tnid)) {
|
||||
@@ -162,7 +170,7 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
$old_node = node_load($node->nid);
|
||||
if (empty($old_node)) {
|
||||
throw new MigrateException(t('System-of-record is DESTINATION, but node !nid does not exist',
|
||||
array('!nid' => $node->nid)));
|
||||
array('!nid' => $node->nid)));
|
||||
}
|
||||
if (!isset($node->created)) {
|
||||
$node->created = $old_node->created;
|
||||
@@ -177,7 +185,8 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
$node->uid = $old_node->uid;
|
||||
}
|
||||
}
|
||||
elseif (!isset($node->type)) {
|
||||
|
||||
if (!isset($node->type)) {
|
||||
// Default the type to our designated destination bundle (by doing this
|
||||
// conditionally, we permit some flexibility in terms of implementing
|
||||
// migrations which can affect more than one type).
|
||||
@@ -207,7 +216,12 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
if (isset($node->uid)) {
|
||||
$uid = $node->uid;
|
||||
}
|
||||
if (isset($node->revision)) {
|
||||
$revision = $node->revision;
|
||||
}
|
||||
|
||||
node_object_prepare($node);
|
||||
|
||||
if (isset($created)) {
|
||||
$node->created = $created;
|
||||
}
|
||||
@@ -215,6 +229,9 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
if (isset($uid)) {
|
||||
$node->uid = $uid;
|
||||
}
|
||||
if (isset($revision)) {
|
||||
$node->revision = $revision;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke migration prepare handlers
|
||||
@@ -230,11 +247,11 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
foreach ($old_node as $field => $value) {
|
||||
// An explicit NULL in the source data means to wipe to old value (i.e.,
|
||||
// don't copy it over from $old_node)
|
||||
if (property_exists($node, $field) && $node->$field === NULL) {
|
||||
if (property_exists($node, $field) && $node->{$field} === NULL) {
|
||||
// Ignore this field
|
||||
}
|
||||
elseif (!isset($node->$field)) {
|
||||
$node->$field = $old_node->$field;
|
||||
elseif (!isset($node->{$field})) {
|
||||
$node->{$field} = $old_node->{$field};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,6 +263,14 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
$updating = FALSE;
|
||||
}
|
||||
|
||||
// Make sure that if is_new is not TRUE, it is not present.
|
||||
if (isset($node->is_new) && empty($node->is_new)) {
|
||||
unset($node->is_new);
|
||||
}
|
||||
|
||||
// Validate field data prior to saving.
|
||||
MigrateDestinationEntity::fieldAttachValidate('node', $node);
|
||||
|
||||
migrate_instrument_start('node_save');
|
||||
node_save($node);
|
||||
migrate_instrument_stop('node_save');
|
||||
@@ -270,7 +295,7 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
|
||||
// Potentially fix uid and timestamp in node_revisions.
|
||||
$query = db_update('node_revision')
|
||||
->condition('vid', $node->vid);
|
||||
->condition('vid', $node->vid);
|
||||
if (isset($changed)) {
|
||||
$fields['timestamp'] = $changed;
|
||||
}
|
||||
@@ -296,3 +321,145 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to import revisions.
|
||||
*
|
||||
* Adapted from
|
||||
* http://www.darrenmothersele.com/blog/2012/07/16/migrating-node-revisions-drupal-7/
|
||||
*
|
||||
* Class MigrateDestinationNodeRevision
|
||||
*
|
||||
* @author darrenmothersele
|
||||
* @author cthos
|
||||
*/
|
||||
class MigrateDestinationNodeRevision extends MigrateDestinationNode {
|
||||
|
||||
/**
|
||||
* Basic initialization.
|
||||
*
|
||||
* @see parent::__construct
|
||||
*
|
||||
* @param string $bundle
|
||||
* A.k.a. the content type (page, article, etc.) of the node.
|
||||
* @param array $options
|
||||
* Options applied to nodes.
|
||||
*/
|
||||
public function __construct($bundle, array $options = array()) {
|
||||
parent::__construct($bundle, $options);
|
||||
|
||||
$this->bypassDestIdCheck = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key schema for the node revision destination.
|
||||
*
|
||||
* @see MigrateDestination::getKeySchema
|
||||
*
|
||||
* @return array
|
||||
* Returns the key schema.
|
||||
*/
|
||||
static public function getKeySchema() {
|
||||
return array(
|
||||
'vid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'description' => 'ID of destination node revision',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns additional fields on top of node destinations.
|
||||
*
|
||||
* @param string $migration
|
||||
* Active migration
|
||||
*
|
||||
* @return array
|
||||
* Fields.
|
||||
*/
|
||||
public function fields($migration = NULL) {
|
||||
$fields = parent::fields($migration);
|
||||
$fields['vid'] = t('Node: <a href="@doc">Revision (vid)</a>', array('@doc' => 'http://drupal.org/node/1298724'));
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolls back any versions that have been created.
|
||||
*
|
||||
* @param array $vids
|
||||
* Version ids to roll back.
|
||||
*/
|
||||
public function bulkRollback(array $vids) {
|
||||
migrate_instrument_start('revision_delete_multiple');
|
||||
$this->prepareRollback($vids);
|
||||
$nids = array();
|
||||
foreach ($vids as $vid) {
|
||||
if ($revision = node_load(NULL, $vid)) {
|
||||
db_delete('node_revision')
|
||||
->condition('vid', $revision->vid)
|
||||
->execute();
|
||||
module_invoke_all('node_revision_delete', $revision);
|
||||
field_attach_delete_revision('node', $revision);
|
||||
$nids[$revision->nid] = $revision->nid;
|
||||
}
|
||||
}
|
||||
$this->completeRollback($vids);
|
||||
foreach ($nids as $nid) {
|
||||
$vid = db_select('node_revision', 'nr')
|
||||
->fields('nr', array('vid'))
|
||||
->condition('nid', $nid, '=')
|
||||
->execute()
|
||||
->fetchField();
|
||||
if (!empty($vid)) {
|
||||
db_update('node')
|
||||
->fields(array('vid' => $vid))
|
||||
->condition('nid', $nid, '=')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
migrate_instrument_stop('revision_delete_multiple');
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden import method.
|
||||
*
|
||||
* This is done because parent::import will return the nid of the newly
|
||||
* created nodes. This is bad since the migrate_map_* table will have
|
||||
* nids instead of vids, which could cause a nightmare explosion on
|
||||
* rollback.
|
||||
*
|
||||
* @param stdClass $node
|
||||
* Populated entity.
|
||||
*
|
||||
* @param stdClass $row
|
||||
* Source information in object format.
|
||||
*
|
||||
* @return array|bool
|
||||
* Array with newly created vid, or FALSE on error.
|
||||
*
|
||||
* @throws MigrateException
|
||||
*/
|
||||
public function import(stdClass $node, stdClass $row) {
|
||||
// We're importing revisions, this should be set.
|
||||
$node->revision = 1;
|
||||
|
||||
if (empty($node->nid)) {
|
||||
throw new MigrateException(t('Missing incoming nid.'));
|
||||
}
|
||||
|
||||
$original_updated = $this->numUpdated;
|
||||
|
||||
parent::import($node, $row);
|
||||
|
||||
// Reset num updated and increment created since new revision is always an update.
|
||||
$this->numUpdated = $original_updated;
|
||||
$this->numCreated++;
|
||||
|
||||
if (empty($node->vid)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return array($node->vid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,29 +6,37 @@
|
||||
*/
|
||||
|
||||
class MigratePathEntityHandler extends MigrateDestinationHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('entity'));
|
||||
}
|
||||
|
||||
public function fields($entity_type, $bundle) {
|
||||
/**
|
||||
* Implementation of MigrateDestinationHandler::fields().
|
||||
*/
|
||||
public function fields($entity_type, $bundle, $migration = NULL) {
|
||||
if (module_exists('path')) {
|
||||
switch ($entity_type) {
|
||||
case 'node':
|
||||
return array('path' => t('Node: Path alias'));
|
||||
case 'user':
|
||||
return array('path' => t('User: Path alias'));
|
||||
case 'taxonomy_term':
|
||||
return array('path' => t('Term: Path alias'));
|
||||
}
|
||||
return array('path' => t('Path alias'));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function prepare($entity, stdClass $row) {
|
||||
if (module_exists('path') && isset($entity->path)) {
|
||||
$path = $entity->path;
|
||||
$entity->path = array();
|
||||
$entity->path['alias'] = $path;
|
||||
// Make sure the alias doesn't already exist
|
||||
$query = db_select('url_alias')
|
||||
->condition('alias', $entity->path)
|
||||
->condition('language', $entity->language);
|
||||
$query->addExpression('1');
|
||||
$query->range(0, 1);
|
||||
if (!$query->execute()->fetchField()) {
|
||||
$path = $entity->path;
|
||||
$entity->path = array();
|
||||
$entity->path['alias'] = $path;
|
||||
}
|
||||
else {
|
||||
unset($entity->path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
* public function prepareRow($row);
|
||||
* $choices = Database::getConnection('default', 'legacy')
|
||||
* ->select('src_poll_choice', 'c')
|
||||
* ->fields('c', array('choice_label', 'choice_order', 'choice_total'))
|
||||
* ->fields('c', array('choice_label', 'choice_order',
|
||||
* 'choice_total'))
|
||||
* ->condition('c.choiceid', $row->src_contentid);
|
||||
* ->execute();
|
||||
* $row->src_choices = array();
|
||||
@@ -38,7 +39,8 @@
|
||||
* ->select('src_poll_vote', 'v')
|
||||
* ->fields('v', array('choice_uid', 'hostname', 'timestamp'))
|
||||
* ->condition('v.choiceid', $row->src_contentid);
|
||||
* $votes = $query->innerJoin('src_poll_choice', 'c', 'v.choice_id=c.choice_id')
|
||||
* $votes = $query->innerJoin('src_poll_choice', 'c',
|
||||
* 'v.choice_id=c.choice_id')
|
||||
* ->fields('c', array('choice_label'))
|
||||
* ->execute();
|
||||
* $row->src_votes = array();
|
||||
@@ -56,17 +58,21 @@
|
||||
*/
|
||||
|
||||
class MigratePollEntityHandler extends MigrateDestinationHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('node'));
|
||||
}
|
||||
|
||||
public function fields($entity_type, $bundle) {
|
||||
/**
|
||||
* Implementation of MigrateDestinationHandler::fields().
|
||||
*/
|
||||
public function fields($entity_type, $bundle, $migration = NULL) {
|
||||
if ($bundle == 'poll') {
|
||||
$fields = array(
|
||||
'active' => t('Poll: Active status'),
|
||||
'runtime' => t('Poll: How long the poll runs for in seconds'),
|
||||
'choice' => t('Poll: Choices. Each choice is an array with chtext, chvotes, and weight keys.'),
|
||||
'votes' => t('Poll: Votes. Each vote is an array with chid (or chtext), uid, hostname, and timestamp keys'),
|
||||
'active' => t('Active status'),
|
||||
'runtime' => t('How long the poll runs for in seconds'),
|
||||
'choice' => t('Choices. Each choice is an array with chtext, chvotes, and weight keys.'),
|
||||
'votes' => t('Votes. Each vote is an array with chid (or chtext), uid, hostname, and timestamp keys'),
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -89,7 +95,10 @@ class MigratePollEntityHandler extends MigrateDestinationHandler {
|
||||
foreach ($row->choice as $choice) {
|
||||
// Have no mapping tracking for chid, so assume choice text is unique.
|
||||
db_update('poll_choice')
|
||||
->fields(array('chvotes' => $choice['chvotes'], 'weight' => $choice['weight']))
|
||||
->fields(array(
|
||||
'chvotes' => $choice['chvotes'],
|
||||
'weight' => $choice['weight'],
|
||||
))
|
||||
->condition('nid', $entity->nid)
|
||||
->condition('chtext', $choice['chtext'])
|
||||
->execute();
|
||||
@@ -97,15 +106,17 @@ class MigratePollEntityHandler extends MigrateDestinationHandler {
|
||||
|
||||
// Insert actual votes.
|
||||
foreach ($row->votes as $vote) {
|
||||
$chid = $vote['chid'];
|
||||
if (!isset($chid)) {
|
||||
if (!isset($vote['chid'])) {
|
||||
$result = db_select('poll_choice', 'pc')
|
||||
->fields('pc', array('chid'))
|
||||
->condition('pc.nid', $entity->nid)
|
||||
->condition('pc.chtext', $vote['chtext'])
|
||||
->execute();
|
||||
->fields('pc', array('chid'))
|
||||
->condition('pc.nid', $entity->nid)
|
||||
->condition('pc.chtext', $vote['chtext'])
|
||||
->execute();
|
||||
$chid = $result->fetchField();
|
||||
}
|
||||
else {
|
||||
$chid = $vote['chid'];
|
||||
}
|
||||
db_insert('poll_vote')
|
||||
->fields(array(
|
||||
'chid' => $chid,
|
||||
|
||||
@@ -6,16 +6,20 @@
|
||||
*/
|
||||
|
||||
class MigrateStatisticsEntityHandler extends MigrateDestinationHandler {
|
||||
|
||||
public function __construct() {
|
||||
$this->registerTypes(array('node'));
|
||||
}
|
||||
|
||||
public function fields() {
|
||||
/**
|
||||
* Implementation of MigrateDestinationHandler::fields().
|
||||
*/
|
||||
public function fields($entity_type, $bundle, $migration = NULL) {
|
||||
if (module_exists('statistics')) {
|
||||
$fields = array(
|
||||
'totalcount' => t('Node: The total number of times the node has been viewed.'),
|
||||
'daycount' => t('Node: The total number of times the node has been viewed today.'),
|
||||
'timestamp' => t('Node: The most recent time the node has been viewed.'),
|
||||
'totalcount' => t('The total number of times the node has been viewed.'),
|
||||
'daycount' => t('The total number of times the node has been viewed today.'),
|
||||
'timestamp' => t('The most recent time the node has been viewed.'),
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -26,9 +30,12 @@ class MigrateStatisticsEntityHandler extends MigrateDestinationHandler {
|
||||
|
||||
public function complete($node, stdClass $row) {
|
||||
if (module_exists('statistics') && isset($node->nid)) {
|
||||
$totalcount = isset($node->totalcount) ? $node->totalcount : 0;
|
||||
$daycount = isset($node->daycount) ? $node->daycount : 0;
|
||||
$timestamp = isset($node->timestamp) ? $node->timestamp : 0;
|
||||
$totalcount = isset($node->totalcount) && is_numeric($node->totalcount) ?
|
||||
$node->totalcount : 0;
|
||||
$daycount = isset($node->daycount) && is_numeric($node->daycount) ?
|
||||
$node->daycount : 0;
|
||||
$timestamp = isset($node->timestamp) && is_numeric($node->timestamp) ?
|
||||
$node->timestamp : 0;
|
||||
db_merge('node_counter')
|
||||
->key(array('nid' => $node->nid))
|
||||
->fields(array(
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* the Schema API.
|
||||
*/
|
||||
class MigrateDestinationTable extends MigrateDestination {
|
||||
|
||||
/**
|
||||
* The schema of the current table.
|
||||
*
|
||||
@@ -57,19 +58,20 @@ class MigrateDestinationTable extends MigrateDestination {
|
||||
/**
|
||||
* Delete a single row.
|
||||
*
|
||||
* @param $id
|
||||
* Primary key values.
|
||||
* @param array $ids
|
||||
* The primary key values of the row to be deleted.
|
||||
*/
|
||||
public function rollback(array $id) {
|
||||
public function rollback(array $ids) {
|
||||
migrate_instrument_start('table rollback');
|
||||
$delete = db_delete($this->tableName);
|
||||
$keys = array_keys(self::getKeySchema($this->tableName));
|
||||
$i = 0;
|
||||
foreach ($id as $value) {
|
||||
$key = $keys[$i++];
|
||||
$values = array_combine($keys, $ids);
|
||||
$this->prepareRollback($values);
|
||||
$delete = db_delete($this->tableName);
|
||||
foreach ($values as $key => $value) {
|
||||
$delete->condition($key, $value);
|
||||
}
|
||||
$delete->execute();
|
||||
$this->completeRollback($values);
|
||||
migrate_instrument_stop('table rollback');
|
||||
}
|
||||
|
||||
@@ -80,6 +82,7 @@ class MigrateDestinationTable extends MigrateDestination {
|
||||
* Object object to build. Prefilled with any fields mapped in the Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields of the object that was saved if
|
||||
* successful. FALSE on failure.
|
||||
@@ -121,7 +124,7 @@ class MigrateDestinationTable extends MigrateDestination {
|
||||
}
|
||||
|
||||
$select = db_select($this->tableName)
|
||||
->fields($this->tableName);
|
||||
->fields($this->tableName);
|
||||
foreach ($this->schema['primary key'] as $key) {
|
||||
$select->condition($key, $entity->{$key});
|
||||
}
|
||||
@@ -130,7 +133,7 @@ class MigrateDestinationTable extends MigrateDestination {
|
||||
throw new MigrateException(t('System-of-record is DESTINATION, but the destination entity does not exist'));
|
||||
}
|
||||
foreach ($entity as $field => $value) {
|
||||
$old_entity->$field = $entity->$field;
|
||||
$old_entity->{$field} = $entity->{$field};
|
||||
}
|
||||
$entity = $old_entity;
|
||||
}
|
||||
@@ -158,6 +161,7 @@ class MigrateDestinationTable extends MigrateDestination {
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -165,7 +169,7 @@ class MigrateDestinationTable extends MigrateDestination {
|
||||
public function fields($migration = NULL) {
|
||||
$fields = array();
|
||||
foreach ($this->schema['fields'] as $column => $schema) {
|
||||
$fields[$column] = t('Type: !type', array('!type' => $schema['type']));
|
||||
$fields[$column] = t('!type', array('!type' => $schema['type']));
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
@@ -207,4 +211,44 @@ class MigrateDestinationTable extends MigrateDestination {
|
||||
$migration->complete($entity, $source_row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Give handlers a shot at cleaning up before the row has been rolled back.
|
||||
*
|
||||
* @param array $ids
|
||||
* The primary key values of the row about to be deleted, keyed by field
|
||||
* name.
|
||||
*/
|
||||
public function prepareRollback(array $ids) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('table', 'prepareRollback', $ids);
|
||||
|
||||
// Then call any complete handler for this specific Migration.
|
||||
if (method_exists($migration, 'prepareRollback')) {
|
||||
$migration->prepareRollback($ids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Give handlers a shot at cleaning up after a row has been rolled back.
|
||||
*
|
||||
* @param array $ids
|
||||
* The primary key values of the row which has been deleted, keyed by field
|
||||
* name.
|
||||
*/
|
||||
public function completeRollback(array $ids) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('table', 'completeRollback', $ids);
|
||||
|
||||
// Then call any complete handler for this specific Migration.
|
||||
if (method_exists($migration, 'completeRollback')) {
|
||||
$migration->completeRollback($ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Destination class implementing migration into a single table.
|
||||
*/
|
||||
class MigrateDestinationTableCopy extends MigrateDestination {
|
||||
|
||||
public function __construct($tableName, $keySchema) {
|
||||
parent::__construct();
|
||||
$this->tableName = $tableName;
|
||||
@@ -41,6 +42,7 @@ class MigrateDestinationTableCopy extends MigrateDestination {
|
||||
* Object object to build. Prefilled with any fields mapped in the Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields of the object that was saved if
|
||||
* successful. FALSE on failure.
|
||||
@@ -49,14 +51,21 @@ class MigrateDestinationTableCopy extends MigrateDestination {
|
||||
$migration = MigrationBase::currentMigration();
|
||||
|
||||
$fields = clone $row;
|
||||
$keys = array_keys($this->keySchema);
|
||||
$values = array();
|
||||
foreach ($keys as $key) {
|
||||
$values[] = $row->$key;
|
||||
// Remove all map data, otherwise we'll try to write it to the destination
|
||||
// table.
|
||||
foreach ($fields as $field => $data) {
|
||||
if (strpos($field, 'migrate_map_') === 0) {
|
||||
unset($fields->{$field});
|
||||
}
|
||||
}
|
||||
unset($fields->migrate_map_destid1);
|
||||
unset($fields->needs_update);
|
||||
$query = db_merge($this->tableName)->key($keys, $values)->fields((array)$fields);
|
||||
$keys = array_keys($this->keySchema);
|
||||
$values = array();
|
||||
foreach ($keys as $key) {
|
||||
$values[] = $row->{$key};
|
||||
}
|
||||
$query = db_merge($this->tableName)
|
||||
->key($keys, $values)
|
||||
->fields((array) $fields);
|
||||
try {
|
||||
$status = $query->execute();
|
||||
if ($status == MergeQuery::STATUS_INSERT) {
|
||||
@@ -66,13 +75,11 @@ class MigrateDestinationTableCopy extends MigrateDestination {
|
||||
$this->numUpdated++;
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
} catch (MigrateException $e) {
|
||||
$migration->saveMessage($e->getMessage(), $e->getLevel());
|
||||
Migration::displayMessage($e->getMessage());
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
} catch (Exception $e) {
|
||||
$migration->handleException($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* Destination class implementing migration into terms.
|
||||
*/
|
||||
class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
|
||||
/**
|
||||
* Boolean indicating whether to permit duplicate terms to be created.
|
||||
*
|
||||
@@ -59,10 +60,12 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fields available to be mapped for this vocabulary (bundle)
|
||||
* Returns a list of fields available to be mapped for this vocabulary
|
||||
* (bundle)
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -70,21 +73,21 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
public function fields($migration = NULL) {
|
||||
$fields = array();
|
||||
// First the core (taxonomy_term_data table) properties
|
||||
$fields['tid'] = t('Term: <a href="@doc">Existing term ID</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#tid'));
|
||||
$fields['name'] = t('Term: <a href="@doc">Name</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#name'));
|
||||
$fields['description'] = t('Term: <a href="@doc">Description</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#description'));
|
||||
$fields['parent'] = t('Term: <a href="@doc">Parent (by Drupal term ID)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#parent'));
|
||||
$fields['tid'] = t('<a href="@doc">Existing term ID</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#tid'));
|
||||
$fields['name'] = t('<a href="@doc">Name</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#name'));
|
||||
$fields['description'] = t('<a href="@doc">Description</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#description'));
|
||||
$fields['parent'] = t('<a href="@doc">Parent (by Drupal term ID)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#parent'));
|
||||
// TODO: Remove parent_name, implement via arguments
|
||||
$fields['parent_name'] = t('Term: <a href="@doc">Parent (by name)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#parent_name'));
|
||||
$fields['format'] = t('Term: <a href="@doc">Format</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#format'));
|
||||
$fields['weight'] = t('Term: <a href="@doc">Weight</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#weight'));
|
||||
$fields['parent_name'] = t('<a href="@doc">Parent (by name)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#parent_name'));
|
||||
$fields['format'] = t('<a href="@doc">Format</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#format'));
|
||||
$fields['weight'] = t('<a href="@doc">Weight</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349702#weight'));
|
||||
|
||||
// Then add in anything provided by handlers
|
||||
$fields += migrate_handler_invoke_all('entity', 'fields', $this->entityType, $this->bundle, $migration);
|
||||
@@ -131,6 +134,7 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
* Term object to build. Prefilled with any fields mapped in the Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields (tid only in this case) of the term that was saved if
|
||||
* successful. FALSE on failure.
|
||||
@@ -142,13 +146,22 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
if (isset($term->tid)) {
|
||||
if ($term->tid != $row->migrate_map_destid1) {
|
||||
throw new MigrateException(t("Incoming tid !tid and map destination nid !destid1 don't match",
|
||||
array('!tid' => $term->tid, '!destid1' => $row->migrate_map_destid1)));
|
||||
array(
|
||||
'!tid' => $term->tid,
|
||||
'!destid1' => $row->migrate_map_destid1,
|
||||
)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$term->tid = $row->migrate_map_destid1;
|
||||
}
|
||||
}
|
||||
|
||||
// Default to bundle if no vocabulary machine name provided
|
||||
if (!isset($term->vocabulary_machine_name)) {
|
||||
$term->vocabulary_machine_name = $this->bundle;
|
||||
}
|
||||
|
||||
if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
|
||||
if (!isset($term->tid)) {
|
||||
throw new MigrateException(t('System-of-record is DESTINATION, but no destination tid provided'));
|
||||
@@ -161,15 +174,11 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
array('!tid' => $term->tid)));
|
||||
}
|
||||
foreach ($rawterm as $field => $value) {
|
||||
$old_term->$field = $term->$field;
|
||||
$old_term->{$field} = $term->{$field};
|
||||
}
|
||||
$term = $old_term;
|
||||
}
|
||||
else {
|
||||
// Default to bundle if no vocabulary machine name provided
|
||||
if (!isset($term->vocabulary_machine_name)) {
|
||||
$term->vocabulary_machine_name = $this->bundle;
|
||||
}
|
||||
// vid is required
|
||||
if (empty($term->vid)) {
|
||||
static $vocab_map = array();
|
||||
@@ -203,7 +212,13 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
if (empty($term->parent)) {
|
||||
$term->parent = array(0);
|
||||
}
|
||||
if (is_array($term->parent) && isset($term->parent['arguments'])) {
|
||||
elseif (!is_array($term->parent)) {
|
||||
// Convert to an array for comparison in findMatchingTerm().
|
||||
// Note: taxonomy_term_save() also normalizes to an array.
|
||||
$term->parent = array($term->parent);
|
||||
}
|
||||
|
||||
if (isset($term->parent['arguments'])) {
|
||||
// Unset arguments here to avoid duplicate entries in the
|
||||
// term_hierarchy table.
|
||||
unset($term->parent['arguments']);
|
||||
@@ -211,12 +226,19 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
if (!isset($term->format)) {
|
||||
$term->format = $this->textFormat;
|
||||
}
|
||||
if (!isset($term->language)) {
|
||||
$term->language = $this->language;
|
||||
}
|
||||
$this->prepare($term, $row);
|
||||
|
||||
if (empty($term->name)) {
|
||||
throw new MigrateException(t('Taxonomy term name is required.'));
|
||||
}
|
||||
|
||||
if (!$this->allowDuplicateTerms && $existing_term = $this->findMatchingTerm($term)) {
|
||||
foreach ($existing_term as $field => $value) {
|
||||
if (!isset($term->$field)) {
|
||||
$term->$field = $existing_term->$field;
|
||||
if (!isset($term->{$field})) {
|
||||
$term->{$field} = $existing_term->{$field};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,8 +250,8 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
if ($existing_term) {
|
||||
// Incoming data overrides existing data, so only copy non-existent fields
|
||||
foreach ($existing_term as $field => $value) {
|
||||
if (!isset($term->$field)) {
|
||||
$term->$field = $existing_term->$field;
|
||||
if (!isset($term->{$field})) {
|
||||
$term->{$field} = $existing_term->{$field};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,6 +264,9 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
|
||||
$updating = FALSE;
|
||||
}
|
||||
|
||||
// Validate field data prior to saving.
|
||||
MigrateDestinationEntity::fieldAttachValidate('taxonomy_term', $term);
|
||||
|
||||
migrate_instrument_start('taxonomy_term_save');
|
||||
$status = taxonomy_term_save($term);
|
||||
migrate_instrument_stop('taxonomy_term_save');
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* Destination class implementing migration into users.
|
||||
*/
|
||||
class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
|
||||
/**
|
||||
* Indicates whether incoming passwords are md5-encrypted - if so, we will
|
||||
* rehash them similarly to the D6->D7 upgrade path.
|
||||
@@ -68,6 +69,7 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
@@ -75,44 +77,44 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
public function fields($migration = NULL) {
|
||||
$fields = array();
|
||||
// First the core (users table) properties
|
||||
$fields['uid'] = t('User: <a href="@doc">Existing user ID</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#uid'));
|
||||
$fields['mail'] = t('User: <a href="@doc">Email address</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#mail'));
|
||||
$fields['name'] = t('User: <a href="@doc">Username</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#name'));
|
||||
$fields['pass'] = t('User: <a href="@doc">Password (plain text)</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#pass'));
|
||||
$fields['status'] = t('User: <a href="@doc">Status</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#status'));
|
||||
$fields['created'] = t('User: <a href="@doc">Registered timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#created'));
|
||||
$fields['access'] = t('User: <a href="@doc">Last access timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#access'));
|
||||
$fields['login'] = t('User: <a href="@doc">Last login timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#login'));
|
||||
$fields['roles'] = t('User: <a href="@doc">Role IDs</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#roles'));
|
||||
$fields['role_names'] = t('User: <a href="@doc">Role Names</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#role_names'));
|
||||
$fields['picture'] = t('User: <a href="@doc">Picture</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#picture'));
|
||||
$fields['signature'] = t('User: <a href="@doc">Signature</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#signature'));
|
||||
$fields['signature_format'] = t('User: <a href="@doc">Signature format</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#signature_format'));
|
||||
$fields['timezone'] = t('User: <a href="@doc">Timezone</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#timezone'));
|
||||
$fields['language'] = t('User: <a href="@doc">Language</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#language'));
|
||||
$fields['theme'] = t('User: <a href="@doc">Default theme</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#theme'));
|
||||
$fields['init'] = t('User: <a href="@doc">Init</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#init'));
|
||||
$fields['data'] = t('User: <a href="@doc">Data</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#init'));
|
||||
$fields['uid'] = t('<a href="@doc">Existing user ID</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#uid'));
|
||||
$fields['mail'] = t('<a href="@doc">Email address</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#mail'));
|
||||
$fields['name'] = t('<a href="@doc">Username</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#name'));
|
||||
$fields['pass'] = t('<a href="@doc">Password</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#pass'));
|
||||
$fields['status'] = t('<a href="@doc">Status</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#status'));
|
||||
$fields['created'] = t('<a href="@doc">Registered timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#created'));
|
||||
$fields['access'] = t('<a href="@doc">Last access timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#access'));
|
||||
$fields['login'] = t('<a href="@doc">Last login timestamp</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#login'));
|
||||
$fields['roles'] = t('<a href="@doc">Role IDs</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#roles'));
|
||||
$fields['role_names'] = t('<a href="@doc">Role Names</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#role_names'));
|
||||
$fields['picture'] = t('<a href="@doc">Picture</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#picture'));
|
||||
$fields['signature'] = t('<a href="@doc">Signature</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#signature'));
|
||||
$fields['signature_format'] = t('<a href="@doc">Signature format</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#signature_format'));
|
||||
$fields['timezone'] = t('<a href="@doc">Timezone</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#timezone'));
|
||||
$fields['language'] = t('<a href="@doc">Language</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#language'));
|
||||
$fields['theme'] = t('<a href="@doc">Default theme</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#theme'));
|
||||
$fields['init'] = t('<a href="@doc">Init</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#init'));
|
||||
$fields['data'] = t('<a href="@doc">Data</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#init'));
|
||||
$fields['is_new'] = t('Option: <a href="@doc">Indicates a new user with the specified uid should be created</a>',
|
||||
array('@doc' => 'http://drupal.org/node/1349632#is_new'));
|
||||
array('@doc' => 'http://drupal.org/node/1349632#is_new'));
|
||||
|
||||
// Then add in anything provided by handlers
|
||||
$fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType, $this->bundle, $migration);
|
||||
@@ -139,9 +141,11 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
* Import a single user.
|
||||
*
|
||||
* @param $account
|
||||
* Account object to build. Prefilled with any fields mapped in the Migration.
|
||||
* Account object to build. Prefilled with any fields mapped in the
|
||||
* Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields (uid only in this case) of the user that was saved if
|
||||
* successful. FALSE on failure.
|
||||
@@ -155,7 +159,10 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
if (isset($account->uid)) {
|
||||
if ($account->uid != $row->migrate_map_destid1) {
|
||||
throw new MigrateException(t("Incoming uid !uid and map destination uid !destid1 don't match",
|
||||
array('!uid' => $account->uid, '!destid1' => $row->migrate_map_destid1)));
|
||||
array(
|
||||
'!uid' => $account->uid,
|
||||
'!destid1' => $row->migrate_map_destid1,
|
||||
)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -195,7 +202,7 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
}
|
||||
$account->roles = drupal_map_assoc($account->roles);
|
||||
}
|
||||
if (empty($account->roles) && empty($old_account->roles)) {
|
||||
if (empty($account->roles) && empty($old_account->roles)) {
|
||||
$account->roles = array();
|
||||
}
|
||||
|
||||
@@ -230,8 +237,11 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
$account->login = MigrationBase::timestamp($account->login);
|
||||
}
|
||||
|
||||
// Validate field data prior to saving.
|
||||
MigrateDestinationEntity::fieldAttachValidate('user', $account);
|
||||
|
||||
migrate_instrument_start('user_save');
|
||||
$newaccount = user_save($old_account, (array)$account);
|
||||
$newaccount = user_save($old_account, (array) $account);
|
||||
migrate_instrument_stop('user_save');
|
||||
if ($newaccount) {
|
||||
if ($this->md5Passwords && !empty($account->pass)) {
|
||||
@@ -262,6 +272,14 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
}
|
||||
else {
|
||||
$this->numCreated++;
|
||||
// user_save() doesn't update file_usage on account creation, we have
|
||||
// to do it ourselves.
|
||||
if (!empty($newaccount->picture)) {
|
||||
$file = file_load($newaccount->picture);
|
||||
if (is_object($file)) {
|
||||
file_usage_add($file, 'user', 'user', $newaccount->uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->complete($newaccount, $row);
|
||||
$return = array($newaccount->uid);
|
||||
@@ -274,6 +292,7 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
|
||||
}
|
||||
|
||||
class MigrateDestinationRole extends MigrateDestinationTable {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct('role');
|
||||
}
|
||||
@@ -298,7 +317,7 @@ class MigrateDestinationRole extends MigrateDestinationTable {
|
||||
public function rollback(array $id) {
|
||||
migrate_instrument_start('role rollback');
|
||||
$rid = reset($id);
|
||||
user_role_delete((int)$rid);
|
||||
user_role_delete((int) $rid);
|
||||
migrate_instrument_stop('role rollback');
|
||||
}
|
||||
|
||||
@@ -309,6 +328,7 @@ class MigrateDestinationRole extends MigrateDestinationTable {
|
||||
* Object object to build. Prefilled with any fields mapped in the Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields of the object that was saved if
|
||||
* successful. FALSE on failure.
|
||||
@@ -322,12 +342,15 @@ class MigrateDestinationRole extends MigrateDestinationTable {
|
||||
if (isset($entity->rid)) {
|
||||
if ($entity->rid != $row->migrate_map_destid1) {
|
||||
throw new MigrateException(t("Incoming id !id and map destination id !destid don't match",
|
||||
array('!id' => $entity->rid, '!destid' => $row->migrate_map_destid1)));
|
||||
}
|
||||
else {
|
||||
$entity->rid = $row->migrate_map_destid1;
|
||||
array(
|
||||
'!id' => $entity->rid,
|
||||
'!destid' => $row->migrate_map_destid1,
|
||||
)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$entity->rid = $row->migrate_map_destid1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
|
||||
@@ -339,7 +362,7 @@ class MigrateDestinationRole extends MigrateDestinationTable {
|
||||
$old_entity = user_role_load($entity->rid);
|
||||
|
||||
foreach ($entity as $field => $value) {
|
||||
$old_entity->$field = $entity->$field;
|
||||
$old_entity->{$field} = $entity->{$field};
|
||||
}
|
||||
$entity = $old_entity;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Support for variable destinations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Destination class implementing migration into {variable}.
|
||||
*/
|
||||
class MigrateDestinationVariable extends MigrateDestination {
|
||||
|
||||
static public function getKeySchema() {
|
||||
return array(
|
||||
'name' => array(
|
||||
'description' => 'The name of the variable.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
$output = t('Variable');
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fields available to be mapped for variables.
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
*/
|
||||
public function fields($migration = NULL) {
|
||||
$fields = array(
|
||||
'name' => t('The name of the variable.'),
|
||||
'value' => t('The value of the variable.'),
|
||||
);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single row.
|
||||
*
|
||||
* @param $variable
|
||||
* Variable object to build. Prefilled with any fields mapped in the
|
||||
* Migration.
|
||||
* @param $row
|
||||
* Raw source data object - passed through to prepare/complete handlers.
|
||||
*
|
||||
* @return array
|
||||
* Array of key fields of the object that was saved if
|
||||
* successful. FALSE on failure.
|
||||
*/
|
||||
public function import(stdClass $variable, stdClass $row) {
|
||||
// Invoke migration prepare handlers
|
||||
$this->prepare($variable, $row);
|
||||
|
||||
// Check to see if this is a new variable.
|
||||
$update = FALSE;
|
||||
// We cannot just check against NULL because a variable might actually be
|
||||
// set to NULL. Attempt to use a unique variable default value that nothing
|
||||
// else would use.
|
||||
$default = 'migrate:' . REQUEST_TIME . ':' . drupal_random_key();
|
||||
if (variable_get($variable->name, $default) !== $default) {
|
||||
$update = TRUE;
|
||||
}
|
||||
|
||||
// variable_set() provides no return callback, so we can't really test this
|
||||
// without running a variable_get() check.
|
||||
migrate_instrument_start('variable_set');
|
||||
variable_set($variable->name, $variable->value);
|
||||
migrate_instrument_stop('variable_set');
|
||||
|
||||
// Return the new id or FALSE on failure.
|
||||
if (variable_get($variable->name, $default) === $variable->value) {
|
||||
// Increment the count if the save succeeded.
|
||||
if ($update) {
|
||||
$this->numUpdated++;
|
||||
}
|
||||
else {
|
||||
$this->numCreated++;
|
||||
}
|
||||
// Return the primary key to the mapping table.
|
||||
$return = array($variable->name);
|
||||
}
|
||||
else {
|
||||
$return = FALSE;
|
||||
}
|
||||
|
||||
// Invoke migration complete handlers.
|
||||
$this->complete($variable, $row);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateDestination::prepare().
|
||||
*/
|
||||
public function prepare($variable, stdClass $row) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
$variable->migrate = array(
|
||||
'machineName' => $migration->getMachineName(),
|
||||
);
|
||||
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('variable', 'prepare', $variable, $row);
|
||||
// Then call any prepare handler for this specific Migration.
|
||||
if (method_exists($migration, 'prepare')) {
|
||||
$migration->prepare($variable, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateDestination::complete().
|
||||
*/
|
||||
public function complete($variable, stdClass $row) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
$variable->migrate = array(
|
||||
'machineName' => $migration->getMachineName(),
|
||||
);
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('variable', 'complete', $variable, $row);
|
||||
// Then call any complete handler for this specific Migration.
|
||||
if (method_exists($migration, 'complete')) {
|
||||
$migration->complete($variable, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single variable.
|
||||
*
|
||||
* @param $id
|
||||
* Array of fields representing the key (in this case, just variable name).
|
||||
*/
|
||||
public function rollback(array $id) {
|
||||
$name = reset($id);
|
||||
migrate_instrument_start('variable_delete');
|
||||
$this->prepareRollback($name);
|
||||
variable_del($name);
|
||||
$this->completeRollback($name);
|
||||
migrate_instrument_stop('variable_delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Give handlers a shot at cleaning up before a variable has been rolled back.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the variable about to be deleted.
|
||||
*/
|
||||
public function prepareRollback($name) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('variable', 'prepareRollback', $name);
|
||||
// Then call any complete handler for this specific Migration.
|
||||
if (method_exists($migration, 'prepareRollback')) {
|
||||
$migration->prepareRollback($name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Give handlers a shot at cleaning up after a variable has been rolled back.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the variable which has been deleted.
|
||||
*/
|
||||
public function completeRollback($name) {
|
||||
// We do nothing here but allow child classes to act.
|
||||
$migration = Migration::currentMigration();
|
||||
// Call any general handlers.
|
||||
migrate_handler_invoke_all('variable', 'completeRollback', $name);
|
||||
// Then call any complete handler for this specific Migration.
|
||||
if (method_exists($migration, 'completeRollback')) {
|
||||
$migration->completeRollback($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,213 @@
|
||||
* Define a MigrateSource for importing from comma separated values files.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of MigrateList, for retrieving a list of IDs to be migrated
|
||||
* from a CSV file.
|
||||
*/
|
||||
class MigrateListCSV extends MigrateList {
|
||||
|
||||
/**
|
||||
* The path to the CSV file containing a list of IDs to be processed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $file;
|
||||
|
||||
/**
|
||||
* File handle for the CSV file being iterated.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $csvHandle = NULL;
|
||||
|
||||
/**
|
||||
* The column which contains the ID.
|
||||
*
|
||||
* If numeric, this is the index of the column from 0; if non-numeric, this
|
||||
* is the column name as given by the header row.
|
||||
*/
|
||||
protected $idColumn;
|
||||
|
||||
/**
|
||||
* The number of rows in the CSV file before the data starts.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $headerRows = 0;
|
||||
|
||||
/**
|
||||
* The array keys taken from the first row.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = array();
|
||||
|
||||
/**
|
||||
* Parameters for the fgetcsv() call.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fgetcsv = array();
|
||||
|
||||
/**
|
||||
* Constructor for MigrateListCSV.
|
||||
*
|
||||
* @param $list_path
|
||||
* The path to the CSV file that holds the list of IDs.
|
||||
* @param $id_column
|
||||
* The column in the file that holds the IDs. A numeric index for the column,
|
||||
* from 0.
|
||||
* @param $options = array()
|
||||
* An array of further options for the CSV file.
|
||||
*/
|
||||
public function __construct($list_path, $id_column, $options = array()) {
|
||||
parent::__construct();
|
||||
$this->file = $list_path;
|
||||
$this->idColumn = $id_column;
|
||||
$this->options = $options;
|
||||
|
||||
// fgetcsv specific options
|
||||
foreach (array(
|
||||
'length' => NULL,
|
||||
'delimiter' => ',',
|
||||
'enclosure' => '"',
|
||||
'escape' => '\\',
|
||||
) as $key => $default) {
|
||||
$this->fgetcsv[$key] = isset($options[$key]) ? $options[$key] : $default;
|
||||
}
|
||||
|
||||
if (!empty($options['header_rows'])) {
|
||||
$this->headerRows = $options['header_rows'];
|
||||
}
|
||||
else {
|
||||
$this->headerRows = 0;
|
||||
}
|
||||
|
||||
if (!is_numeric($id_column)) {
|
||||
//we need to store the headers.
|
||||
$this->csvHandle = fopen($this->file, 'r');
|
||||
if (!$this->validResource()) {
|
||||
return;
|
||||
}
|
||||
$this->headers = $this->getNextLine();
|
||||
fclose($this->csvHandle);
|
||||
$this->csvHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representing the source file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of IDs from the CSV file.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIdList() {
|
||||
$ids = array();
|
||||
|
||||
$this->csvHandle = fopen($this->file, 'r');
|
||||
if (!$this->validResource()) {
|
||||
return $ids;
|
||||
}
|
||||
|
||||
// Skip the headers if any,
|
||||
for ($i = 0; $i < $this->headerRows; $i++) {
|
||||
$this->getNextLine();
|
||||
}
|
||||
|
||||
while ($row = $this->getNextLine()) {
|
||||
$ids[] = $row[$this->idColumn];
|
||||
}
|
||||
|
||||
fclose($this->csvHandle);
|
||||
$this->csvHandle = NULL;
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a count of all available IDs from the source listing.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function computeCount() {
|
||||
// If the data may have embedded newlines, the file line count won't reflect
|
||||
// the number of CSV records (one record will span multiple lines). We need
|
||||
// to scan with fgetcsv to get the true count.
|
||||
if (!empty($this->options['embedded_newlines'])) {
|
||||
$this->csvHandle = fopen($this->file, 'r');
|
||||
$count = 0;
|
||||
if (!$this->validResource()) {
|
||||
return $count;
|
||||
}
|
||||
// Skip all but the last header
|
||||
for ($i = 0; $i < $this->headerRows; $i++) {
|
||||
fgets($this->csvHandle);
|
||||
}
|
||||
while ($this->getNextLine()) {
|
||||
$count++;
|
||||
}
|
||||
fclose($this->csvHandle);
|
||||
$this->csvHandle = NULL;
|
||||
}
|
||||
else {
|
||||
|
||||
$file = new SplFileObject($this->file, 'r');
|
||||
$file->seek(PHP_INT_MAX);
|
||||
$count = $file->key() + 1;
|
||||
$file->rewind();
|
||||
$count -= $this->headerRows;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next line of the CSV file.
|
||||
* Returns an array of the next line, keyed by headers if required
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNextLine() {
|
||||
// escape parameter was added in PHP 5.3.
|
||||
if (version_compare(phpversion(), '5.3', '<')) {
|
||||
$row = fgetcsv($this->csvHandle, $this->fgetcsv['length'],
|
||||
$this->fgetcsv['delimiter'], $this->fgetcsv['enclosure']);
|
||||
}
|
||||
else {
|
||||
$row = fgetcsv($this->csvHandle, $this->fgetcsv['length'],
|
||||
$this->fgetcsv['delimiter'], $this->fgetcsv['enclosure'],
|
||||
$this->fgetcsv['escape']);
|
||||
}
|
||||
// Key the array with the headers
|
||||
if (!empty($this->headers) && $row) {
|
||||
$row = array_combine($this->headers, $row);
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if resource loaded correctly.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validResource() {
|
||||
if (!$this->csvHandle) {
|
||||
Migration::displayMessage(t('Could not open CSV file !url',
|
||||
array('!url' => $this->file)));
|
||||
}
|
||||
return (bool) $this->csvHandle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource, to handle imports from CSV files.
|
||||
*
|
||||
@@ -12,6 +219,7 @@
|
||||
* UTF BOM (Byte Order Marker) so they are interpreted correctly.
|
||||
*/
|
||||
class MigrateSourceCSV extends MigrateSource {
|
||||
|
||||
/**
|
||||
* List of available source fields.
|
||||
*
|
||||
@@ -47,6 +255,13 @@ class MigrateSourceCSV extends MigrateSource {
|
||||
*/
|
||||
protected $rowNumber;
|
||||
|
||||
/**
|
||||
* The path to the source file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $file;
|
||||
|
||||
/**
|
||||
* Simple initialization.
|
||||
*
|
||||
@@ -73,7 +288,12 @@ class MigrateSourceCSV extends MigrateSource {
|
||||
$this->options = $options;
|
||||
$this->fields = $fields;
|
||||
// fgetcsv specific options
|
||||
foreach (array('length' => NULL, 'delimiter' => ',', 'enclosure' => '"', 'escape' => '\\') as $key => $default) {
|
||||
foreach (array(
|
||||
'length' => NULL,
|
||||
'delimiter' => ',',
|
||||
'enclosure' => '"',
|
||||
'escape' => '\\',
|
||||
) as $key => $default) {
|
||||
$this->fgetcsv[$key] = isset($options[$key]) ? $options[$key] : $default;
|
||||
}
|
||||
// One can either pass in an explicit list of column names to use, or if we have
|
||||
@@ -81,6 +301,9 @@ class MigrateSourceCSV extends MigrateSource {
|
||||
if ($this->headerRows && empty($csvcolumns)) {
|
||||
$this->csvcolumns = array();
|
||||
$this->csvHandle = fopen($this->file, 'r');
|
||||
if (!$this->validResource()) {
|
||||
return;
|
||||
}
|
||||
// Skip all but the last header
|
||||
for ($i = 0; $i < $this->headerRows - 1; $i++) {
|
||||
$this->getNextLine();
|
||||
@@ -139,11 +362,14 @@ class MigrateSourceCSV extends MigrateSource {
|
||||
// to scan with fgetcsv to get the true count.
|
||||
if (!empty($this->options['embedded_newlines'])) {
|
||||
$this->csvHandle = fopen($this->file, 'r');
|
||||
$count = 0;
|
||||
if (!$this->validResource()) {
|
||||
return $count;
|
||||
}
|
||||
// Skip all but the last header
|
||||
for ($i = 0; $i < $this->headerRows; $i++) {
|
||||
fgets($this->csvHandle);
|
||||
}
|
||||
$count = 0;
|
||||
while ($this->getNextLine()) {
|
||||
$count++;
|
||||
}
|
||||
@@ -171,6 +397,9 @@ class MigrateSourceCSV extends MigrateSource {
|
||||
}
|
||||
// Load up the first row, skipping the header(s) if necessary
|
||||
$this->csvHandle = fopen($this->file, 'r');
|
||||
if (!$this->validResource()) {
|
||||
return;
|
||||
}
|
||||
for ($i = 0; $i < $this->headerRows; $i++) {
|
||||
$this->getNextLine();
|
||||
}
|
||||
@@ -186,6 +415,8 @@ class MigrateSourceCSV extends MigrateSource {
|
||||
public function getNextRow() {
|
||||
$row = $this->getNextLine();
|
||||
if ($row) {
|
||||
// only use rows specified in $this->csvcolumns().
|
||||
$row = array_intersect_key($row, $this->csvcolumns);
|
||||
// Set meaningful keys for the columns mentioned in $this->csvcolumns().
|
||||
foreach ($this->csvcolumns as $int => $values) {
|
||||
list($key, $description) = $values;
|
||||
@@ -194,7 +425,7 @@ class MigrateSourceCSV extends MigrateSource {
|
||||
unset($row[$int]);
|
||||
}
|
||||
$row['csvrownum'] = $this->rowNumber++;
|
||||
return (object)$row;
|
||||
return (object) $row;
|
||||
}
|
||||
else {
|
||||
fclose($this->csvHandle);
|
||||
@@ -216,4 +447,18 @@ class MigrateSourceCSV extends MigrateSource {
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if resource loaded correctly.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validResource() {
|
||||
if (!$this->csvHandle) {
|
||||
Migration::displayMessage(t('Could not open CSV file !url',
|
||||
array('!url' => $this->file)));
|
||||
}
|
||||
return (bool) $this->csvHandle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Define a MigrateSource class for importing from IBM DB2 databases.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource, to handle imports from remote DB2 servers.
|
||||
*/
|
||||
class MigrateSourceDB2 extends MigrateSource {
|
||||
|
||||
/**
|
||||
* Array containing information for connecting to DB2:
|
||||
* database - Database to connect to
|
||||
* username - Username to connect as
|
||||
* password - Password for authentication
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* The active DB2 connection for this source.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
public function getConnection() {
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SQL query from which to obtain data. Is a string.
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* The statement resource from executing the query - traversed to process the
|
||||
* incoming data.
|
||||
*/
|
||||
protected $stmt;
|
||||
|
||||
/**
|
||||
* Return an options array for DB2 sources.
|
||||
*
|
||||
* @param boolean $cache_counts
|
||||
* Indicates whether to cache counts of source records.
|
||||
*/
|
||||
static public function options($cache_counts = FALSE) {
|
||||
return compact('cache_counts');
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple initialization.
|
||||
*/
|
||||
public function __construct(array $configuration, $query, $count_query,
|
||||
array $fields, array $options = array()) {
|
||||
parent::__construct($options);
|
||||
$this->query = $query;
|
||||
$this->countQuery = $count_query;
|
||||
$this->configuration = $configuration;
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representing the source query.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect lazily to the DB server.
|
||||
*/
|
||||
protected function connect() {
|
||||
if (!isset($this->connection)) {
|
||||
// Check for the ibm_db2 extension before attempting to connect with it.
|
||||
if (!extension_loaded('ibm_db2')) {
|
||||
throw new Exception(t('You must configure the ibm_db2 extension in PHP.'));
|
||||
}
|
||||
// Connect to db2.
|
||||
$this->connection = db2_connect($this->configuration['database'],
|
||||
$this->configuration['username'], $this->configuration['password']);
|
||||
}
|
||||
if ($this->connection) {
|
||||
return TRUE;
|
||||
}
|
||||
// If we failed to connect, throw an exception with the connection error
|
||||
// message.
|
||||
else {
|
||||
$e = db2_conn_errormsg();
|
||||
throw new Exception($e);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fields available to be mapped from the source query.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
*/
|
||||
public function fields() {
|
||||
// The fields are passed to the constructor for this plugin.
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a count of all available source records.
|
||||
*/
|
||||
public function computeCount() {
|
||||
migrate_instrument_start('MigrateSourceDB2 count');
|
||||
// Make sure we're connected.
|
||||
if ($this->connect()) {
|
||||
// Execute the count query.
|
||||
$stmt = db2_exec($this->connection, $this->countQuery);
|
||||
// If something went wrong, throw an exception with the error message.
|
||||
if (!$stmt) {
|
||||
$e = db2_stmt_errormsg($stmt);
|
||||
throw new Exception($e);
|
||||
}
|
||||
// Grab the first row as an array.
|
||||
$count_array = db2_fetch_array($stmt);
|
||||
// The first item in this array will be our count.
|
||||
$count = reset($count_array);
|
||||
}
|
||||
else {
|
||||
// Connection failed.
|
||||
$count = FALSE;
|
||||
}
|
||||
migrate_instrument_stop('MigrateSourceDB2 count');
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource::performRewind().
|
||||
*/
|
||||
public function performRewind() {
|
||||
migrate_instrument_start('db2_query');
|
||||
// Ensure we're connected to the database.
|
||||
$this->connect();
|
||||
// Execute the query.
|
||||
$this->stmt = db2_exec($this->connection, $this->query);
|
||||
// Throw an exception with the error message if something went wrong.
|
||||
if (!$this->stmt) {
|
||||
$e = db2_stmt_errormsg($this->stmt);
|
||||
throw new Exception($e);
|
||||
}
|
||||
migrate_instrument_stop('db2_query');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource::getNextRow().
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getNextRow() {
|
||||
return db2_fetch_object($this->stmt);
|
||||
}
|
||||
}
|
||||
@@ -23,15 +23,71 @@ define('MIGRATE_CHUNK_SEPARATOR', '-?MIGRATECHUNK?-');
|
||||
* count, and then retrieve each chunk by it's ID.
|
||||
*/
|
||||
abstract class MigrateContentParser {
|
||||
|
||||
/**
|
||||
* Tells the MigrateListFiles implementation to always use chunk IDs when
|
||||
* constructing item IDs.
|
||||
*/
|
||||
public $alwaysUseChunkIDs = FALSE;
|
||||
|
||||
/**
|
||||
* The content of the current file to parse.
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
public function setContent($content) {
|
||||
/**
|
||||
* The ID of the current file being parsed.
|
||||
*/
|
||||
protected $item_id;
|
||||
|
||||
/**
|
||||
* Set the content and file ID for the current file to parse.
|
||||
*
|
||||
* @param $content
|
||||
* The content of the current file to parse.
|
||||
* @param $item_id = NULL
|
||||
* The ID of the file. The chunk IDs are appended to this to form the IDs
|
||||
* that are returned by the MigrateList implementation.
|
||||
*/
|
||||
public function setContent($content, $item_id = NULL) {
|
||||
$this->content = $content;
|
||||
$this->item_id = $item_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of IDs for all the chunks found in the current file.
|
||||
*
|
||||
* Unless $alwaysUseChunkIDs is set to TRUE, this is only called if
|
||||
* getChunkCount() returned a count greater than 1.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* An array of IDs.
|
||||
*/
|
||||
abstract public function getChunkIDs();
|
||||
|
||||
/**
|
||||
* Get a single chunk from the current file.
|
||||
*
|
||||
* @param $id
|
||||
* The ID for the chunk. This is one of the IDs returned by getChunkIDs().
|
||||
*
|
||||
* @return
|
||||
* The content of the chunk.
|
||||
*/
|
||||
abstract public function getChunk($id);
|
||||
|
||||
/**
|
||||
* Return the count of chunks in the current file.
|
||||
*
|
||||
* If this returns 1, then getChunkIDs() is not called and the file is treated
|
||||
* as a single chunk. To override this, set $alwaysUseChunkIDs to TRUE.
|
||||
*
|
||||
* @return int
|
||||
* The number of chunks in the current file.
|
||||
*/
|
||||
abstract public function getChunkCount();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,12 +96,15 @@ abstract class MigrateContentParser {
|
||||
* primarily for when your HTML files map one-to-one to nodes.
|
||||
*/
|
||||
class MigrateSimpleContentParser extends MigrateContentParser {
|
||||
|
||||
public function getChunkIDs() {
|
||||
return array('1');
|
||||
}
|
||||
|
||||
public function getChunk($id) {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function getChunkCount() {
|
||||
return 1;
|
||||
}
|
||||
@@ -54,15 +113,24 @@ class MigrateSimpleContentParser extends MigrateContentParser {
|
||||
/**
|
||||
* Implementation of MigrateList, for retrieving a list of IDs to be migrated
|
||||
* from a directory listing. Each item is a file, it's ID is the path.
|
||||
*
|
||||
* If individual files contain more than one source, use a MigrateContentParser
|
||||
* to parse the file and split it into chunks.
|
||||
*/
|
||||
class MigrateListFiles extends MigrateList {
|
||||
|
||||
protected $listDirs;
|
||||
|
||||
protected $baseDir;
|
||||
|
||||
protected $fileMask;
|
||||
|
||||
protected $directoryOptions;
|
||||
|
||||
protected $parser;
|
||||
|
||||
protected $getContents;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -76,18 +144,25 @@ class MigrateListFiles extends MigrateList {
|
||||
* );
|
||||
* @param $base_dir
|
||||
* The base dir is the part of the path that will be excluded when making
|
||||
* an ID for each file. To continue the example from above, you want base_dir
|
||||
* to be = '/var/html_source', so that the files will have IDs in the format
|
||||
* an ID for each file. To continue the example from above, you want
|
||||
* base_dir to be = '/var/html_source', so that the files will have IDs in
|
||||
* the format
|
||||
* '/en/news/news_2011_03_4.html'.
|
||||
* @param $file_mask
|
||||
* Passed on and used to filter for certain types of files. Use a regular
|
||||
* expression, for example '/(.*\.htm$|.*\.html$)/i' to match all .htm and
|
||||
* .html files, case insensitive.
|
||||
* (optional) Passed on and used to filter for certain types of files. Use
|
||||
* a
|
||||
* regular expression, for example '/(.*\.htm$|.*\.html$)/i' to match all
|
||||
* .htm and .html files, case insensitive. Defaults to matching all files.
|
||||
* @param $options
|
||||
* Options that will be passed on to file_scan_directory(). See docs of that
|
||||
* Options that will be passed on to file_scan_directory(). See docs of
|
||||
* that
|
||||
* core Drupal function for more information.
|
||||
* @param MigrateContentParser $parser
|
||||
* Content parser class to use.
|
||||
* @param $get_contents
|
||||
* Whether to load the contents of files.
|
||||
*/
|
||||
public function __construct($list_dirs, $base_dir, $file_mask = NULL, $options = array(), MigrateContentParser $parser = NULL) {
|
||||
public function __construct($list_dirs, $base_dir, $file_mask = '//', $options = array(), MigrateContentParser $parser = NULL, $get_contents = TRUE) {
|
||||
if (!$parser) {
|
||||
$parser = new MigrateSimpleContentParser();
|
||||
}
|
||||
@@ -97,6 +172,7 @@ class MigrateListFiles extends MigrateList {
|
||||
$this->fileMask = $file_mask;
|
||||
$this->directoryOptions = $options;
|
||||
$this->parser = $parser;
|
||||
$this->getContents = $get_contents;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,15 +212,21 @@ class MigrateListFiles extends MigrateList {
|
||||
protected function getIDsFromFiles(array $files) {
|
||||
$ids = array();
|
||||
foreach ($files as $file) {
|
||||
$contents = file_get_contents($file->uri);
|
||||
$this->parser->setContent($contents);
|
||||
if ($this->parser->getChunkCount() > 1) {
|
||||
foreach ($this->parser->getChunkIDs() as $chunk_id) {
|
||||
$ids[] = str_replace($this->baseDir, '', (string) $file->uri) . MIGRATE_CHUNK_SEPARATOR . $chunk_id;
|
||||
$file_base_id = str_replace($this->baseDir, '', (string) $file->uri);
|
||||
if ($this->getContents) {
|
||||
$contents = file_get_contents($file->uri);
|
||||
$this->parser->setContent($contents, $file_base_id);
|
||||
if ($this->parser->alwaysUseChunkIDs || $this->parser->getChunkCount() > 1) {
|
||||
foreach ($this->parser->getChunkIDs() as $chunk_id) {
|
||||
$ids[] = $file_base_id . MIGRATE_CHUNK_SEPARATOR . $chunk_id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$ids[] = $file_base_id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$ids[] = str_replace($this->baseDir, '', (string) $file->uri);
|
||||
$ids[] = $file_base_id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -172,7 +254,9 @@ class MigrateListFiles extends MigrateList {
|
||||
class MigrateItemFile extends MigrateItem {
|
||||
|
||||
protected $baseDir;
|
||||
|
||||
protected $getContents;
|
||||
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
@@ -204,28 +288,30 @@ class MigrateItemFile extends MigrateItem {
|
||||
* The item object for migration.
|
||||
*/
|
||||
public function getItem($id) {
|
||||
$pieces = explode(MIGRATE_CHUNK_SEPARATOR ,$id);
|
||||
$pieces = explode(MIGRATE_CHUNK_SEPARATOR, $id);
|
||||
$item_uri = $this->baseDir . $pieces[0];
|
||||
$chunk = $pieces[1];
|
||||
$chunk = !empty($pieces[1]) ? $pieces[1] : '';
|
||||
|
||||
// Get the file data at the specified URI
|
||||
$data = $this->loadFile($item_uri);
|
||||
if (is_string($data)) {
|
||||
$this->parser->setContent($data);
|
||||
$this->parser->setContent($data, $pieces[0]);
|
||||
$return = new stdClass;
|
||||
$return->filedata = $this->parser->getChunk($chunk);
|
||||
return $return;
|
||||
}
|
||||
else if ($data === TRUE) {
|
||||
$return = new stdClass;
|
||||
return $return;
|
||||
}
|
||||
else {
|
||||
$migration = Migration::currentMigration();
|
||||
$message = t('Loading of !objecturi failed:', array('!objecturi' => $item_uri));
|
||||
$migration->getMap()->saveMessage(
|
||||
array($id), $message, MigrationBase::MESSAGE_ERROR);
|
||||
return NULL;
|
||||
if ($data === TRUE) {
|
||||
$return = new stdClass;
|
||||
return $return;
|
||||
}
|
||||
else {
|
||||
$migration = Migration::currentMigration();
|
||||
$message = t('Loading of !objecturi failed:', array('!objecturi' => $item_uri));
|
||||
$migration->getMap()->saveMessage(
|
||||
array($id), $message, MigrationBase::MESSAGE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,4 +328,4 @@ class MigrateItemFile extends MigrateItem {
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* from a JSON object.
|
||||
*/
|
||||
class MigrateListJSON extends MigrateList {
|
||||
|
||||
/**
|
||||
* A URL pointing to an JSON object containing a list of IDs to be processed.
|
||||
*
|
||||
@@ -35,7 +36,8 @@ class MigrateListJSON extends MigrateList {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the JSON at the given URL, and return an array of the IDs found within it.
|
||||
* Load the JSON at the given URL, and return an array of the IDs found
|
||||
* within it.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -51,12 +53,12 @@ class MigrateListJSON extends MigrateList {
|
||||
migrate_instrument_stop("Retrieve $this->listUrl");
|
||||
if ($json) {
|
||||
$data = drupal_json_decode($json);
|
||||
if ($data) {
|
||||
if ($data !== NULL) {
|
||||
return $this->getIDsFromJSON($data);
|
||||
}
|
||||
}
|
||||
Migration::displayMessage(t('Loading of !listurl failed:',
|
||||
array('!listurl' => $this->listUrl)));
|
||||
array('!listurl' => $this->listUrl)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -92,7 +94,7 @@ class MigrateListJSON extends MigrateList {
|
||||
if ($json) {
|
||||
$data = drupal_json_decode($json);
|
||||
if ($data) {
|
||||
$count = count($data);
|
||||
$count = count($this->getIDsFromJSON($data));
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
@@ -104,6 +106,7 @@ class MigrateListJSON extends MigrateList {
|
||||
* an ID provided by a MigrateList class.
|
||||
*/
|
||||
class MigrateItemJSON extends MigrateItem {
|
||||
|
||||
/**
|
||||
* A URL pointing to a JSON object containing the data for one item to be
|
||||
* migrated.
|
||||
@@ -136,7 +139,7 @@ class MigrateItemJSON extends MigrateItem {
|
||||
}
|
||||
else {
|
||||
$migration = Migration::currentMigration();
|
||||
$message = t('Loading of !objecturl failed:', array('!objecturl' => $item_url));
|
||||
$message = t('Loading of !objecturl failed:', array('!objecturl' => $item_url));
|
||||
$migration->getMap()->saveMessage(
|
||||
array($id), $message, MigrationBase::MESSAGE_ERROR);
|
||||
return NULL;
|
||||
@@ -178,6 +181,7 @@ class MigrateItemJSON extends MigrateItem {
|
||||
* To deal with different structures, extend this class and override next().
|
||||
*/
|
||||
class MigrateJSONReader implements Iterator {
|
||||
|
||||
/**
|
||||
* URL of the source JSON file.
|
||||
*
|
||||
@@ -242,7 +246,8 @@ class MigrateJSONReader implements Iterator {
|
||||
* The next non-whitespace character, or FALSE on end-of-file.
|
||||
*/
|
||||
protected function getNonBlank() {
|
||||
while (($c = fgetc($this->fileHandle)) !== FALSE && trim($c) == '') {}
|
||||
while (($c = fgetc($this->fileHandle)) !== FALSE && trim($c) == '') {
|
||||
}
|
||||
return $c;
|
||||
}
|
||||
|
||||
@@ -266,7 +271,7 @@ class MigrateJSONReader implements Iterator {
|
||||
$this->fileHandle = fopen($this->url, 'r');
|
||||
if (!$this->fileHandle) {
|
||||
Migration::displayMessage(t('Could not open JSON file !url',
|
||||
array('!url' => $this->url)));
|
||||
array('!url' => $this->url)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -278,7 +283,7 @@ class MigrateJSONReader implements Iterator {
|
||||
}
|
||||
if ($char != '[') {
|
||||
Migration::displayMessage(t('!url is not a JSON file containing an array of objects',
|
||||
array('!url' => $this->url)));
|
||||
array('!url' => $this->url)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -390,9 +395,11 @@ class MigrateJSONReader implements Iterator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource, to handle imports from stand-alone JSON files.
|
||||
* Implementation of MigrateSource, to handle imports from stand-alone JSON
|
||||
* files.
|
||||
*/
|
||||
class MigrateSourceJSON extends MigrateSource {
|
||||
|
||||
/**
|
||||
* The MigrateJSONReader object serving as a cursor over the JSON source file.
|
||||
*
|
||||
@@ -402,6 +409,7 @@ class MigrateSourceJSON extends MigrateSource {
|
||||
|
||||
/**
|
||||
* Name of the field within the JSON object holding the ID value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $idField;
|
||||
@@ -454,7 +462,7 @@ class MigrateSourceJSON extends MigrateSource {
|
||||
* MigrateJSONReader).
|
||||
*/
|
||||
public function __construct($urls, $id_field, array $fields = array(),
|
||||
array $options = array()) {
|
||||
array $options = array()) {
|
||||
parent::__construct($options);
|
||||
$this->idField = $id_field;
|
||||
if (empty($options['reader_class'])) {
|
||||
@@ -503,7 +511,7 @@ class MigrateSourceJSON extends MigrateSource {
|
||||
* @return string
|
||||
*/
|
||||
public function activeUrl() {
|
||||
if ($this->activeUrl) {
|
||||
if (isset($this->activeUrl)) {
|
||||
return $this->sourceUrls[$this->activeUrl];
|
||||
}
|
||||
}
|
||||
@@ -581,7 +589,7 @@ class MigrateSourceJSON extends MigrateSource {
|
||||
// Return value
|
||||
$status = FALSE;
|
||||
|
||||
while ($this->activeUrl === NULL || (count($this->sourceUrls)-1) > $this->activeUrl) {
|
||||
while ($this->activeUrl === NULL || (count($this->sourceUrls) - 1) > $this->activeUrl) {
|
||||
if (is_null($this->activeUrl)) {
|
||||
$this->activeUrl = 0;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
* obtain a list of IDs from an XML document).
|
||||
*/
|
||||
abstract class MigrateList {
|
||||
public function __construct() {}
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementors are expected to return a string representing where the listing
|
||||
@@ -34,7 +36,8 @@ abstract class MigrateList {
|
||||
abstract public function getIdList();
|
||||
|
||||
/**
|
||||
* Implementors are expected to return a count of IDs available to be migrated.
|
||||
* Implementors are expected to return a count of IDs available to be
|
||||
* migrated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -46,7 +49,9 @@ abstract class MigrateList {
|
||||
* given migratable item given its ID as provided by the MigrateList class.
|
||||
*/
|
||||
abstract class MigrateItem {
|
||||
public function __construct() {}
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementors are expected to return an object representing a source item.
|
||||
@@ -56,6 +61,17 @@ abstract class MigrateItem {
|
||||
* @return stdClass
|
||||
*/
|
||||
abstract public function getItem($id);
|
||||
|
||||
/**
|
||||
* Implementors may optionally implement a hash function, applied to the
|
||||
* entire source row, if this particular item type makes it difficult to
|
||||
* do on the raw row.
|
||||
*
|
||||
* @param $row
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
//abstract public function hash($row);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +79,7 @@ abstract class MigrateItem {
|
||||
* IDs provided by a MigrateList and retrieving data from a MigrateItem.
|
||||
*/
|
||||
class MigrateSourceList extends MigrateSource {
|
||||
|
||||
/**
|
||||
* MigrateList object used to obtain ID lists.
|
||||
*
|
||||
@@ -94,8 +111,7 @@ class MigrateSourceList extends MigrateSource {
|
||||
/**
|
||||
* Simple initialization.
|
||||
*/
|
||||
public function __construct(MigrateList $list_class, MigrateItem $item_class, $fields = array(),
|
||||
$options = array()) {
|
||||
public function __construct(MigrateList $list_class, MigrateItem $item_class, $fields = array(), $options = array()) {
|
||||
parent::__construct($options);
|
||||
$this->listClass = $list_class;
|
||||
$this->itemClass = $item_class;
|
||||
@@ -111,6 +127,15 @@ class MigrateSourceList extends MigrateSource {
|
||||
return (string) $this->listClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return MigrateList object used to obtain ID lists.
|
||||
*
|
||||
* @return MigrateList
|
||||
*/
|
||||
public function listClass() {
|
||||
return $this->listClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fields available to be mapped from the source query.
|
||||
* Since we can't reliably figure out what "fields" are in the source,
|
||||
@@ -184,12 +209,27 @@ class MigrateSourceList extends MigrateSource {
|
||||
foreach (array_keys($this->activeMap->getSourceKey()) as $key_name) {
|
||||
// Grab the first id and advance the array cursor. Then save the ID
|
||||
// using the map source key - it will be used for mapping.
|
||||
list(, $id) = each($ids);
|
||||
$row->$key_name = $id;
|
||||
$id = current($ids);
|
||||
next($ids);
|
||||
$row->{$key_name} = $id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides MigrateSource::hash().
|
||||
*/
|
||||
protected function hash($row) {
|
||||
// Let the item class override the default hash function.
|
||||
if (method_exists($this->itemClass, 'hash')) {
|
||||
$hash = $this->itemClass->hash($row);
|
||||
}
|
||||
else {
|
||||
$hash = parent::hash($row);
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Define a MigrateSource for importing from MongoDB connections
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource, to handle imports from MongoDB connections.
|
||||
*/
|
||||
class MigrateSourceMongoDB extends MigrateSource {
|
||||
|
||||
/**
|
||||
* The mongodb collection object.
|
||||
*
|
||||
* @var MongoCollection
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
/**
|
||||
* The mongodb cursor object.
|
||||
*
|
||||
* @var MongoCursor
|
||||
*/
|
||||
protected $cursor;
|
||||
|
||||
/**
|
||||
* The mongodb query.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* List of available source fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = array();
|
||||
|
||||
/**
|
||||
* Simple initialization.
|
||||
*/
|
||||
public function __construct(MongoCollection $collection, array $query,
|
||||
array $fields = array(), array $sort = array('_id' => 1),
|
||||
array $options = array()) {
|
||||
parent::__construct($options);
|
||||
|
||||
$this->collection = $collection;
|
||||
$this->query = $query;
|
||||
$this->sort = $sort;
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fields available to be mapped from the source query.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping)
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
*/
|
||||
public function fields() {
|
||||
// The fields are passed to the constructor for this plugin.
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a count of all available source records.
|
||||
*/
|
||||
public function computeCount() {
|
||||
return $this->cursor->count(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource::getNextRow().
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getNextRow() {
|
||||
$row = $this->cursor->getNext();
|
||||
|
||||
if ($row) {
|
||||
return (object) $row;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource::performRewind().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function performRewind() {
|
||||
$keys = $this->getSourceKeyNameAndType();
|
||||
|
||||
// If we have an existing idlist we use it.
|
||||
if ($this->idList) {
|
||||
foreach ($this->idList as $key => $id) {
|
||||
// Try make new ObjectID.
|
||||
$this->idList[$key] = $this->getMongoId($id, $keys);
|
||||
}
|
||||
|
||||
$this->query[$keys[0]['name']]['$in'] = $this->idList;
|
||||
}
|
||||
|
||||
migrate_instrument_start('MigrateSourceMongoDB execute');
|
||||
try {
|
||||
$this->cursor = $this->collection
|
||||
->find($this->query)
|
||||
->sort($this->sort);
|
||||
$this->cursor->timeout(-1);
|
||||
} catch (MongoCursorException $e) {
|
||||
Migration::displayMessage($e->getMessage(), 'error');
|
||||
}
|
||||
migrate_instrument_stop('MigrateSourceMongoDB execute');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representing the source query.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
if (is_null($this->cursor)) {
|
||||
$this->cursor = $this->collection
|
||||
->find($this->query)
|
||||
->sort($this->sort);
|
||||
$this->cursor->timeout(-1);
|
||||
}
|
||||
|
||||
$query_info = $this->cursor->info();
|
||||
|
||||
$query = 'query: ' . drupal_json_encode($query_info['query']['$query']);
|
||||
$sort = 'order by: ' . drupal_json_encode($query_info['query']['$orderby']);
|
||||
$fields = 'fields: ' . drupal_json_encode($query_info['fields']);
|
||||
|
||||
return $query . PHP_EOL .
|
||||
$sort . PHP_EOL .
|
||||
$fields . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given document id is a mongo ObjectId and return mongo ObjectId
|
||||
* or simple value.
|
||||
*
|
||||
* @param mixed $document_id
|
||||
* Document key value.
|
||||
* @param array $keys
|
||||
* List of keys.
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public function getMongoId($document_id, $keys) {
|
||||
if ($keys[0]['name'] != '_id') {
|
||||
switch ($keys[0]['type']) {
|
||||
case 'int':
|
||||
return (int) $document_id;
|
||||
break;
|
||||
default:
|
||||
return $document_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Trying create Mongo ObjectId
|
||||
$mongoid = new MongoId($document_id);
|
||||
|
||||
// If (string) $mongoid == $document_id we return $mongoid object
|
||||
if ((string) $mongoid == $document_id) {
|
||||
return $mongoid;
|
||||
}
|
||||
|
||||
return $document_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source keys array.
|
||||
*/
|
||||
public function getSourceKeyNameAndType() {
|
||||
// Get the key name, and type.
|
||||
$keys = array();
|
||||
foreach ($this->activeMap->getSourceKey() as $field_name => $field_schema) {
|
||||
$keys[] = array(
|
||||
'name' => $field_name,
|
||||
'type' => $field_schema['type'],
|
||||
);
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,11 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource, to handle imports from remote MS SQL Server db servers.
|
||||
* Implementation of MigrateSource, to handle imports from remote MS SQL Server
|
||||
* db servers.
|
||||
*/
|
||||
class MigrateSourceMSSQL extends MigrateSource {
|
||||
|
||||
/**
|
||||
* Array containing information for connecting to SQL Server:
|
||||
* servername - Hostname of the SQL Server
|
||||
@@ -62,7 +64,7 @@ class MigrateSourceMSSQL extends MigrateSource {
|
||||
* Simple initialization.
|
||||
*/
|
||||
public function __construct(array $configuration, $query, $count_query,
|
||||
array $fields, array $options = array()) {
|
||||
array $fields, array $options = array()) {
|
||||
parent::__construct($options);
|
||||
$this->query = $query;
|
||||
$this->countQuery = $count_query;
|
||||
@@ -125,7 +127,8 @@ class MigrateSourceMSSQL extends MigrateSource {
|
||||
migrate_instrument_start('MigrateSourceMSSQL count');
|
||||
if ($this->connect()) {
|
||||
$result = mssql_query($this->countQuery);
|
||||
$count = reset(mssql_fetch_object($result));
|
||||
$result_array = mssql_fetch_array($result);
|
||||
$count = reset($result_array);
|
||||
}
|
||||
else {
|
||||
// Do something else?
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
* to obtain the data for a given migratable item given its ID.
|
||||
*/
|
||||
abstract class MigrateItems {
|
||||
public function __construct() {}
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementors are expected to return a string representing where the listing
|
||||
@@ -33,7 +35,8 @@ abstract class MigrateItems {
|
||||
abstract public function getIdList();
|
||||
|
||||
/**
|
||||
* Implementors are expected to return a count of IDs available to be migrated.
|
||||
* Implementors are expected to return a count of IDs available to be
|
||||
* migrated.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -47,8 +50,18 @@ abstract class MigrateItems {
|
||||
* @return stdClass
|
||||
*/
|
||||
abstract public function getItem($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementors may optionally implement a hash function, applied to the
|
||||
* entire source row, if this particular item type makes it difficult to
|
||||
* do on the raw row.
|
||||
*
|
||||
* @param $row
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
//abstract public function hash($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateItems, for providing a list of IDs and for
|
||||
@@ -60,6 +73,7 @@ abstract class MigrateItems {
|
||||
* IDs provided by a MigrateItems and retrieving data from a MigrateItems.
|
||||
*/
|
||||
class MigrateSourceMultiItems extends MigrateSource {
|
||||
|
||||
/**
|
||||
* MigrateItems object used to obtain the list of IDs and source for
|
||||
* all objects.
|
||||
@@ -127,7 +141,7 @@ class MigrateSourceMultiItems extends MigrateSource {
|
||||
* @return int
|
||||
*/
|
||||
public function computeCount() {
|
||||
// @API: Support old count method for now.
|
||||
// @API: Support old count method for now.
|
||||
if (method_exists($this->itemsClass, 'computeCount')) {
|
||||
return $this->itemsClass->computeCount();
|
||||
}
|
||||
@@ -146,8 +160,8 @@ class MigrateSourceMultiItems extends MigrateSource {
|
||||
if ($this->idList) {
|
||||
$this->idsToProcess = $this->idList;
|
||||
}
|
||||
else {
|
||||
$this->idsToProcess = $this->itemsClass->getIdList();
|
||||
elseif (!$this->idsToProcess = $this->itemsClass->getIdList()) {
|
||||
$this->idsToProcess = array();
|
||||
}
|
||||
$this->idIterator = ($this->idsToProcess instanceof Iterator) ?
|
||||
$this->idsToProcess : new ArrayIterator($this->idsToProcess);
|
||||
@@ -176,11 +190,24 @@ class MigrateSourceMultiItems extends MigrateSource {
|
||||
// Save the ID using the map source key - it will be used for mapping
|
||||
$sourceKey = $this->activeMap->getSourceKey();
|
||||
$key_name = key($sourceKey);
|
||||
$row->$key_name = $id;
|
||||
$row->{$key_name} = $id;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides MigrateSource::hash().
|
||||
*/
|
||||
protected function hash($row) {
|
||||
// Let the item class override the default hash function.
|
||||
if (method_exists($this->itemsClass, 'hash')) {
|
||||
$hash = $this->itemsClass->hash($row);
|
||||
}
|
||||
else {
|
||||
$hash = parent::hash($row);
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,17 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource, to handle imports from remote Oracle servers.
|
||||
* Implementation of MigrateSource, to handle imports from remote Oracle
|
||||
* servers.
|
||||
*/
|
||||
class MigrateSourceOracle extends MigrateSource {
|
||||
|
||||
/**
|
||||
* Array containing information for connecting to Oracle:
|
||||
* username - Username to connect as
|
||||
* password - Password for logging in
|
||||
* connection_string - See http://us.php.net/manual/en/function.oci-connect.php.
|
||||
* connection_string - See
|
||||
* http://us.php.net/manual/en/function.oci-connect.php.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@@ -25,6 +28,7 @@ class MigrateSourceOracle extends MigrateSource {
|
||||
* @var resource
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
public function getConnection() {
|
||||
return $this->connection;
|
||||
}
|
||||
@@ -63,7 +67,7 @@ class MigrateSourceOracle extends MigrateSource {
|
||||
* Simple initialization.
|
||||
*/
|
||||
public function __construct(array $configuration, $query, $count_query,
|
||||
array $fields, array $options = array()) {
|
||||
array $fields, array $options = array()) {
|
||||
parent::__construct($options);
|
||||
$this->query = $query;
|
||||
$this->countQuery = $count_query;
|
||||
@@ -206,13 +210,13 @@ class MigrateSourceOracle extends MigrateSource {
|
||||
* Implementation of MigrateSource::getNextRow().
|
||||
*
|
||||
* Returns the next row of the result set as an object, making sure NULLs are
|
||||
* represented as PHP NULLs and that LOBs are returned directly without special
|
||||
* handling.
|
||||
* represented as PHP NULLs and that LOBs are returned directly without
|
||||
* special handling.
|
||||
*/
|
||||
public function getNextRow() {
|
||||
$row = oci_fetch_array($this->result, OCI_ASSOC | OCI_RETURN_NULLS | OCI_RETURN_LOBS);
|
||||
if (!empty($row)) {
|
||||
return (object)$row;
|
||||
return (object) $row;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Define a MigrateSource for importing from spreadsheet files.
|
||||
*
|
||||
* Requires the PHPExcel library to be installed.
|
||||
* - Download PHPExcel at http://phpexcel.codeplex.com/.
|
||||
* - Extract the archive to a temporary folder.
|
||||
* - Ensure the hosting environment fulfills the requirements found in
|
||||
* install.txt.
|
||||
* - Copy the contents of the Classes folder to an appropriate location
|
||||
* (sites/all/libraries/PHPExcel).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements MigrateSource, to handle imports from XLS files.
|
||||
*/
|
||||
class MigrateSourceSpreadsheet extends MigrateSource {
|
||||
|
||||
/**
|
||||
* PHPExcel object for storing the workbook data.
|
||||
*
|
||||
* @var PHPExcel
|
||||
*/
|
||||
protected $workbook;
|
||||
|
||||
/**
|
||||
* PHPExcel object for storing the worksheet data.
|
||||
*
|
||||
* @var PHPExcel_Worksheet
|
||||
*/
|
||||
protected $worksheet;
|
||||
|
||||
/**
|
||||
* The name of the worksheet that will be processed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $sheetName;
|
||||
|
||||
/**
|
||||
* Number of rows in the worksheet that is being processed.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $rows = 0;
|
||||
|
||||
/**
|
||||
* Number of columns in the worksheet that is being processed.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $cols = 0;
|
||||
|
||||
/**
|
||||
* List of available source fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = array();
|
||||
|
||||
/**
|
||||
* The current row number in the XLS file.
|
||||
*
|
||||
* @var integer+
|
||||
*/
|
||||
protected $rowNumber;
|
||||
|
||||
/**
|
||||
* The columns to be read from Excel
|
||||
*/
|
||||
protected $columns;
|
||||
|
||||
/**
|
||||
* The first row from where the table starts. It's a "zero based" value.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headerRows = 0;
|
||||
|
||||
/**
|
||||
* Simple initialization.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to the source file.
|
||||
* @param string $sheet_name
|
||||
* The name of the sheet to be processed.
|
||||
* @param array $options
|
||||
* Options applied to this source. If the source data begins on different
|
||||
* row than the first row, pass this "zero based" value as 'header_rows' in
|
||||
* options, along with other options inherited from MigrateSource.
|
||||
*/
|
||||
public function __construct($path, $sheet_name, $columns = array(), array $options = array()) {
|
||||
parent::__construct($options);
|
||||
$this->file = $path;
|
||||
$this->sheetName = $sheet_name;
|
||||
$this->columns = $columns;
|
||||
if (!empty($options['header_rows'])) {
|
||||
$this->headerRows = $options['header_rows'];
|
||||
}
|
||||
|
||||
// Load the workbook.
|
||||
if ($this->load()) {
|
||||
// Get the dimensions of the worksheet.
|
||||
$this->rows = $this->worksheet->getHighestDataRow();
|
||||
$this->cols = PHPExcel_Cell::columnIndexFromString($this->worksheet->getHighestDataColumn());
|
||||
|
||||
// Map field names to their column index.
|
||||
for ($col = 0; $col < $this->cols; ++$col) {
|
||||
$this->fields[$col] = trim($this->worksheet->getCellByColumnAndRow($col, $this->headerRows + 1)
|
||||
->getValue());
|
||||
}
|
||||
|
||||
$this->unload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the workbook.
|
||||
*
|
||||
* @return bool
|
||||
* Returns true if the workbook was successfully loaded, otherwise false.
|
||||
*/
|
||||
public function load() {
|
||||
// Check that the file exists.
|
||||
if (!file_exists($this->file)) {
|
||||
Migration::displayMessage(t('The file !filename does not exist.', array('!filename' => $this->file)));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check that required modules are enabled.
|
||||
if (!module_exists('libraries')) {
|
||||
Migration::displayMessage(t('The Libraries API module is not enabled.'));
|
||||
return FALSE;
|
||||
}
|
||||
if (!module_exists('phpexcel')) {
|
||||
Migration::displayMessage(t('The PHPExcel module is not enabled.'));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$library = libraries_load('PHPExcel');
|
||||
if (empty($library['loaded'])) {
|
||||
Migration::displayMessage(t('The PHPExcel library could not be found.'));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Load the workbook.
|
||||
try {
|
||||
// Identify the type of the input file.
|
||||
$type = PHPExcel_IOFactory::identify($this->file);
|
||||
// Create a new Reader of the file type.
|
||||
$reader = PHPExcel_IOFactory::createReader($type);
|
||||
// Advise the Reader that we only want to load cell data.
|
||||
$reader->setReadDataOnly(TRUE);
|
||||
// Advise the Reader of which worksheet we want to load.
|
||||
$reader->setLoadSheetsOnly($this->sheetName);
|
||||
// Load the source file.
|
||||
$this->workbook = $reader->load($this->file);
|
||||
$this->worksheet = $this->workbook->getSheet();
|
||||
} catch (Exception $e) {
|
||||
Migration::displayMessage(t('Error loading file: %message', array('%message' => $e->getMessage())));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads the workbook.
|
||||
*/
|
||||
public function unload() {
|
||||
$this->workbook->disconnectWorksheets();
|
||||
unset($this->workbook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representing the source query.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fields available to be mapped from the source query.
|
||||
*
|
||||
* @return array
|
||||
* Keys: machine names of the fields (to be passed to addFieldMapping).
|
||||
* Values: Human-friendly descriptions of the fields.
|
||||
*/
|
||||
public function fields() {
|
||||
$fields = array();
|
||||
|
||||
foreach ($this->fields as $name) {
|
||||
$fields[$name] = $name;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a count of all available source records.
|
||||
*/
|
||||
public function computeCount() {
|
||||
// Subtract the non-data rows (rows before header and the header).
|
||||
return $this->rows - $this->headerRows - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements MigrateSource::performRewind().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function performRewind() {
|
||||
// Initialize the workbook if it isn't already.
|
||||
if (!isset($this->workbook)) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
$this->rowNumber = $this->headerRows + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements MigrateSource::getNextRow().
|
||||
*
|
||||
* @return null|object
|
||||
*/
|
||||
public function getNextRow() {
|
||||
migrate_instrument_start('MigrateSourceSpreadsheet::next');
|
||||
++$this->rowNumber;
|
||||
|
||||
if ($this->rowNumber <= $this->rows) {
|
||||
$row_values = array();
|
||||
for ($col = 0; $col < $this->cols; ++$col) {
|
||||
if (in_array($this->fields[$col], $this->columns) || empty($this->columns)) {
|
||||
$row_values[$this->fields[$col]] = trim($this->worksheet->getCellByColumnAndRow($col, $this->rowNumber)
|
||||
->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return (object) $row_values;
|
||||
}
|
||||
else {
|
||||
// EOF, close the workbook.
|
||||
$this->unload();
|
||||
|
||||
migrate_instrument_stop('MigrateSourceSpreadsheet::next');
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,13 @@
|
||||
* Implementation of MigrateSource, to handle imports from Drupal connections.
|
||||
*/
|
||||
class MigrateSourceSQL extends MigrateSource {
|
||||
|
||||
/**
|
||||
* The SQL query objects from which to obtain data, and counts of data
|
||||
*
|
||||
* @var SelectQueryInterface
|
||||
*/
|
||||
protected $originalQuery, $query, $countQuery;
|
||||
protected $originalQuery, $query, $countQuery, $alteredQuery;
|
||||
|
||||
/**
|
||||
* Return a reference to the base query, in particular so Migration classes
|
||||
@@ -24,7 +25,7 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
* @return SelectQueryInterface
|
||||
*/
|
||||
public function &query() {
|
||||
return $this->originalQuery();
|
||||
return $this->originalQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,6 +43,21 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
*/
|
||||
protected $numProcessed = 0;
|
||||
|
||||
/**
|
||||
* Current data batch.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $batch = 0;
|
||||
|
||||
/**
|
||||
* Number of records to fetch from the database during each batch. A value
|
||||
* of zero indicates no batching is to be done.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $batchSize = 0;
|
||||
|
||||
/**
|
||||
* List of available source fields.
|
||||
*
|
||||
@@ -57,6 +73,7 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
* @var boolean
|
||||
*/
|
||||
protected $mapJoinable = FALSE;
|
||||
|
||||
// Dynamically set whether the map is joinable - not really for production use,
|
||||
// this is primarily to support simpletests
|
||||
public function setMapJoinable($map_joinable) {
|
||||
@@ -82,7 +99,8 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
* Return an options array for PDO sources.
|
||||
*
|
||||
* @param boolean $map_joinable
|
||||
* Indicates whether the map table can be joined directly to the source query.
|
||||
* Indicates whether the map table can be joined directly to the source
|
||||
* query.
|
||||
* @param boolean $cache_counts
|
||||
* Indicates whether to cache counts of source records.
|
||||
*/
|
||||
@@ -106,7 +124,7 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
* Options applied to this source.
|
||||
*/
|
||||
public function __construct(SelectQueryInterface $query, array $fields = array(),
|
||||
SelectQueryInterface $count_query = NULL, array $options = array()) {
|
||||
SelectQueryInterface $count_query = NULL, array $options = array()) {
|
||||
parent::__construct($options);
|
||||
$this->originalQuery = $query;
|
||||
$this->query = clone $query;
|
||||
@@ -118,6 +136,18 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
$this->countQuery = $count_query;
|
||||
}
|
||||
|
||||
if (isset($options['batch_size'])) {
|
||||
$this->batchSize = $options['batch_size'];
|
||||
// Joining to the map table is incompatible with batching, disable it.
|
||||
$options['map_joinable'] = FALSE;
|
||||
}
|
||||
|
||||
// If we're tracking changes, then we need to fetch all rows to see if
|
||||
// they've changed, we can't make that determination through a direct join.
|
||||
if (!empty($options['track_changes'])) {
|
||||
$options['map_joinable'] = FALSE;
|
||||
}
|
||||
|
||||
if (isset($options['map_joinable'])) {
|
||||
$this->mapJoinable = $options['map_joinable'];
|
||||
}
|
||||
@@ -126,41 +156,42 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
// directly to the query, but this won't work unless/until
|
||||
// http://drupal.org/node/802514 is committed, assume joinable for now
|
||||
$this->mapJoinable = TRUE;
|
||||
/* // To be able to join the map directly, it must be a PDO map on the same
|
||||
// connection, or a compatible connection
|
||||
$map = $migration->getMap();
|
||||
if (is_a($map, 'MigrateSQLMap')) {
|
||||
$map_options = $map->getConnection()->getConnectionOptions();
|
||||
$query_options = $this->query->connection()->getConnectionOptions();
|
||||
/* // To be able to join the map directly, it must be a PDO map on the same
|
||||
// connection, or a compatible connection
|
||||
$map = $migration->getMap();
|
||||
if (is_a($map, 'MigrateSQLMap')) {
|
||||
$map_options = $map->getConnection()->getConnectionOptions();
|
||||
$query_options = $this->query->connection()->getConnectionOptions();
|
||||
|
||||
// Identical options means it will work
|
||||
if ($map_options == $query_options) {
|
||||
$this->mapJoinable = TRUE;
|
||||
}
|
||||
else {
|
||||
// Otherwise, the one scenario we know will work is if it's MySQL and
|
||||
// the credentials match (SQLite too?)
|
||||
if ($map_options['driver'] == 'mysql' && $query_options['driver'] == 'mysql') {
|
||||
if ($map_options['host'] == $query_options['host'] &&
|
||||
$map_options['port'] == $query_options['port'] &&
|
||||
$map_options['username'] == $query_options['username'] &&
|
||||
$map_options['password'] == $query_options['password']) {
|
||||
$this->mapJoinable = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
// Identical options means it will work
|
||||
if ($map_options == $query_options) {
|
||||
$this->mapJoinable = TRUE;
|
||||
}
|
||||
else {
|
||||
// Otherwise, the one scenario we know will work is if it's MySQL and
|
||||
// the credentials match (SQLite too?)
|
||||
if ($map_options['driver'] == 'mysql' && $query_options['driver'] == 'mysql') {
|
||||
if ($map_options['host'] == $query_options['host'] &&
|
||||
$map_options['port'] == $query_options['port'] &&
|
||||
$map_options['username'] == $query_options['username'] &&
|
||||
$map_options['password'] == $query_options['password']) {
|
||||
$this->mapJoinable = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a string representing the source query.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return (string) $this->query;
|
||||
$query = clone $this->query;
|
||||
$query = $query->extend('MigrateConnectionQuery');
|
||||
return $query->getString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,26 +223,26 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle queries without explicit field lists
|
||||
* TODO: Waiting on http://drupal.org/node/814312
|
||||
$info = Database::getConnectionInfo($query->getConnection());
|
||||
$database = $info['default']['database'];
|
||||
foreach ($this->query->getTables() as $table) {
|
||||
if (isset($table['all_fields']) && $table['all_fields']) {
|
||||
/*
|
||||
* Handle queries without explicit field lists
|
||||
* TODO: Waiting on http://drupal.org/node/814312
|
||||
$info = Database::getConnectionInfo($query->getConnection());
|
||||
$database = $info['default']['database'];
|
||||
foreach ($this->query->getTables() as $table) {
|
||||
if (isset($table['all_fields']) && $table['all_fields']) {
|
||||
|
||||
$database = 'plants';
|
||||
$table = $table['table'];
|
||||
$sql = 'SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema=:database AND table_name = :table
|
||||
ORDER BY ordinal_position';
|
||||
$result = dbtng_query($sql, array(':database' => $database, ':table' => $table));
|
||||
foreach ($result as $row) {
|
||||
$fields[$row->column_name] = $table . '.' . $row->column_name;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
$database = 'plants';
|
||||
$table = $table['table'];
|
||||
$sql = 'SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema=:database AND table_name = :table
|
||||
ORDER BY ordinal_position';
|
||||
$result = dbtng_query($sql, array(':database' => $database, ':table' => $table));
|
||||
foreach ($result as $row) {
|
||||
$fields[$row->column_name] = $table . '.' . $row->column_name;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
$expressionFields = $this->query->getExpressions();
|
||||
foreach ($expressionFields as $field_name => $field_info) {
|
||||
$fields[$field_name] = $field_info['alias'];
|
||||
@@ -243,6 +274,7 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
public function performRewind() {
|
||||
$this->result = NULL;
|
||||
$this->query = clone $this->originalQuery;
|
||||
$this->batch = 0;
|
||||
|
||||
// Get the key values, for potential use in joining to the map table, or
|
||||
// enforcing idlist.
|
||||
@@ -259,7 +291,41 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
// 1. If idlist is provided, then only process items in that list (AND key
|
||||
// IN (idlist)). Only applicable with single-value keys.
|
||||
if ($this->idList) {
|
||||
$this->query->condition($keys[0], $this->idList, 'IN');
|
||||
$simple_ids = array();
|
||||
$compound_ids = array();
|
||||
$key_count = count($keys);
|
||||
|
||||
foreach ($this->idList as $id) {
|
||||
// Look for multi-key separator. If there is only 1 key, ignore.
|
||||
if (strpos($id, $this->multikeySeparator) === FALSE || $key_count == 1) {
|
||||
$simple_ids[] = $id;
|
||||
continue;
|
||||
}
|
||||
|
||||
$compound_ids[] = explode($this->multikeySeparator, $id);
|
||||
}
|
||||
|
||||
// Check for compunded ids. If present add them with subsequent OR statements.
|
||||
if (!empty($compound_ids)) {
|
||||
$condition = db_or();
|
||||
if (!empty($simple_ids)) {
|
||||
$condition->condition($keys[0], $simple_ids, 'IN');
|
||||
}
|
||||
|
||||
foreach ($compound_ids as $values) {
|
||||
$temp_and = db_and();
|
||||
foreach ($values as $pos => $value) {
|
||||
$temp_and->condition($keys[$pos], $value);
|
||||
}
|
||||
|
||||
$condition->condition($temp_and);
|
||||
}
|
||||
|
||||
$this->query->condition($condition);
|
||||
}
|
||||
else {
|
||||
$this->query->condition($keys[0], $simple_ids, 'IN');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 2. If the map is joinable, join it. We will want to accept all rows
|
||||
@@ -292,7 +358,7 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
}
|
||||
|
||||
$alias = $this->query->leftJoin($this->activeMap->getQualifiedMapTable(),
|
||||
'map', $map_join);
|
||||
'map', $map_join);
|
||||
$conditions->isNull($alias . '.sourceid1');
|
||||
$conditions->condition($alias . '.needs_update', MigrateMap::STATUS_NEEDS_UPDATE);
|
||||
$condition_added = TRUE;
|
||||
@@ -313,20 +379,43 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
// 3. If we are using highwater marks, also include rows above the mark.
|
||||
// But, include all rows if the highwater mark is not set.
|
||||
if (isset($this->highwaterField['name']) && $this->activeMigration->getHighwater() !== '') {
|
||||
if (isset($this->highwaterField['alias'])) {
|
||||
$highwater = $this->highwaterField['alias'] . '.' . $this->highwaterField['name'];
|
||||
// But, if there are any existing items marked as needing update which
|
||||
// fall below the highwater mark, and map_joinable is FALSE, those
|
||||
// items will be skipped. Thus, in that case do not add the highwater
|
||||
// optimization to the query.
|
||||
$add_highwater_condition = TRUE;
|
||||
if (!$this->mapJoinable) {
|
||||
$count_needs_update = db_query('SELECT COUNT(*) FROM {' .
|
||||
$this->activeMap->getQualifiedMapTable() . '} WHERE needs_update = 1')
|
||||
->fetchField();
|
||||
if ($count_needs_update > 0) {
|
||||
$add_highwater_condition = FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$highwater = $this->highwaterField['name'];
|
||||
if ($add_highwater_condition) {
|
||||
if (isset($this->highwaterField['alias'])) {
|
||||
$highwater = $this->highwaterField['alias'] . '.' . $this->highwaterField['name'];
|
||||
}
|
||||
else {
|
||||
$highwater = $this->highwaterField['name'];
|
||||
}
|
||||
$conditions->condition($highwater, $this->activeMigration->getHighwater(), '>');
|
||||
$condition_added = TRUE;
|
||||
}
|
||||
$conditions->condition($highwater, $this->activeMigration->getHighwater(), '>');
|
||||
$condition_added = TRUE;
|
||||
}
|
||||
if ($condition_added) {
|
||||
$this->query->condition($conditions);
|
||||
}
|
||||
|
||||
// 4. Download data in batches for performance.
|
||||
if ($this->batchSize > 0) {
|
||||
$this->query->range($this->batch * $this->batchSize, $this->batchSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Save our fixed-up query so getNextBatch() matches it.
|
||||
$this->alteredQuery = clone $this->query;
|
||||
|
||||
migrate_instrument_start('MigrateSourceSQL execute');
|
||||
$this->result = $this->query->execute();
|
||||
migrate_instrument_stop('MigrateSourceSQL execute');
|
||||
@@ -338,6 +427,71 @@ class MigrateSourceSQL extends MigrateSource {
|
||||
* @return object
|
||||
*/
|
||||
public function getNextRow() {
|
||||
return $this->result->fetchObject();
|
||||
$row = $this->result->fetchObject();
|
||||
|
||||
// We might be out of data entirely, or just out of data in the current batch.
|
||||
// Attempt to fetch the next batch and see.
|
||||
if (!is_object($row) && $this->batchSize > 0) {
|
||||
$this->getNextBatch();
|
||||
$row = $this->result->fetchObject();
|
||||
}
|
||||
if (is_object($row)) {
|
||||
return $row;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the next set of data from the source database.
|
||||
*/
|
||||
protected function getNextBatch() {
|
||||
$this->batch++;
|
||||
$query = clone $this->alteredQuery;
|
||||
$query->range($this->batch * $this->batchSize, $this->batchSize);
|
||||
$this->result = $query->execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Query extender for retrieving the connection used on the query.
|
||||
*/
|
||||
class MigrateConnectionQuery extends SelectQueryExtender {
|
||||
|
||||
public function __construct(SelectQueryInterface $query, DatabaseConnection $connection) {
|
||||
parent::__construct($query, $connection);
|
||||
// Add the connection as metadata if anything else wants to access it.
|
||||
$query->addMetaData('connection', $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representing the source query.
|
||||
*
|
||||
* This is copied from devel module's dpq() function.
|
||||
*
|
||||
* @param bool $prefix
|
||||
* If the tables should be prefixed. If FALSE will return tables names in
|
||||
* the query like {tablename}.
|
||||
*
|
||||
* @return string
|
||||
* The SQL query.
|
||||
*/
|
||||
public function getString($prefix = TRUE) {
|
||||
$query = $this;
|
||||
if (method_exists($this, 'preExecute')) {
|
||||
$query->preExecute();
|
||||
}
|
||||
$sql = (string) $this;
|
||||
$quoted = array();
|
||||
foreach ((array) $this->arguments() as $key => $val) {
|
||||
$quoted[$key] = $this->connection->quote($val);
|
||||
}
|
||||
$sql = strtr($sql, $quoted);
|
||||
if ($prefix) {
|
||||
$sql = $this->connection->prefixTables($sql);
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,18 @@
|
||||
*/
|
||||
|
||||
class MigrateSQLMap extends MigrateMap {
|
||||
|
||||
/**
|
||||
* Names of tables created for tracking the migration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mapTable, $messageTable;
|
||||
|
||||
public function getMapTable() {
|
||||
return $this->mapTable;
|
||||
}
|
||||
|
||||
public function getMessageTable() {
|
||||
return $this->messageTable;
|
||||
}
|
||||
@@ -36,7 +39,13 @@ class MigrateSQLMap extends MigrateMap {
|
||||
return $this->mapTable;
|
||||
}
|
||||
else {
|
||||
return $options['database'] . '.' . $this->mapTable;
|
||||
$dbname = $options['database'];
|
||||
|
||||
if (!empty($options['driver']) && $options['driver'] == 'mysql') {
|
||||
// Backtick the db name on mysql to ensure dbs with hyphens work.
|
||||
$dbname = "`{$dbname}`";
|
||||
}
|
||||
return $dbname . '.' . $this->mapTable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,15 +58,18 @@ class MigrateSQLMap extends MigrateMap {
|
||||
public function getSourceKey() {
|
||||
return $this->sourceKey;
|
||||
}
|
||||
|
||||
public function getDestinationKey() {
|
||||
return $this->destinationKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drupal connection object on which to create the map/message tables
|
||||
*
|
||||
* @var DatabaseConnection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
public function getConnection() {
|
||||
return $this->connection;
|
||||
}
|
||||
@@ -69,19 +81,52 @@ class MigrateSQLMap extends MigrateMap {
|
||||
*/
|
||||
protected $ensured;
|
||||
|
||||
/**
|
||||
* Provide caching for Source or Desination Map Lookups.
|
||||
*/
|
||||
protected $cacheMapLookups;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $machine_name
|
||||
* The unique reference to the migration that we are mapping.
|
||||
* @param array $source_key
|
||||
* The database schema for the source key.
|
||||
* @param array $destination_key
|
||||
* The database schema for the destination key.
|
||||
* @param string $connection_key
|
||||
* Optional - The connection used to create the mapping tables. By default
|
||||
* this is the destination (Drupal). If it's not possible to make joins
|
||||
* between the destination database and your source database you can specify
|
||||
* a different connection to create the mapping tables on.
|
||||
* @param array $options
|
||||
* Optional - Options applied to this source.
|
||||
*/
|
||||
public function __construct($machine_name, array $source_key,
|
||||
array $destination_key, $connection_key = 'default', $options = array()) {
|
||||
array $destination_key, $connection_key = 'default', $options = array()) {
|
||||
if (isset($options['track_last_imported'])) {
|
||||
$this->trackLastImported = TRUE;
|
||||
}
|
||||
|
||||
if (isset($options['cache_map_lookups'])) {
|
||||
$this->cacheMapLookups = $options['cache_map_lookups'];
|
||||
}
|
||||
else {
|
||||
$this->cacheMapLookups = FALSE;
|
||||
}
|
||||
|
||||
$this->connection = Database::getConnection('default', $connection_key);
|
||||
|
||||
// Default generated table names, limited to 63 characters
|
||||
$prefixLength = strlen($this->connection->tablePrefix());
|
||||
$this->mapTable = 'migrate_map_' . drupal_strtolower($machine_name);
|
||||
$this->mapTable = drupal_substr($this->mapTable, 0, 63);
|
||||
$this->mapTable = drupal_substr($this->mapTable, 0, 63 - $prefixLength);
|
||||
$this->messageTable = 'migrate_message_' . drupal_strtolower($machine_name);
|
||||
$this->messageTable = drupal_substr($this->messageTable, 0, 63);
|
||||
$this->messageTable = drupal_substr($this->messageTable, 0, 63 - $prefixLength);
|
||||
$this->sourceKey = $source_key;
|
||||
$this->destinationKey = $destination_key;
|
||||
$this->connection = Database::getConnection('default', $connection_key);
|
||||
|
||||
// Build the source and destination key maps
|
||||
$this->sourceKeyMap = array();
|
||||
$count = 1;
|
||||
@@ -110,6 +155,7 @@ class MigrateSQLMap extends MigrateMap {
|
||||
foreach ($this->sourceKey as $field_schema) {
|
||||
$mapkey = 'sourceid' . $count++;
|
||||
$source_key_schema[$mapkey] = $field_schema;
|
||||
$source_key_schema[$mapkey]['not null'] = TRUE;
|
||||
$pks[] = $mapkey;
|
||||
}
|
||||
|
||||
@@ -121,6 +167,7 @@ class MigrateSQLMap extends MigrateMap {
|
||||
foreach ($this->destinationKey as $field_schema) {
|
||||
// Allow dest key fields to be NULL (for IGNORED/FAILED cases)
|
||||
$field_schema['not null'] = FALSE;
|
||||
$field_schema['default'] = NULL;
|
||||
$mapkey = 'destid' . $count++;
|
||||
$fields[$mapkey] = $field_schema;
|
||||
}
|
||||
@@ -147,6 +194,12 @@ class MigrateSQLMap extends MigrateMap {
|
||||
'default' => 0,
|
||||
'description' => 'UNIX timestamp of the last time this row was imported',
|
||||
);
|
||||
$fields['hash'] = array(
|
||||
'type' => 'varchar',
|
||||
'length' => '32',
|
||||
'not null' => FALSE,
|
||||
'description' => 'Hash of source row data, for detecting changes',
|
||||
);
|
||||
$schema = array(
|
||||
'description' => t('Mappings from source key to destination key'),
|
||||
'fields' => $fields,
|
||||
@@ -182,6 +235,30 @@ class MigrateSQLMap extends MigrateMap {
|
||||
);
|
||||
$this->connection->schema()->createTable($this->messageTable, $schema);
|
||||
}
|
||||
else {
|
||||
// Add any missing columns to the map table
|
||||
if (!$this->connection->schema()->fieldExists($this->mapTable,
|
||||
'rollback_action')) {
|
||||
$this->connection->schema()->addField($this->mapTable,
|
||||
'rollback_action', array(
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Flag indicating what to do for this item on rollback',
|
||||
));
|
||||
}
|
||||
if (!$this->connection->schema()
|
||||
->fieldExists($this->mapTable, 'hash')) {
|
||||
$this->connection->schema()->addField($this->mapTable, 'hash', array(
|
||||
'type' => 'varchar',
|
||||
'length' => '32',
|
||||
'not null' => FALSE,
|
||||
'description' => 'Hash of source row data, for detecting changes',
|
||||
));
|
||||
}
|
||||
}
|
||||
$this->ensured = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -194,7 +271,7 @@ class MigrateSQLMap extends MigrateMap {
|
||||
public function getRowBySource(array $source_id) {
|
||||
migrate_instrument_start('mapRowBySource');
|
||||
$query = $this->connection->select($this->mapTable, 'map')
|
||||
->fields('map');
|
||||
->fields('map');
|
||||
foreach ($this->sourceKeyMap as $key_name) {
|
||||
$query = $query->condition("map.$key_name", array_shift($source_id), '=');
|
||||
}
|
||||
@@ -211,7 +288,7 @@ class MigrateSQLMap extends MigrateMap {
|
||||
public function getRowByDestination(array $destination_id) {
|
||||
migrate_instrument_start('getRowByDestination');
|
||||
$query = $this->connection->select($this->mapTable, 'map')
|
||||
->fields('map');
|
||||
->fields('map');
|
||||
foreach ($this->destinationKeyMap as $key_name) {
|
||||
$query = $query->condition("map.$key_name", array_shift($destination_id), '=');
|
||||
}
|
||||
@@ -225,16 +302,17 @@ class MigrateSQLMap extends MigrateMap {
|
||||
*
|
||||
* @param int $count
|
||||
* Maximum rows to return; defaults to 10,000
|
||||
*
|
||||
* @return array
|
||||
* Array of map row objects with needs_update==1.
|
||||
*/
|
||||
public function getRowsNeedingUpdate($count) {
|
||||
$rows = array();
|
||||
$result = $this->connection->select($this->mapTable, 'map')
|
||||
->fields('map')
|
||||
->condition('needs_update', MigrateMap::STATUS_NEEDS_UPDATE)
|
||||
->range(0, $count)
|
||||
->execute();
|
||||
->fields('map')
|
||||
->condition('needs_update', MigrateMap::STATUS_NEEDS_UPDATE)
|
||||
->range(0, $count)
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
@@ -242,45 +320,79 @@ class MigrateSQLMap extends MigrateMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a (possibly multi-field) destination key, return the (possibly multi-field)
|
||||
* source key mapped to it.
|
||||
* Given a (possibly multi-field) destination key, return the (possibly
|
||||
* multi-field) source key mapped to it.
|
||||
*
|
||||
* @param array $destination_id
|
||||
* Array of destination key values.
|
||||
*
|
||||
* @return array
|
||||
* Array of source key values, or NULL on failure.
|
||||
*/
|
||||
public function lookupSourceID(array $destination_id) {
|
||||
migrate_instrument_start('lookupSourceID');
|
||||
// Try a cache lookup if enabled.
|
||||
if ($this->cacheMapLookups) {
|
||||
$cache = &drupal_static($this->mapTable . '_sourceIDCache');
|
||||
$serialized = json_encode($destination_id);
|
||||
if (isset($cache[$serialized])) {
|
||||
migrate_instrument_stop('lookupSourceID');
|
||||
return $cache[$serialized];
|
||||
}
|
||||
}
|
||||
|
||||
$query = $this->connection->select($this->mapTable, 'map')
|
||||
->fields('map', $this->sourceKeyMap);
|
||||
->fields('map', $this->sourceKeyMap);
|
||||
foreach ($this->destinationKeyMap as $key_name) {
|
||||
$query = $query->condition("map.$key_name", array_shift($destination_id), '=');
|
||||
}
|
||||
$result = $query->execute();
|
||||
$source_id = $result->fetchAssoc();
|
||||
|
||||
// Store the id in a cache if enabled.
|
||||
if ($this->cacheMapLookups) {
|
||||
$cache[$serialized] = $destination_id;
|
||||
}
|
||||
|
||||
migrate_instrument_stop('lookupSourceID');
|
||||
return $source_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a (possibly multi-field) source key, return the (possibly multi-field)
|
||||
* destination key it is mapped to.
|
||||
* Given a (possibly multi-field) source key, return the (possibly
|
||||
* multi-field) destination key it is mapped to.
|
||||
*
|
||||
* @param array $source_id
|
||||
* Array of source key values.
|
||||
*
|
||||
* @return array
|
||||
* Array of destination key values, or NULL on failure.
|
||||
*/
|
||||
public function lookupDestinationID(array $source_id) {
|
||||
migrate_instrument_start('lookupDestinationID');
|
||||
// Try a cache lookup if enabled.
|
||||
if ($this->cacheMapLookups) {
|
||||
$cache = &drupal_static($this->mapTable . '_destinationIDCache');
|
||||
$serialized = json_encode($source_id);
|
||||
if (isset($cache[$serialized])) {
|
||||
migrate_instrument_stop('lookupDestinationID');
|
||||
return $cache[$serialized];
|
||||
}
|
||||
}
|
||||
|
||||
$query = $this->connection->select($this->mapTable, 'map')
|
||||
->fields('map', $this->destinationKeyMap);
|
||||
->fields('map', $this->destinationKeyMap);
|
||||
foreach ($this->sourceKeyMap as $key_name) {
|
||||
$query = $query->condition("map.$key_name", array_shift($source_id), '=');
|
||||
}
|
||||
$result = $query->execute();
|
||||
$destination_id = $result->fetchAssoc();
|
||||
|
||||
// Store the id in a cache if enabled.
|
||||
if ($this->cacheMapLookups) {
|
||||
$cache[$serialized] = $destination_id;
|
||||
}
|
||||
|
||||
migrate_instrument_stop('lookupDestinationID');
|
||||
return $destination_id;
|
||||
}
|
||||
@@ -300,27 +412,31 @@ class MigrateSQLMap extends MigrateMap {
|
||||
* @param int $rollback_action
|
||||
* How to handle the destination object on rollback. Defaults to
|
||||
* ROLLBACK_DELETE.
|
||||
* $param string $hash
|
||||
* If hashing is enabled, the hash of the raw source row.
|
||||
*/
|
||||
public function saveIDMapping(stdClass $source_row, array $dest_ids,
|
||||
$needs_update = MigrateMap::STATUS_IMPORTED, $rollback_action = MigrateMap::ROLLBACK_DELETE) {
|
||||
$needs_update = MigrateMap::STATUS_IMPORTED,
|
||||
$rollback_action = MigrateMap::ROLLBACK_DELETE, $hash = NULL) {
|
||||
migrate_instrument_start('saveIDMapping');
|
||||
// Construct the source key
|
||||
$keys = array();
|
||||
foreach ($this->sourceKeyMap as $field_name => $key_name) {
|
||||
// A NULL key value will fail.
|
||||
if (is_null($source_row->$field_name)) {
|
||||
if (is_null($source_row->{$field_name})) {
|
||||
Migration::displayMessage(t(
|
||||
'Could not save to map table due to NULL value for key field !field',
|
||||
array('!field' => $field_name)));
|
||||
migrate_instrument_stop('saveIDMapping');
|
||||
return;
|
||||
}
|
||||
$keys[$key_name] = $source_row->$field_name;
|
||||
$keys[$key_name] = $source_row->{$field_name};
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
'needs_update' => (int)$needs_update,
|
||||
'rollback_action' => (int)$rollback_action,
|
||||
'needs_update' => (int) $needs_update,
|
||||
'rollback_action' => (int) $rollback_action,
|
||||
'hash' => $hash,
|
||||
);
|
||||
$count = 1;
|
||||
if (!empty($dest_ids)) {
|
||||
@@ -354,8 +470,8 @@ class MigrateSQLMap extends MigrateMap {
|
||||
if (is_array($source_key)) {
|
||||
foreach ($source_key as $key_value) {
|
||||
$fields['sourceid' . $count++] = $key_value;
|
||||
// If any key value is empty, we can't save - print out and abort
|
||||
if (empty($key_value)) {
|
||||
// If any key value is not set, we can't save - print out and abort
|
||||
if (!isset($key_value)) {
|
||||
print($message);
|
||||
return;
|
||||
}
|
||||
@@ -379,8 +495,8 @@ class MigrateSQLMap extends MigrateMap {
|
||||
*/
|
||||
public function prepareUpdate() {
|
||||
$this->connection->update($this->mapTable)
|
||||
->fields(array('needs_update' => MigrateMap::STATUS_NEEDS_UPDATE))
|
||||
->execute();
|
||||
->fields(array('needs_update' => MigrateMap::STATUS_NEEDS_UPDATE))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,7 +520,10 @@ class MigrateSQLMap extends MigrateMap {
|
||||
public function importedCount() {
|
||||
$query = $this->connection->select($this->mapTable);
|
||||
$query->addExpression('COUNT(*)', 'count');
|
||||
$query->condition('needs_update', array(MigrateMap::STATUS_IMPORTED, MigrateMap::STATUS_NEEDS_UPDATE), 'IN');
|
||||
$query->condition('needs_update', array(
|
||||
MigrateMap::STATUS_IMPORTED,
|
||||
MigrateMap::STATUS_NEEDS_UPDATE,
|
||||
), 'IN');
|
||||
$count = $query->execute()->fetchField();
|
||||
return $count;
|
||||
}
|
||||
@@ -450,7 +569,8 @@ class MigrateSQLMap extends MigrateMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the map entry and any message table entries for the specified source row.
|
||||
* Delete the map entry and any message table entries for the specified
|
||||
* source row.
|
||||
*
|
||||
* @param array $source_key
|
||||
*/
|
||||
@@ -475,7 +595,8 @@ class MigrateSQLMap extends MigrateMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the map entry and any message table entries for the specified destination row.
|
||||
* Delete the map entry and any message table entries for the specified
|
||||
* destination row.
|
||||
*
|
||||
* @param array $destination_key
|
||||
*/
|
||||
@@ -504,7 +625,7 @@ class MigrateSQLMap extends MigrateMap {
|
||||
*/
|
||||
public function setUpdate(array $source_key) {
|
||||
$query = $this->connection->update($this->mapTable)
|
||||
->fields(array('needs_update' => MigrateMap::STATUS_NEEDS_UPDATE));
|
||||
->fields(array('needs_update' => MigrateMap::STATUS_NEEDS_UPDATE));
|
||||
$count = 1;
|
||||
foreach ($source_key as $key_value) {
|
||||
$query->condition('sourceid' . $count++, $key_value);
|
||||
@@ -552,7 +673,7 @@ class MigrateSQLMap extends MigrateMap {
|
||||
*/
|
||||
public function clearMessages() {
|
||||
$this->connection->truncate($this->messageTable)
|
||||
->execute();
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -564,15 +685,18 @@ class MigrateSQLMap extends MigrateMap {
|
||||
}
|
||||
|
||||
protected $result = NULL;
|
||||
|
||||
protected $currentRow = NULL;
|
||||
|
||||
protected $currentKey = array();
|
||||
|
||||
public function getCurrentKey() {
|
||||
return $this->currentKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::rewind() - called before beginning a foreach loop.
|
||||
* TODO: Support idlist, itemlimit
|
||||
* Implementation of Iterator::rewind() - called before beginning a foreach
|
||||
* loop. TODO: Support idlist, itemlimit
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->currentRow = NULL;
|
||||
@@ -604,17 +728,18 @@ class MigrateSQLMap extends MigrateMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::key - called when entering a loop iteration, returning
|
||||
* the key of the current row. It must be a scalar - we will serialize
|
||||
* to fulfill the requirement, but using getCurrentKey() is preferable.
|
||||
* Implementation of Iterator::key - called when entering a loop iteration,
|
||||
* returning the key of the current row. It must be a scalar - we will
|
||||
* serialize to fulfill the requirement, but using getCurrentKey() is
|
||||
* preferable.
|
||||
*/
|
||||
public function key() {
|
||||
return serialize($this->currentKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::next() - called at the bottom of the loop implicitly,
|
||||
* as well as explicitly from rewind().
|
||||
* Implementation of Iterator::next() - called at the bottom of the loop
|
||||
* implicitly, as well as explicitly from rewind().
|
||||
*/
|
||||
public function next() {
|
||||
$this->currentRow = $this->result->fetchObject();
|
||||
@@ -624,16 +749,16 @@ class MigrateSQLMap extends MigrateMap {
|
||||
}
|
||||
else {
|
||||
foreach ($this->sourceKeyMap as $map_field) {
|
||||
$this->currentKey[$map_field] = $this->currentRow->$map_field;
|
||||
$this->currentKey[$map_field] = $this->currentRow->{$map_field};
|
||||
// Leave only destination fields
|
||||
unset($this->currentRow->$map_field);
|
||||
unset($this->currentRow->{$map_field});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::valid() - called at the top of the loop, returning
|
||||
* TRUE to process the loop and FALSE to terminate it
|
||||
* Implementation of Iterator::valid() - called at the top of the loop,
|
||||
* returning TRUE to process the loop and FALSE to terminate it
|
||||
*/
|
||||
public function valid() {
|
||||
// TODO: Check numProcessed against itemlimit
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
* Test node migration.
|
||||
*/
|
||||
class MigrateImportOptionsTest extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Import options',
|
||||
@@ -21,7 +22,7 @@ class MigrateImportOptionsTest extends DrupalWebTestCase {
|
||||
parent::setUp('migrate_example');
|
||||
|
||||
// Make sure the migrations are registered.
|
||||
migrate_get_module_apis();
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function testItemLimitOption() {
|
||||
@@ -40,17 +41,13 @@ class MigrateImportOptionsTest extends DrupalWebTestCase {
|
||||
|
||||
$result = $migration->processImport($options);
|
||||
|
||||
$this->verbose(print_r($timers, 1));
|
||||
$successes = $migration->importedCount();
|
||||
$this->verbose("Total successes: {$successes}");
|
||||
$assertion = format_plural($limit, 'The migration successfully processed 1 item.',
|
||||
'The migration successfully processed @count items.');
|
||||
$this->assertEqual($limit, $successes, $assertion);
|
||||
|
||||
$prepare_row_count = $timers['BeerTermMigration prepareRow']['count'];
|
||||
$this->verbose("prepareRow() count: {$prepare_row_count}");
|
||||
$processed = $migration->processedCount();
|
||||
$this->verbose("Total processed count: {$processed}");
|
||||
$assertion = format_plural($processed, 'The migration executed processRow() on 1 item.',
|
||||
'The migration executed processRow() on @count items.');
|
||||
$this->assertEqual($prepare_row_count, $processed, $assertion);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Test comment migration.
|
||||
*/
|
||||
class MigrateCommentUnitTest extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Comment migration',
|
||||
@@ -21,7 +22,7 @@ class MigrateCommentUnitTest extends DrupalWebTestCase {
|
||||
parent::setUp('taxonomy', 'image', 'comment', 'migrate', 'migrate_example');
|
||||
|
||||
// Make sure the migrations are registered.
|
||||
migrate_get_module_apis();
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function testCommentImport() {
|
||||
@@ -62,18 +63,29 @@ class MigrateCommentUnitTest extends DrupalWebTestCase {
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Comment import returned RESULT_COMPLETED'));
|
||||
$result = db_select('migrate_message_winecomment', 'w')
|
||||
->fields('w', array('message'))
|
||||
->execute();
|
||||
->fields('w', array('message'))
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$this->error($row->message);
|
||||
}
|
||||
|
||||
$result = db_select('migrate_example_wine_comment', 'wc')
|
||||
->fields('wc', array('commentid', 'comment_parent', 'name', 'mail',
|
||||
'accountid', 'body', 'wineid', 'subject', 'commenthost', 'userpage',
|
||||
'posted', 'lastchanged'))
|
||||
->orderBy('comment_parent')
|
||||
->execute();
|
||||
->fields('wc', array(
|
||||
'commentid',
|
||||
'comment_parent',
|
||||
'name',
|
||||
'mail',
|
||||
'accountid',
|
||||
'body',
|
||||
'wineid',
|
||||
'subject',
|
||||
'commenthost',
|
||||
'userpage',
|
||||
'posted',
|
||||
'lastchanged',
|
||||
))
|
||||
->orderBy('comment_parent')
|
||||
->execute();
|
||||
|
||||
$rawcomments = comment_load_multiple(FALSE);
|
||||
// Index by subject
|
||||
@@ -96,7 +108,8 @@ class MigrateCommentUnitTest extends DrupalWebTestCase {
|
||||
$this->assertEqual($comment->name, $row->name, t('Name matches'));
|
||||
$this->assertEqual($comment->status, COMMENT_PUBLISHED, t('Status matches'));
|
||||
$wine_migration = MigrationBase::getInstance('WineWine');
|
||||
$destid = $wine_migration->getMap()->lookupDestinationID(array($row->wineid));
|
||||
$destid = $wine_migration->getMap()
|
||||
->lookupDestinationID(array($row->wineid));
|
||||
$this->assertEqual($comment->nid, reset($destid), t('Nid matches'));
|
||||
$body = field_get_items('comment', $comment, 'comment_body');
|
||||
$this->assertEqual($body[0]['value'], $row->body, t('Body matches'));
|
||||
@@ -108,7 +121,8 @@ class MigrateCommentUnitTest extends DrupalWebTestCase {
|
||||
$comment = $comments['im child'];
|
||||
$row = $rows['im child'];
|
||||
$user_migration = MigrationBase::getInstance('WineUser');
|
||||
$destid = $user_migration->getMap()->lookupDestinationID(array($row->accountid));
|
||||
$destid = $user_migration->getMap()
|
||||
->lookupDestinationID(array($row->accountid));
|
||||
$this->assertEqual($comment->uid, reset($destid), t('Uid matches'));
|
||||
$this->assertEqual($comment->pid, $comments['im parent']->cid, t('Parent matches'));
|
||||
|
||||
@@ -123,16 +137,19 @@ class MigrateCommentUnitTest extends DrupalWebTestCase {
|
||||
foreach ($original_comments as $cid => $original_comment) {
|
||||
foreach ($original_comment as $field => $value) {
|
||||
if ($field == 'subject') {
|
||||
if ($value == $final_comments[$cid]->$field) {
|
||||
if ($value == $final_comments[$cid]->{$field}) {
|
||||
$this->error(t('Field !field should have changed but did not, value=!value',
|
||||
array('!field' => $field, '!value' => print_r($value, TRUE))));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($value != $final_comments[$cid]->$field) {
|
||||
if ($value != $final_comments[$cid]->{$field}) {
|
||||
$this->error(t('Field !field mismatch: original !value1, result !value2',
|
||||
array('!field' => $field, '!value1' => print_r($value, TRUE),
|
||||
'!value2' => print_r($final_comments[$cid]->$field, TRUE))));
|
||||
array(
|
||||
'!field' => $field,
|
||||
'!value1' => print_r($value, TRUE),
|
||||
'!value2' => print_r($final_comments[$cid]->{$field}, TRUE),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,16 +162,16 @@ class MigrateCommentUnitTest extends DrupalWebTestCase {
|
||||
$rawcomments = comment_load_multiple(FALSE);
|
||||
$this->assertEqual(count($rawcomments), 0, t('All comments deleted'));
|
||||
$count = db_select('migrate_map_winecomment', 'map')
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Map cleared'));
|
||||
$count = db_select('migrate_message_winecomment', 'msg')
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Messages cleared'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Test node migration.
|
||||
*/
|
||||
class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node migration',
|
||||
@@ -21,7 +22,7 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
parent::setUp('list', 'number', 'taxonomy', 'image', 'migrate', 'migrate_example');
|
||||
|
||||
// Make sure the migrations are registered.
|
||||
migrate_get_module_apis();
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function testNodeImport() {
|
||||
@@ -67,7 +68,13 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
$query = db_select('migrate_example_wine_producer', 'p')
|
||||
->fields('p', array('producerid', 'name', 'body', 'excerpt', 'accountid'));
|
||||
->fields('p', array(
|
||||
'producerid',
|
||||
'name',
|
||||
'body',
|
||||
'excerpt',
|
||||
'accountid',
|
||||
));
|
||||
// Region term is singletons, handled straighforwardly
|
||||
$query->leftJoin('migrate_example_wine_category_producer', 'reg',
|
||||
"p.producerid = reg.producerid");
|
||||
@@ -87,8 +94,17 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
$wine_nodes[$node->title] = $node;
|
||||
}
|
||||
$query = db_select('migrate_example_wine', 'w')
|
||||
->fields('w', array('wineid', 'name', 'body', 'excerpt', 'accountid',
|
||||
'posted', 'last_changed', 'variety', 'region'));
|
||||
->fields('w', array(
|
||||
'wineid',
|
||||
'name',
|
||||
'body',
|
||||
'excerpt',
|
||||
'accountid',
|
||||
'posted',
|
||||
'last_changed',
|
||||
'variety',
|
||||
'region',
|
||||
));
|
||||
$query->leftJoin('migrate_example_wine_category_wine', 'cwbw',
|
||||
"w.wineid = cwbw.wineid");
|
||||
$query->leftJoin('migrate_example_wine_categories', 'bw',
|
||||
@@ -111,7 +127,8 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
$wine_row = $wine_rows['Montes Classic Cabernet Sauvignon'];
|
||||
$user_migration = MigrationBase::getInstance('WineUser');
|
||||
|
||||
$mapped_uid = $user_migration->getMap()->lookupDestinationID(array($producer_row->accountid));
|
||||
$mapped_uid = $user_migration->getMap()
|
||||
->lookupDestinationID(array($producer_row->accountid));
|
||||
if (is_array($mapped_uid)) {
|
||||
$this->assertEqual($producer_node->uid, reset($mapped_uid),
|
||||
t('uid properly migrated'));
|
||||
@@ -134,7 +151,8 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
// taxonomy_term_reference - single and multiple
|
||||
$variety = field_get_items('node', $wine_node, 'migrate_example_wine_varieties');
|
||||
$variety_migration = MigrationBase::getInstance('WineVariety');
|
||||
$mapped_tid = $variety_migration->getMap()->lookupDestinationID(array($wine_row->variety));
|
||||
$mapped_tid = $variety_migration->getMap()
|
||||
->lookupDestinationID(array($wine_row->variety));
|
||||
if (is_array($mapped_tid)) {
|
||||
$this->assertEqual($variety[0]['tid'], reset($mapped_tid),
|
||||
t('Single taxonomy_term_reference properly migrated'));
|
||||
@@ -147,7 +165,8 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
$source_ids = explode(',', $wine_row->best_with);
|
||||
$mapped_tids = array();
|
||||
foreach ($source_ids as $source_id) {
|
||||
$tid = $best_with_migration->getMap()->lookupDestinationID(array($source_id));
|
||||
$tid = $best_with_migration->getMap()
|
||||
->lookupDestinationID(array($source_id));
|
||||
if ($tid) {
|
||||
$mapped_tids[reset($tid)] = reset($tid);
|
||||
}
|
||||
@@ -176,33 +195,66 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
foreach ($original_nodes as $nid => $original_node) {
|
||||
foreach ($original_node as $field => $value) {
|
||||
if ($field == 'field_migrate_example_wine_ratin' || $field == 'changed' || $field == 'revision_timestamp') {
|
||||
if ($value == $final_nodes[$nid]->$field) {
|
||||
if ($value == $final_nodes[$nid]->{$field}) {
|
||||
$this->error(t('Field !field should have changed but did not, value=!value',
|
||||
array('!field' => $field, '!value' => print_r($value, TRUE))));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($value != $final_nodes[$nid]->$field) {
|
||||
if ($value != $final_nodes[$nid]->{$field}) {
|
||||
$this->error(t('Field !field mismatch: original !value1, result !value2',
|
||||
array('!field' => $field, '!value1' => print_r($value, TRUE),
|
||||
'!value2' => print_r($final_nodes[$nid]->$field, TRUE))));
|
||||
array(
|
||||
'!field' => $field,
|
||||
'!value1' => print_r($value, TRUE),
|
||||
'!value2' => print_r($final_nodes[$nid]->{$field}, TRUE),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test highwater marks - add new wines, modify an old one, and see what changes
|
||||
$fields = array('wineid', 'name', 'body', 'excerpt', 'accountid',
|
||||
'posted', 'last_changed', 'variety', 'region', 'rating');
|
||||
$fields = array(
|
||||
'wineid',
|
||||
'name',
|
||||
'body',
|
||||
'excerpt',
|
||||
'accountid',
|
||||
'posted',
|
||||
'last_changed',
|
||||
'variety',
|
||||
'region',
|
||||
'rating',
|
||||
);
|
||||
$query = db_insert('migrate_example_wine')
|
||||
->fields($fields);
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
// Timestamps 1284008523, 1284120550
|
||||
array(3, 'Schloss Muhlenhof Dornfelder', 'Juicy black & red berry fruits', 'Pretty good', 9,
|
||||
strtotime('2010-09-09 01:02:03'), strtotime('2010-09-10 08:09:10'), 25, 17, 95),
|
||||
array(
|
||||
3,
|
||||
'Schloss Muhlenhof Dornfelder',
|
||||
'Juicy black & red berry fruits',
|
||||
'Pretty good',
|
||||
9,
|
||||
strtotime('2010-09-09 01:02:03'),
|
||||
strtotime('2010-09-10 08:09:10'),
|
||||
25,
|
||||
17,
|
||||
95,
|
||||
),
|
||||
// Timestamps 1286122209, 1286122209
|
||||
array(4, 'Gachot-Monot Bourgogne Rge 06', 'Funky', 'Pair with white sauced dishes', 3,
|
||||
strtotime('2010-10-03 12:10:09'), strtotime('2010-10-03 12:10:09'), 26, 2, 85),
|
||||
array(
|
||||
4,
|
||||
'Gachot-Monot Bourgogne Rge 06',
|
||||
'Funky',
|
||||
'Pair with white sauced dishes',
|
||||
3,
|
||||
strtotime('2010-10-03 12:10:09'),
|
||||
strtotime('2010-10-03 12:10:09'),
|
||||
26,
|
||||
2,
|
||||
85,
|
||||
),
|
||||
);
|
||||
foreach ($data as $row) {
|
||||
$query->values(array_combine($fields, $row));
|
||||
@@ -211,9 +263,9 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
|
||||
db_update('migrate_example_wine')
|
||||
->fields(array(
|
||||
'body' => 'Not so much berry character',
|
||||
// Timestamp 1285058521
|
||||
'last_changed' => strtotime('2010-10-21 04:42:01'),
|
||||
'body' => 'Not so much berry character',
|
||||
// Timestamp 1285058521
|
||||
'last_changed' => strtotime('2010-10-21 04:42:01'),
|
||||
))
|
||||
->condition('wineid', 2)
|
||||
->execute();
|
||||
@@ -241,22 +293,24 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
$rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
|
||||
$this->assertEqual(count($rawnodes), 0, t('All nodes deleted'));
|
||||
$count = db_select('migrate_map_winewine', 'map')
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Map cleared'));
|
||||
$count = db_select('migrate_message_winewine', 'msg')
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Messages cleared'));
|
||||
|
||||
// Now test highwater with unjoined map table
|
||||
$migration->getSource()->setMapJoinable(FALSE);
|
||||
$result = $migration->processImport(array('limit' =>
|
||||
array('value' => 2, 'unit' => 'items')));
|
||||
$result = $migration->processImport(array(
|
||||
'limit' =>
|
||||
array('value' => 2, 'unit' => 'items'),
|
||||
));
|
||||
db_update('migrate_example_wine')
|
||||
->fields(array(
|
||||
'body' => 'Very berry',
|
||||
@@ -282,8 +336,10 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
// Test itemlimit (joined map table)
|
||||
$result = $migration->processRollback(array('force' => TRUE));
|
||||
$migration->getSource()->setMapJoinable(TRUE);
|
||||
$result = $migration->processImport(array('limit' =>
|
||||
array('value' => 1, 'unit' => 'item')));
|
||||
$result = $migration->processImport(array(
|
||||
'limit' =>
|
||||
array('value' => 1, 'unit' => 'item'),
|
||||
));
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Wine node import with itemlimit returned RESULT_COMPLETED'));
|
||||
$rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
|
||||
@@ -303,8 +359,10 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
// Test itemlimit (unjoined map table)
|
||||
$result = $migration->processRollback(array('force' => TRUE));
|
||||
$migration->getSource()->setMapJoinable(FALSE);
|
||||
$result = $migration->processImport(array('limit' =>
|
||||
array('value' => 1, 'unit' => 'item')));
|
||||
$result = $migration->processImport(array(
|
||||
'limit' =>
|
||||
array('value' => 1, 'unit' => 'item'),
|
||||
));
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Wine node import with itemlimit returned RESULT_COMPLETED'));
|
||||
$rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
|
||||
@@ -359,10 +417,10 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
->execute();
|
||||
$result = $migration->processImport();
|
||||
$newHighwater = db_select('migrate_status', 'ms')
|
||||
->fields('ms', array('highwater'))
|
||||
->condition('machine_name', 'WineWine')
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('ms', array('highwater'))
|
||||
->condition('machine_name', 'WineWine')
|
||||
->execute()
|
||||
->fetchField();
|
||||
if (!$this->assertEqual($newHighwater, 1000000000, t('Correct highwater mark set'))) {
|
||||
$this->error(t('Unexpected highwater mark !highwater', array('!highwater' => $newHighwater)));
|
||||
}
|
||||
@@ -387,10 +445,23 @@ class MigrateNodeUnitTest extends DrupalWebTestCase {
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Beer node update import returned RESULT_COMPLETED'));
|
||||
$result = db_select('migrate_message_beernode', 'msg')
|
||||
->fields('msg', array('message'))
|
||||
->execute();
|
||||
->fields('msg', array('message'))
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$this->error($row->message);
|
||||
}
|
||||
|
||||
// Test proper disableMailSystem()/restoreMailSystem() behavior.
|
||||
// @see https://www.drupal.org/node/2499861
|
||||
// This is not necessarily node-related, but it was shoved in the node test.
|
||||
$migration->saveMailSystem();
|
||||
$migration->disableMailSystem();
|
||||
$ms = variable_get('mail_system', NULL);
|
||||
$this->assertTrue(is_array($ms) && in_array('MigrateMailIgnore', $ms),
|
||||
t('Migration has disabled mail system'));
|
||||
$migration->restoreMailSystem();
|
||||
$ms = variable_get('mail_system', NULL);
|
||||
$this->assertFalse(is_array($ms) && in_array('MigrateMailIgnore', $ms),
|
||||
t('Migration::restoreMailSystem restored mail system'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Test table migration.
|
||||
*/
|
||||
class MigrateTableUnitTest extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Table migration',
|
||||
@@ -21,7 +22,7 @@ class MigrateTableUnitTest extends DrupalWebTestCase {
|
||||
parent::setUp('migrate', 'migrate_example');
|
||||
|
||||
// Make sure the migrations are registered.
|
||||
migrate_get_module_apis();
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function testTableImport() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Test taxonomy migration.
|
||||
*/
|
||||
class MigrateTaxonomyUnitTest extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Taxonomy migration',
|
||||
@@ -21,7 +22,7 @@ class MigrateTaxonomyUnitTest extends DrupalWebTestCase {
|
||||
parent::setUp('taxonomy', 'migrate', 'migrate_example');
|
||||
|
||||
// Make sure the migrations are registered.
|
||||
migrate_get_module_apis();
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function testTermImport() {
|
||||
@@ -36,10 +37,16 @@ class MigrateTaxonomyUnitTest extends DrupalWebTestCase {
|
||||
$terms[$term->name] = $term;
|
||||
}
|
||||
$query = db_select('migrate_example_wine_categories', 'wc')
|
||||
->fields('wc', array('categoryid', 'name', 'details', 'category_parent', 'ordering'))
|
||||
->condition('wc.type', 'variety');
|
||||
->fields('wc', array(
|
||||
'categoryid',
|
||||
'name',
|
||||
'details',
|
||||
'category_parent',
|
||||
'ordering',
|
||||
))
|
||||
->condition('wc.type', 'variety');
|
||||
$query->leftJoin('migrate_example_wine_categories', 'wcpar',
|
||||
'wc.category_parent=wcpar.categoryid');
|
||||
'wc.category_parent=wcpar.categoryid');
|
||||
$query->addField('wcpar', 'name', 'parent_name');
|
||||
$result = $query->execute();
|
||||
|
||||
@@ -79,16 +86,19 @@ class MigrateTaxonomyUnitTest extends DrupalWebTestCase {
|
||||
foreach ($original_terms as $tid => $original_term) {
|
||||
foreach ($original_term as $field => $value) {
|
||||
if ($field == 'description') {
|
||||
if ($value == $final_terms[$tid]->$field) {
|
||||
if ($value == $final_terms[$tid]->{$field}) {
|
||||
$this->error(t('Field !field should have changed but did not, value=!value',
|
||||
array('!field' => $field, '!value' => print_r($value, TRUE))));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($value != $final_terms[$tid]->$field) {
|
||||
if ($value != $final_terms[$tid]->{$field}) {
|
||||
$this->error(t('Field !field mismatch: original !value1, result !value2',
|
||||
array('!field' => $field, '!value1' => print_r($value, TRUE),
|
||||
'!value2' => print_r($final_terms[$tid]->$field, TRUE))));
|
||||
array(
|
||||
'!field' => $field,
|
||||
'!value1' => print_r($value, TRUE),
|
||||
'!value2' => print_r($final_terms[$tid]->{$field}, TRUE),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,16 +111,16 @@ class MigrateTaxonomyUnitTest extends DrupalWebTestCase {
|
||||
$rawterms = taxonomy_term_load_multiple(array(), array('vid' => $vocab->vid));
|
||||
$this->assertEqual(count($rawterms), 0, t('All terms deleted'));
|
||||
$count = db_select('migrate_map_winevariety', 'map')
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Map cleared'));
|
||||
$count = db_select('migrate_message_winevariety', 'msg')
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Messages cleared'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Test user migration.
|
||||
*/
|
||||
class MigrateUserUnitTest extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'User migration',
|
||||
@@ -24,7 +25,7 @@ class MigrateUserUnitTest extends DrupalWebTestCase {
|
||||
date_default_timezone_set('US/Mountain');
|
||||
|
||||
// Make sure the migrations are registered.
|
||||
migrate_get_module_apis();
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function testUserImport() {
|
||||
@@ -38,27 +39,45 @@ class MigrateUserUnitTest extends DrupalWebTestCase {
|
||||
t('Role import returned RESULT_COMPLETED'));
|
||||
// Confirm both roles were successfully imported
|
||||
$result = db_select('role', 'r')
|
||||
->fields('r', array('rid', 'name'))
|
||||
->condition('name', array('Taster', 'Vintner'), 'IN')
|
||||
->execute();
|
||||
->fields('r', array('rid', 'name'))
|
||||
->condition('name', array('Taster', 'Vintner'), 'IN')
|
||||
->execute();
|
||||
$roles = array();
|
||||
foreach ($result as $row) {
|
||||
$roles[$row->name] = $row->rid;
|
||||
}
|
||||
$this->assertEqual(count($roles), 2, t('Both roles imported'));
|
||||
|
||||
// Make sure update does not fail (regression test of
|
||||
// http://drupal.org/node/1872446)
|
||||
$migration->prepareUpdate();
|
||||
$migration->processImport();
|
||||
$num_messages = $migration->getMap()->messageCount();
|
||||
$this->assertEqual($num_messages, 0, t('No messages generated'));
|
||||
|
||||
$migration = Migration::getInstance('WineUser');
|
||||
$result = $migration->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('User import returned RESULT_COMPLETED'));
|
||||
$result = db_select('migrate_example_wine_account', 'mea')
|
||||
->fields('mea', array('accountid', 'status', 'posted', 'name',
|
||||
'sex', 'password', 'mail', 'last_access', 'last_login',
|
||||
'sig', 'original_mail'))
|
||||
->execute();
|
||||
->fields('mea', array(
|
||||
'accountid',
|
||||
'status',
|
||||
'posted',
|
||||
'name',
|
||||
'sex',
|
||||
'password',
|
||||
'mail',
|
||||
'last_access',
|
||||
'last_login',
|
||||
'sig',
|
||||
'original_mail',
|
||||
))
|
||||
->execute();
|
||||
$uids = db_select('users', 'u')
|
||||
->fields('u', array('uid'))
|
||||
->execute()
|
||||
->fetchCol();
|
||||
->fields('u', array('uid'))
|
||||
->execute()
|
||||
->fetchCol();
|
||||
// Index by name
|
||||
$users = array();
|
||||
foreach ($uids as $uid) {
|
||||
@@ -106,9 +125,10 @@ class MigrateUserUnitTest extends DrupalWebTestCase {
|
||||
1, t('Female gender migrated'));
|
||||
$this->assert(!isset($users['fonzie']->field_migrate_example_gender[LANGUAGE_NONE][0]['value']),
|
||||
t('Missing gender left unmigrated'));
|
||||
$this->assert(is_object($users['fonzie']->picture) &&
|
||||
$users['fonzie']->picture->filename == 'association-individual.png',
|
||||
t('Picture migrated'));
|
||||
/* For some reason, this fails on d.o but not in local environments
|
||||
$this->assert(is_object($users['fonzie']->picture) &&
|
||||
$users['fonzie']->picture->filename == '200',
|
||||
t('Picture migrated'));*/
|
||||
$this->assertNotNull($users['fonzie']->roles[$roles['Taster']], t('Taster role'));
|
||||
$this->assertNotNull($users['fonzie']->roles[$roles['Vintner']], t('Vintner role'));
|
||||
|
||||
@@ -131,20 +151,26 @@ class MigrateUserUnitTest extends DrupalWebTestCase {
|
||||
foreach ($original_users as $uid => $original_user) {
|
||||
foreach ($original_user as $field => $value) {
|
||||
if ($field == 'field_migrate_example_gender') {
|
||||
if ($value == $final_users[$uid]->$field) {
|
||||
if ($value == $final_users[$uid]->{$field}) {
|
||||
$this->error(t('For user !name, field !field should have changed but did not, value=!value',
|
||||
array('!name' => $final_users[$uid]->name, '!field' => $field,
|
||||
'!value' => print_r($value, TRUE))));
|
||||
array(
|
||||
'!name' => $final_users[$uid]->name,
|
||||
'!field' => $field,
|
||||
'!value' => print_r($value, TRUE),
|
||||
)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($value != $final_users[$uid]->$field) {
|
||||
if ($value != $final_users[$uid]->{$field}) {
|
||||
// Core bug http://drupal.org/node/935592 causes picture mismatches, ignore until it's fixed
|
||||
if ($field != 'picture') {
|
||||
$this->error(t('For user !name, field !field mismatch: original !value1, result !value2',
|
||||
array('!name' => $final_users[$uid]->name, '!field' => $field,
|
||||
array(
|
||||
'!name' => $final_users[$uid]->name,
|
||||
'!field' => $field,
|
||||
'!value1' => print_r($value, TRUE),
|
||||
'!value2' => print_r($final_users[$uid]->$field, TRUE))));
|
||||
'!value2' => print_r($final_users[$uid]->{$field}, TRUE),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,23 +182,23 @@ class MigrateUserUnitTest extends DrupalWebTestCase {
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('User rollback returned RESULT_COMPLETED'));
|
||||
$count = db_select('users', 'u')
|
||||
->fields('u', array('uid'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('u', array('uid'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
// 2 users left - anon and admin
|
||||
$this->assertEqual($count, 2, t('All imported users deleted'));
|
||||
$count = db_select('migrate_map_wineuser', 'map')
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Map cleared'));
|
||||
$count = db_select('migrate_message_wineuser', 'msg')
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Messages cleared'));
|
||||
|
||||
// Test deduping
|
||||
@@ -182,9 +208,9 @@ class MigrateUserUnitTest extends DrupalWebTestCase {
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('User import returned RESULT_COMPLETED'));
|
||||
$accounts = db_select('users', 'u')
|
||||
->fields('u', array('mail', 'name'))
|
||||
->execute()
|
||||
->fetchAllKeyed();
|
||||
->fields('u', array('mail', 'name'))
|
||||
->execute()
|
||||
->fetchAllKeyed();
|
||||
if (!$this->assertEqual($accounts['alice@example.com'], 'alice', t('alice found'))) {
|
||||
$this->error(t('Expected alice, found !name', array('!name' => $accounts['alice@example.com'])));
|
||||
}
|
||||
@@ -198,9 +224,9 @@ class MigrateUserUnitTest extends DrupalWebTestCase {
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('User import returned RESULT_COMPLETED'));
|
||||
$accounts = db_select('users', 'u')
|
||||
->fields('u', array('mail', 'name'))
|
||||
->execute()
|
||||
->fetchAllKeyed();
|
||||
->fields('u', array('mail', 'name'))
|
||||
->execute()
|
||||
->fetchAllKeyed();
|
||||
if (!$this->assertEqual($accounts['alice@example.com'], 'alice', t('alice found'))) {
|
||||
$this->error(t('Expected alice, found !name', array('!name' => $accounts['alice@example.com'])));
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* when that is fixed.
|
||||
*/
|
||||
class MigrateOracleUnitTest extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Oracle migration',
|
||||
@@ -23,7 +24,7 @@ class MigrateOracleUnitTest extends DrupalWebTestCase {
|
||||
function setUp() {
|
||||
global $conf;
|
||||
if (empty($conf['oracle_db']) || empty($conf['oracle_db']['username']) ||
|
||||
empty($conf['oracle_db']['password']) || empty($conf['oracle_db']['connection_string'])) {
|
||||
empty($conf['oracle_db']['password']) || empty($conf['oracle_db']['connection_string'])) {
|
||||
parent::setUp();
|
||||
}
|
||||
else {
|
||||
@@ -31,7 +32,7 @@ class MigrateOracleUnitTest extends DrupalWebTestCase {
|
||||
}
|
||||
|
||||
// Make sure the migrations are registered.
|
||||
migrate_get_module_apis();
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function testOracleImport() {
|
||||
@@ -87,16 +88,16 @@ class MigrateOracleUnitTest extends DrupalWebTestCase {
|
||||
$rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_oracle'), TRUE);
|
||||
$this->assertEqual(count($rawnodes), 0, t('All nodes deleted'));
|
||||
$count = db_select('migrate_map_migrateexampleoracle', 'map')
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Map cleared'));
|
||||
$count = db_select('migrate_message_migrateexampleoracle', 'msg')
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Messages cleared'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Test node migration.
|
||||
*/
|
||||
class MigrateXMLUnitTest extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML migration',
|
||||
@@ -21,7 +22,7 @@ class MigrateXMLUnitTest extends DrupalWebTestCase {
|
||||
parent::setUp('taxonomy', 'migrate', 'migrate_example');
|
||||
|
||||
// Make sure the migrations are registered.
|
||||
migrate_get_module_apis();
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function testNodeImport() {
|
||||
@@ -29,22 +30,51 @@ class MigrateXMLUnitTest extends DrupalWebTestCase {
|
||||
$result = $migration->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Region term import returned RESULT_COMPLETED'));
|
||||
|
||||
$migration = Migration::getInstance('WineFileCopy');
|
||||
$result = $migration->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('File import returned RESULT_COMPLETED'));
|
||||
|
||||
$migration = Migration::getInstance('WineRole');
|
||||
$result = $migration->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Role import returned RESULT_COMPLETED'));
|
||||
|
||||
$migration = Migration::getInstance('WineUser');
|
||||
$result = $migration->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('User import returned RESULT_COMPLETED'));
|
||||
$migration = Migration::getInstance('WineProducerXML');
|
||||
$result = $migration->processImport();
|
||||
|
||||
$migration1 = Migration::getInstance('WineProducerXML');
|
||||
$result = $migration1->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node import returned RESULT_COMPLETED'));
|
||||
t('Producer node import 1 returned RESULT_COMPLETED'));
|
||||
|
||||
$migration2 = Migration::getInstance('WineProducerNamespaceXML');
|
||||
$result = $migration2->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node import 2 returned RESULT_COMPLETED'));
|
||||
|
||||
$migration3 = Migration::getInstance('WineProducerMultiXML');
|
||||
$result = $migration3->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node import 3 returned RESULT_COMPLETED'));
|
||||
|
||||
$migration4 = Migration::getInstance('WineProducerMultiNamespaceXML');
|
||||
$result = $migration4->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node import 4 returned RESULT_COMPLETED'));
|
||||
|
||||
$migration5 = Migration::getInstance('WineProducerXMLPull');
|
||||
$result = $migration5->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node import 5 returned RESULT_COMPLETED'));
|
||||
|
||||
$migration6 = Migration::getInstance('WineProducerNamespaceXMLPull');
|
||||
$result = $migration6->processImport();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node import 6 returned RESULT_COMPLETED'));
|
||||
|
||||
// Gather producer nodes, and their corresponding input data
|
||||
$rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_producer'), TRUE);
|
||||
@@ -54,7 +84,7 @@ class MigrateXMLUnitTest extends DrupalWebTestCase {
|
||||
$producer_nodes[$node->title] = $node;
|
||||
}
|
||||
|
||||
$this->assertEqual(count($producer_nodes), 1,
|
||||
$this->assertEqual(count($producer_nodes), 10,
|
||||
t('Counts of producer nodes and input rows match'));
|
||||
|
||||
// Test each base node field
|
||||
@@ -81,23 +111,45 @@ class MigrateXMLUnitTest extends DrupalWebTestCase {
|
||||
$this->assertEqual($region[0]['tid'], $term->tid,
|
||||
t('region properly migrated'));
|
||||
|
||||
// Test rollback
|
||||
$result = $migration->processRollback();
|
||||
// Rollback producer migrations
|
||||
$result = $migration1->processRollback();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node rollback returned RESULT_COMPLETED'));
|
||||
t('Producer node rollback 1 returned RESULT_COMPLETED'));
|
||||
|
||||
$result = $migration2->processRollback();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node rollback 2 returned RESULT_COMPLETED'));
|
||||
|
||||
$result = $migration3->processRollback();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node rollback 3 returned RESULT_COMPLETED'));
|
||||
|
||||
$result = $migration4->processRollback();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node rollback 4 returned RESULT_COMPLETED'));
|
||||
|
||||
$result = $migration5->processRollback();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node rollback 5 returned RESULT_COMPLETED'));
|
||||
|
||||
$result = $migration6->processRollback();
|
||||
$this->assertEqual($result, Migration::RESULT_COMPLETED,
|
||||
t('Producer node rollback 6 returned RESULT_COMPLETED'));
|
||||
|
||||
// Test rollback
|
||||
$rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_producer'), TRUE);
|
||||
$this->assertEqual(count($rawnodes), 0, t('All nodes deleted'));
|
||||
$count = db_select('migrate_map_wineproducerxml', 'map')
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('map', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Map cleared'));
|
||||
$count = db_select('migrate_message_wineproducerxml', 'msg')
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
->fields('msg', array('sourceid1'))
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, t('Messages cleared'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
/**
|
||||
* @file
|
||||
* Sample file for handling redirection from old to new URIs. Use an Apache
|
||||
* rewrite rule (or equivalent) to map legacy requests to this file. To use, copy
|
||||
* or symlink this file to the root of your drupal site. Customize
|
||||
* this file to your needs.
|
||||
* rewrite rule (or equivalent) to map legacy requests to this file. To use,
|
||||
* copy or symlink this file to the root of your drupal site. Customize this
|
||||
* file to your needs.
|
||||
*
|
||||
* CREATE TABLE `migrate_source_uri_map` (
|
||||
* `source_uri` varchar(255) NOT NULL DEFAULT '',
|
||||
@@ -41,7 +41,7 @@ function migrate_build_url($destid1, $migration_name) {
|
||||
}
|
||||
|
||||
// Build absolute url for 301 redirect.
|
||||
return $base_url . '/' . $destination_uri;
|
||||
return $base_url . '/' . $destination_uri;
|
||||
}
|
||||
|
||||
define('DRUPAL_ROOT', getcwd());
|
||||
|
||||
Reference in New Issue
Block a user