FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,46 @@
The Migrate Extras module provides extensions to Migrate (http://drupal.org/project/migrate)
to support various contributed modules. The ideal place to implement migration support
for a contributed module is in that module. That way, the migration support is always
self-consistent with the current module implementation - it's not practical for the
migrate_extras module to keep up with changes to all other contrib modules. Support
for contributed modules may be added to migrate_extras for two reasons - if the module's
maintainer does not accept a patch providing migration support, or as an intermediate
step before submitting such a patch to the other module.
In cases where modules supported by migrate_extras end up implementing the support
themselves, you could at least temporarily end up with redundant implementations.
The extra implementation may be disabled at admin/content/migrate/configure.
The following modules are currently supported in Migrate Extras on Drupal 7 (note
that Date module support has moved into the Date module itself):
Address Field
Entity API
Flag
Geofield
Interval
Media
Name Field
Pathauto
Phone Number (cck_phone)
Private Messages
Profile2
Rules
User Relationships
Voting API
Webform
Compatibility
-------------
This release of Migrate Extras requires Migrate V2.4-beta1 or later.
Migrate_Extras_Examples
----------------
See the Examples folder for a few implemented Migrations that you can run and
inspect.
Maintainers
-----------
Frank Carey http://drupal.org/user/112063
Mike Ryan - http://drupal.org/user/4420
Moshe Weitzman - http://drupal.org/user/23

View File

@@ -0,0 +1,62 @@
<?php
/**
* Primary value passed to this field must be the two letter ISO country code of
* the address.
*
* Arguments are used to specify all the other values:
* 'administrative_area' - The administrative area of this address. (i.e. State/Province)
* 'sub_administrative_area' - The sub administrative area of this address.
* 'locality' - The locality of this address. (i.e. City)
* 'dependent_locality' - The dependent locality of this address.
* 'postal_code' - The postal code of this address.
* 'thoroughfare' - The thoroughfare of this address. (i.e. Street address)
* 'premise' - The premise of this address. (i.e. Apartment / Suite number)
* 'sub_premise' - The sub_premise of this address.
* 'organisation_name' - Contents of a primary OrganisationName element in the xNL XML.
* 'name_line' - Contents of a primary NameLine element in the xNL XML.
* 'first_name' - Contents of the FirstName element of a primary PersonName element in the xNL XML.
* 'last_name' - Contents of the LastName element of a primary PersonName element in the xNL XML.
* 'data' - Additional data for this address.
*
* Add the source field mappings to the argument array then add null mappings to
* avoid having fields flagged as as unmapped:
* @code
* $arguments = array(
* 'thoroughfare' => array('source_field' => 'profile_address'),
* 'locality' => array('source_field' => 'profile_city'),
* 'administrative_area' => array('source_field' => 'profile_state'),
* );
* // The country should be passed in as the primary value.
* $this->addFieldMapping('field_user_address', 'profile_country')
* ->arguments($arguments);
* // Since the excerpt is mapped via an argument, add a null mapping so it's
* // not flagged as unmapped.
* $this->addFieldMapping(NULL, 'profile_address');
* $this->addFieldMapping(NULL, 'profile_city');
* $this->addFieldMapping(NULL, 'profile_state');
* @endcode
*/
class MigrateAddressFieldHandler extends MigrateFieldHandler {
public function __construct() {
$this->registerTypes(array('addressfield'));
}
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);
// Setup the standard Field API array for saving.
$delta = 0;
foreach ($values as $value) {
$return[$language][$delta] = array('country' => $value) + array_intersect_key($arguments, $field_info['columns']);
$delta++;
}
return isset($return) ? $return : NULL;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Primary value passed to this field must be the two letter ISO country code of
* the phone number.
*
* Subfields are used to specify all the other values:
* 'number' - The actual phone number.
* 'extension' - The extension.
*
* @code
* // The country should be passed in as the primary value.
* $this->addFieldMapping('field_user_phone', 'profile_country');
* $this->addFieldMapping('field_user_phone:number', 'profile_number');
* $this->addFieldMapping('field_user_phone:extension', 'profile_extension');
* @endcode
*/
class MigrateCckPhoneHandler extends MigrateFieldHandler {
public function __construct() {
$this->registerTypes(array('phone_number'));
}
public function fields($type) {
return array(
'number' => t('Subfield: The phone number itself'),
'extension' => t('Extension: An optional extension'),
);
}
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);
// Setup the standard Field API array for saving.
$delta = 0;
foreach ($values as $value) {
$return[$language][$delta] = array('country_codes' => drupal_strtolower($value));
if (isset($arguments['number'])) {
if (is_array($arguments['number'])) {
$return[$language][$delta]['number'] = $arguments['number'][$delta];
}
else {
$return[$language][$delta]['number'] = $arguments['number'];
}
}
if (isset($arguments['extension'])) {
if (is_array($arguments['extension'])) {
$return[$language][$delta]['extension'] = $arguments['extension'][$delta];
}
else {
$return[$language][$delta]['extension'] = $arguments['extension'];
}
}
$delta++;
}
return isset($return) ? $return : NULL;
}
}

View File

@@ -0,0 +1,275 @@
<?php
/**
* @file
* Support for entity types implementing the Entity API.
*/
/**
* Destination class implementing migration into entity types.
*
* To make entity properties that correspond to columns in the entity's base
* table available as FieldMapping destinations, they must be present in Entity
* API's entity property info and have setter callbacks defined. Because the
* EntityDefaultMetadataController doesn't add setter callbacks to the default
* entity property info it produces, the custom entity needs to provide this
* either in an implementation of hook_entity_property_info(), or via EntityAPI
* in a custom metadata controller class.
*/
class MigrateDestinationEntityAPI extends MigrateDestinationEntity {
/**
* Info about the current entity type.
*
* @var array
*/
protected $info;
/**
* Name of the entity id key (for example, nid for nodes).
*
* @var string
*/
protected $id;
/**
* Name of the entity revision key (for example, vid for nodes).
*
* @var string
*/
protected $revision;
/**
* Gets the schema for the base key(s) of an entity type.
*
* @param string $entity_type
* A Drupal entity type.
*/
static public function getKeySchema($entity_type = NULL) {
// Migrate UI invokes $destination->getKeySchema() without any parameters.
if (!$entity_type) {
if (isset($this)) {
if ($this instanceof MigrateDestination) {
$entity_type = $this->entityType;
}
elseif ($this instanceof Migration) {
$entity_type = $this->destination->entityType;
}
}
else {
return array();
}
}
$info = entity_get_info($entity_type);
$schema = drupal_get_schema($info['base table']);
$key = isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'];
$key_schema = $schema['fields'][$key];
$revision_key = isset($info['entity keys']['revision']) ? $info['entity keys']['revision'] : NULL;
$revision_schema = empty($revision_key) ? NULL : $schema['fields'][$revision_key];
// We can't have any form of serial fields here, since the mapping table
// already has it's own.
$key_schema['auto_increment'] = FALSE;
if ($key_schema['type'] == 'serial') {
$key_schema['type'] = 'int';
}
$return = array($key => $key_schema);
if (!empty($revision_key)) {
$return[$revision_key] = $revision_schema;
}
return $return;
}
/**
* Return an options array (language, text_format), used for creating fields.
*
* @param string $language
* @param string $text_format
*/
static public function options($language, $text_format) {
return compact('language', 'text_format');
}
/**
* Basic initialization
*
* @param string $entity_type
* @param string $bundle
* @param array $options
* Options (language, text_format) used for creating fields.
*/
public function __construct($entity_type, $bundle, array $options = array()) {
parent::__construct($entity_type, $bundle, $options);
$this->info = entity_get_info($entity_type);
$this->id = isset($this->info['entity keys']['name']) ? $this->info['entity keys']['name'] : $this->info['entity keys']['id'];
$this->revision = isset($this->info['entity keys']['revision']) ? $this->info['entity keys']['revision'] : NULL;
}
/**
* Returns a list of fields available to be mapped for entities attached to
* a particular 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.
*/
public function fields($migration = NULL) {
$properties = entity_get_property_info($this->entityType);
$fields = array();
foreach ($properties['properties'] as $name => $property_info) {
if (isset($property_info['setter callback'])) {
$fields[$name] = $property_info['description'];
}
}
// Then add in anything provided by handlers
$fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType, $this->bundle);
return $fields;
}
/**
* Deletes multiple entities.
*
* @param array $ids
* An array of entity ids of the entities to delete.
*/
public function bulkRollback(array $ids) {
migrate_instrument_start('entity_delete_multiple');
$this->prepareRollback($ids);
$result = entity_delete_multiple($this->entityType, $ids);
$this->completeRollback($ids);
migrate_instrument_stop('entity_delete_multiple');
return $result;
}
/**
* Imports a single entity.
*
* @param stdClass $entity
* Generic entity object, refilled with any fields mapped in the Migration.
* @param stdClass $row
* Raw source data object - passed through to prepare/complete handlers.
*
* @return array
* An array of key fields (entity id, and revision id if applicable) of the
* entity that was saved if successful. FALSE on failure.
*/
public function import(stdClass $entity, stdClass $row) {
$migration = Migration::currentMigration();
// Updating previously-migrated content?
if (isset($row->migrate_map_destid1)) {
if (isset($entity->{$this->id})) {
if ($entity->{$this->id} != $row->migrate_map_destid1) {
throw new MigrateException(t("Incoming id !id and map destination id !destid1 don't match",
array('!id' => $entity->{$this->id}, '!destid1' => $row->migrate_map_destid1)));
}
}
else {
$entity->{$this->id} = $row->migrate_map_destid1;
}
}
elseif ($migration->getSystemOfRecord() == Migration::SOURCE) {
unset($entity->{$this->id});
}
if (isset($row->migrate_map_destid2)) {
if (isset($entity->{$this->revision})) {
if ($entity->{$this->revision} != $row->migrate_map_destid2) {
throw new MigrateException(t("Incoming revision !id and map destination revision !destid2 don't match",
array('!id' => $entity->{$this->revision}, '!destid2' => $row->migrate_map_destid2)));
}
}
else {
$entity->{$this->revision} = $row->migrate_map_destid2;
}
}
if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
if (!isset($entity->{$this->id})) {
throw new MigrateException(t('System-of-record is DESTINATION, but no destination id provided'));
}
// Load the entity that's being updated, update its values, then
// substitute the (fake) passed in entity with that one.
$old_entity = entity_load_single($this->entityType, $entity->{$this->id});
if (empty($old_entity)) {
throw new MigrateException(t("Failed to load entity of type %type and id %id", array('%type' => $this->entityType, '%id' => $entity->{$this->id})));
}
// Prepare the entity to get the right array structure.
$this->prepare($entity, $row);
foreach ($entity as $field => $value) {
$old_entity->$field = $entity->$field;
}
$entity = $old_entity;
}
else {
// Create a real entity object, update its values with the ones we have
// and pass it along.
$new_entity = array();
if (!empty($this->bundle) && !empty($this->info['entity keys']['bundle'])) {
$new_entity[$this->info['entity keys']['bundle']] = $this->bundle;
}
$new_entity = entity_create($this->entityType, $new_entity);
foreach ($entity as $field => $value) {
$new_entity->$field = $entity->$field;
}
// If a destination id exists, the entity is obviously not new.
if (!empty($new_entity->{$this->id}) && isset($new_entity->is_new)) {
unset($new_entity->is_new);
}
$entity = $new_entity;
$this->prepare($entity, $row);
}
$updating = (!empty($entity->{$this->id}) && empty($entity->is_new));
migrate_instrument_start('entity_save');
entity_save($this->entityType, $entity);
// It's probably not worth keeping the static cache around.
entity_get_controller($this->entityType)->resetCache();
migrate_instrument_stop('entity_save');
$this->complete($entity, $row);
if (isset($entity->{$this->id}) && $entity->{$this->id} > 0) {
if ($updating) {
$this->numUpdated++;
}
else {
$this->numCreated++;
}
$return = array($entity->{$this->id});
if (isset($entity->{$this->revision}) && $entity->{$this->revision} > 0) {
$return[] = array($entity->{$this->revision});
}
return $return;
}
return FALSE;
}
/**
* Clear the field cache after an import or rollback.
*/
public function postImport() {
field_cache_clear();
}
public function postRollback() {
field_cache_clear();
}
}

View File

@@ -0,0 +1,164 @@
<?php
/**
* @file
* Flag module integration
*/
/**
* Destination class implementing when you want just an insert into flag_content table.
*/
class MigrateDestinationFlagSimple extends MigrateDestination {
public function __construct($fid) {
parent::__construct();
$this->fid = $fid;
}
public function __toString() {
$flag = flag_get_flag(NULL, $this->fid);
return t('flag (!flag)', array('!flag' => $flag->name));
}
static public function getKeySchema() {
return array(
'fcid' => array(
'type' => 'int',
'not null' => TRUE,
),
);
}
/**
* Delete a membership.
*
* @param $id
* IDs to be deleted.
*/
public function bulkRollback(array $ids) {
migrate_instrument_start(__METHOD__);
db_delete('flag_content')
->condition('fcid', $ids, 'IN')
->execute();
migrate_instrument_stop(__METHOD__);
}
/**
* Import a single flag_content.
*
* @param $entity
* 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.
*/
public function import(stdClass $entity, stdClass $row) {
if (isset($entity->timestamp)) {
$entity->timestamp = Migration::timestamp($entity->timestamp);
}
$entity->fid = $this->fid;
if (!empty($entity->fcid)) {
$return = drupal_write_record('flag_content', $entity, 'fcid');
}
else {
$return = drupal_write_record('flag_content', $entity);
}
if ($return) {
// Update the flag_counts table.
$count = db_select('flag_content')
->fields('flag_content')
->condition('fid', $this->fid)
->condition('content_type', $entity->content_type)
->condition('content_id', $entity->content_id)
->countQuery()
->execute()
->fetchField();
db_merge('flag_counts')
->key(array(
'fid' => $this->fid,
'content_id' => $entity->content_id,
))
->fields(array(
'content_type' => $entity->content_type,
'count' => $count,
'last_updated' => REQUEST_TIME,
))
->execute();
return array($entity->fcid);
}
}
public function fields() {
return array(
'fcid' => 'Flag content ID',
'fid' => 'Flag ID',
'content_type' => '',
'content_id' => '',
'uid' => 'User ID',
'timestamp' => '',
);
}
}
/**
* Field handler - this will expose flags as fields on the object they're
* attached to, and set the flag to the value mapped in addFieldMapping().
*/
abstract class MigrateExtrasFlagHandler extends MigrateDestinationHandler {
/**
* Make the flags assigned to this object visible.
*/
public function fields($entity_type, $bundle) {
$fields = array();
if (module_exists('flag')) {
$flags = flag_get_flags($entity_type, $bundle);
foreach ($flags as $flag_name => $flag) {
$fields[$flag_name] = $flag->title;
}
}
return $fields;
}
}
/**
* Because we can't identify what kind of entity is passed to complete, we
* implement a separate handler for each type.
*/
class MigrateExtrasNodeFlagHandler extends MigrateExtrasFlagHandler {
public function __construct() {
$this->registerTypes(array('node'));
}
public function complete($node, stdClass $row) {
if (!module_exists('flag')) return;
$flags = flag_get_flags('node', $node->type);
$fields = array();
foreach ($flags as $flag_name => $flag) {
if (!empty($node->$flag_name)) {
flag('flag', $flag_name, $node->nid);
}
}
}
}
class MigrateExtrasUserFlagHandler extends MigrateExtrasFlagHandler {
public function __construct() {
$this->registerTypes(array('user'));
}
public function complete($user, stdClass $row) {
if (!module_exists('flag')) return;
$flags = flag_get_flags('user', 'user');
$fields = array();
foreach ($flags as $flag_name => $flag) {
if (!empty($user->$flag_name)) {
flag('flag', $flag_name, $user->uid);
}
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* Primary value passed to this field must be the geometry type of the geofield: Point, LineString, Polygon
*
* Arguments are used to specify all the other values:
* 'wkt' - Well Known Text
* 'lat' - Latitude.
* 'lon' - Longitude
* 'top' - Top Latitude
* 'bottom' - Bottom Latitude
* 'right' - Right Longitude
* 'left' - Left Longitude
*
* Add the source field mappings to the argument array then add null mappings to
* avoid having fields flagged as as unmapped:
* @code
* $geo_arguments = array(
* 'lat' => array('source_field' => 'source_field_latidute'),
* 'lon' => array('source_field' => 'source_field_longitude'),
* );
* // The geometry type should be passed in as the primary value.
* $this->addFieldMapping('field_geofield_dest', 'field_source_geotype')
* ->arguments($geo_arguments);
* // Since the excerpt is mapped via an argument, add a null mapping so it's
* // not flagged as unmapped.
* $this->addFieldMapping(NULL, 'source_field_latitude');
* $this->addFieldMapping(NULL, 'source_field_longitude');
* @endcode
*/
class MigrateGeoFieldHandler extends MigrateFieldHandler {
public function __construct() {
$this->registerTypes(array('geofield'));
}
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);
// Setup the standard Field API array for saving.
$delta = 0;
foreach ($values as $value) {
$return[$language][$delta] = array('geo_type' => $value) + array_intersect_key($arguments, $field_info['columns']);
$delta++;
}
return isset($return) ? $return : NULL;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* Primary value passed to this field must be interval value itself (number of
* periods).
*
* Pass the period as an argument, e.g.:
*
* @code
* $arguments = array('period' => 'month');
* $this->addFieldMapping('field_subscription_duration', 'num_months')
* ->arguments($arguments);
* @endcode
*/
class MigrateIntervalFieldHandler extends MigrateFieldHandler {
public function __construct() {
$this->registerTypes(array('interval'));
}
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);
// Setup the standard Field API array for saving.
$delta = 0;
foreach ($values as $value) {
$return[$language][$delta] = array(
'interval' => $value,
'period' => $arguments['period'],
);
$delta++;
}
return isset($return) ? $return : NULL;
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* Destination class implementing migration into media entities.
*/
class MigrateDestinationMedia extends MigrateDestinationFile {
/**
* Call this from the prepare() method of a migration that contains media
* image files, if you want to rewrite the IMG tags into media references.
*
* @param $entity
* Entity object being built.
* @param $field
* Name of the text field within the entity to be modified. Defaults to 'body'.
*/
static public function rewriteImgTags($entity, $field = 'body') {
if (is_array($entity->$field)) {
migrate_instrument_start('MigrateDestinationMedia rewriteImgTags');
foreach ($entity->$field as $language => $values) {
$body = $values[0]['value'];
break;
}
// Quickly skip any non-candidates
if (!stristr($body, '<img')) {
migrate_instrument_stop('MigrateDestinationMedia rewriteImgTags');
return;
}
// Pass full img tags into the callback
$new_body = preg_replace_callback('|<img [^>]*>|i', array(self, 'replaceCallback'),
$body);
$entity->{$field}[$language][0]['value'] = $new_body;
migrate_instrument_stop('MigrateDestinationMedia rewriteImgTags');
}
}
/**
* If a referenced image can be found in the files table, replace the <img> tag
* with a media JSON reference.
*
* @param array $matches
*/
static protected function replaceCallback(array $matches) {
$src_matches = array();
// Default to the original <img> tag
$result = $matches[0];
// Extract the src parameter
if (preg_match('|src=[\'"]([^\'"]+)[\'"]|i', $matches[0], $src_matches)) {
// Replace the scheme and host portions of the referenced URI with the
// Drupal scheme as it's saved in the file_unmanaged table
$src = $src_matches[1];
$fid = db_select('file_managed', 'f')
->fields('f', array('fid'))
->condition('filename', basename($src))
->execute()
->fetchField();
if ($fid) {
$image_info = array(
'type' => 'media',
'view_mode' => 'media_large',
'fid' => $fid,
'attributes' => array(
'alt' => '',
'title' => '',
'class' => 'media-image',
'typeof' => 'foaf:Image',
'wysiwyg' => 1,
),
);
// Get the height and width parameters if present
preg_match('|width=[\'"]([^\'"]+)[\'"]|i', $matches[0], $width);
preg_match('|height=[\'"]([^\'"]+)[\'"]|i', $matches[0], $height);
// image width
if ($width) {
$image_info['attributes']['width'] = $width[1];
}
// image height
if ($height) {
$image_info['attributes']['height'] = $height[1];
}
$result = '[[' . drupal_json_encode($image_info) . ']]';
}
}
return $result;
}
}
/**
* Class for creating Youtube file entities.
*/
class MigrateExtrasFileYoutube implements MigrateFileInterface {
/**
* Implementation of MigrateFileInterface::fields().
*
* @return array
*/
static public function fields() {
return array();
}
/**
* Implementation of MigrateFileInterface::processFiles().
*
* @param $value
* 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.
*/
public function processFile($value, $owner) {
// Convert the Youtube URI into a local stream wrapper.
if (class_exists('MediaInternetYouTubeHandler')) {
$handler = new MediaInternetYouTubeHandler($value);
$destination = $handler->parse($value);
$file = file_uri_to_object($destination, TRUE);
}
elseif (class_exists('MediaInternetOEmbedHandler')) {
$handler = new MediaInternetOEmbedHandler($value);
$file = $handler->getFileObject();
}
else {
MigrationBase::displayMessage(t('Could not find a handler for YouTube videos'));
return FALSE;
}
// Create a file entity object for this Youtube reference, and see if we
// can get the video title.
if (empty($file->fid) && $info = $handler->getOEmbed()) {
$file->filename = truncate_utf8($info['title'], 255);
}
$file = file_save($file);
if (is_object($file)) {
return $file;
}
else {
return FALSE;
}
}
}
// Basic support for the deprecated media field type.
class MigrateMediaFieldHandler extends MigrateFileFieldHandler {
public function __construct() {
$this->registerTypes(array('media'));
}
/**
* Implementation of MigrateFieldHandler::fields().
*
* @param $type
* The field type.
* @param $instance
* Instance info for the field.
* @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);
unset($fields['description']);
unset($fields['display']);
$fields += array(
'title' => t('Subfield: String to be used as the title value'),
'data' => t('Subfield: Additional data about the field'),
);
return $fields;
}
}

View File

@@ -0,0 +1,29 @@
name = Migrate Extras
description = "Adds migrate module integration with contrib modules and other miscellaneous tweaks."
core = 7.x
package=Development
dependencies[] = migrate
files[] = addressfield.inc
files[] = cck_phone.inc
files[] = entity_api.inc
files[] = flag.inc
files[] = geofield.inc
files[] = interval.inc
files[] = media.inc
files[] = name.inc
files[] = pathauto.inc
files[] = privatemsg.inc
files[] = profile2.inc
files[] = rules.inc
files[] = user_relationships.inc
files[] = votingapi.inc
files[] = webform.inc
files[] = tests/pathauto.test
; Information added by drupal.org packaging script on 2012-11-07
version = "7.x-2.5"
core = "7.x"
project = "migrate_extras"
datestamp = "1352299013"

View File

@@ -0,0 +1,24 @@
<?php
/*
* Implementation of hook_migrate_api().
*/
function migrate_extras_migrate_api() {
$api = array(
'api' => 2,
'destination handlers' => array(
'MigratePathautoHandler',
'MigrateExtrasNodeFlagHandler',
'MigrateExtrasUserFlagHandler',
),
'field handlers' => array(
'MigrateAddressFieldHandler',
'MigrateCckPhoneHandler',
'MigrateGeoFieldHandler',
'MigrateIntervalFieldHandler',
'MigrateNameHandler',
'MigrateMediaFieldHandler',
)
);
return $api;
}

View File

@@ -0,0 +1,10 @@
<?php
function migrate_extras_init() {
// Set variable to FALSE to disable rules processing.
if (module_exists('rules') && !variable_get('migrate_extras_rules', TRUE)) {
foreach (rules_get_event_sets() as $key => $set) {
$GLOBALS['conf']['rules_inactive_sets'][] = $key;
}
}
}

View File

@@ -0,0 +1,345 @@
<?php
/**
* @file
* migrate_extras_media.features.field.inc
*/
/**
* Implements hook_field_default_fields().
*/
function migrate_extras_media_field_default_fields() {
$fields = array();
// Exported field: 'node-migrate_extras_media_example-body'
$fields['node-migrate_extras_media_example-body'] = array(
'field_config' => array(
'active' => '1',
'cardinality' => '1',
'deleted' => '0',
'entity_types' => array(
0 => 'node',
),
'field_name' => 'body',
'foreign keys' => array(
'format' => array(
'columns' => array(
'format' => 'format',
),
'table' => 'filter_format',
),
),
'indexes' => array(
'format' => array(
0 => 'format',
),
),
'module' => 'text',
'settings' => array(),
'translatable' => '1',
'type' => 'text_with_summary',
),
'field_instance' => array(
'bundle' => 'migrate_extras_media_example',
'default_value' => NULL,
'deleted' => '0',
'description' => '',
'display' => array(
'default' => array(
'label' => 'hidden',
'module' => 'text',
'settings' => array(),
'type' => 'text_default',
'weight' => 0,
),
'teaser' => array(
'label' => 'hidden',
'module' => 'text',
'settings' => array(
'trim_length' => 600,
),
'type' => 'text_summary_or_trimmed',
'weight' => 0,
),
),
'entity_type' => 'node',
'field_name' => 'body',
'label' => 'Body',
'required' => FALSE,
'settings' => array(
'display_summary' => TRUE,
'text_processing' => 1,
'user_register_form' => FALSE,
),
'widget' => array(
'module' => 'text',
'settings' => array(
'rows' => 20,
'summary_rows' => 5,
),
'type' => 'text_textarea_with_summary',
'weight' => '31',
),
),
);
// Exported field: 'node-migrate_extras_media_example-field_document'
$fields['node-migrate_extras_media_example-field_document'] = array(
'field_config' => array(
'active' => '1',
'cardinality' => '-1',
'deleted' => '0',
'entity_types' => array(),
'field_name' => 'field_document',
'foreign keys' => array(
'fid' => array(
'columns' => array(
'fid' => 'fid',
),
'table' => 'file_managed',
),
),
'indexes' => array(
'fid' => array(
0 => 'fid',
),
),
'module' => 'file',
'settings' => array(
'display_default' => 0,
'display_field' => 0,
'uri_scheme' => 'public',
),
'translatable' => '0',
'type' => 'file',
),
'field_instance' => array(
'bundle' => 'migrate_extras_media_example',
'deleted' => '0',
'description' => '',
'display' => array(
'default' => array(
'label' => 'above',
'module' => 'file',
'settings' => array(),
'type' => 'file_default',
'weight' => 3,
),
'teaser' => array(
'label' => 'above',
'settings' => array(),
'type' => 'hidden',
'weight' => 0,
),
),
'entity_type' => 'node',
'field_name' => 'field_document',
'label' => 'Document',
'required' => 0,
'settings' => array(
'description_field' => 0,
'file_directory' => '',
'file_extensions' => 'pdf doc docx txt',
'max_filesize' => '',
'user_register_form' => FALSE,
),
'widget' => array(
'active' => 1,
'module' => 'media',
'settings' => array(
'allowed_schemes' => array(
'private' => 0,
'public' => 'public',
'wordpress' => 0,
'youtube' => 0,
),
'allowed_types' => array(
'audio' => 0,
'default' => 'default',
'image' => 0,
'video' => 0,
),
'progress_indicator' => 'throbber',
),
'type' => 'media_generic',
'weight' => '34',
),
),
);
// Exported field: 'node-migrate_extras_media_example-field_media_image'
$fields['node-migrate_extras_media_example-field_media_image'] = array(
'field_config' => array(
'active' => '1',
'cardinality' => '1',
'deleted' => '0',
'entity_types' => array(),
'field_name' => 'field_media_image',
'foreign keys' => array(
'fid' => array(
'columns' => array(
'fid' => 'fid',
),
'table' => 'file_managed',
),
),
'indexes' => array(
'fid' => array(
0 => 'fid',
),
),
'module' => 'file',
'settings' => array(
'display_default' => 0,
'display_field' => 0,
'uri_scheme' => 'public',
),
'translatable' => '0',
'type' => 'file',
),
'field_instance' => array(
'bundle' => 'migrate_extras_media_example',
'deleted' => '0',
'description' => '',
'display' => array(
'default' => array(
'label' => 'above',
'module' => 'file',
'settings' => array(),
'type' => 'file_default',
'weight' => 2,
),
'teaser' => array(
'label' => 'above',
'settings' => array(),
'type' => 'hidden',
'weight' => 0,
),
),
'entity_type' => 'node',
'field_name' => 'field_media_image',
'label' => 'Media image',
'required' => 0,
'settings' => array(
'description_field' => 0,
'file_directory' => '',
'file_extensions' => 'jpg jpeg gif png',
'max_filesize' => '',
'user_register_form' => FALSE,
),
'widget' => array(
'active' => 1,
'module' => 'media',
'settings' => array(
'allowed_schemes' => array(
'private' => 0,
'public' => 'public',
'wordpress' => 0,
'youtube' => 0,
),
'allowed_types' => array(
'audio' => 0,
'default' => 0,
'image' => 'image',
'video' => 0,
),
'progress_indicator' => 'throbber',
),
'type' => 'media_generic',
'weight' => '33',
),
),
);
// Exported field: 'node-migrate_extras_media_example-field_youtube_video'
$fields['node-migrate_extras_media_example-field_youtube_video'] = array(
'field_config' => array(
'active' => '1',
'cardinality' => '-1',
'deleted' => '0',
'entity_types' => array(),
'field_name' => 'field_youtube_video',
'foreign keys' => array(
'fid' => array(
'columns' => array(
'fid' => 'fid',
),
'table' => 'file_managed',
),
),
'indexes' => array(
'fid' => array(
0 => 'fid',
),
),
'module' => 'file',
'settings' => array(
'display_default' => 0,
'display_field' => 0,
'uri_scheme' => 'public',
),
'translatable' => '0',
'type' => 'file',
),
'field_instance' => array(
'bundle' => 'migrate_extras_media_example',
'deleted' => '0',
'description' => '',
'display' => array(
'default' => array(
'label' => 'above',
'module' => 'file',
'settings' => array(),
'type' => 'file_default',
'weight' => 1,
),
'teaser' => array(
'label' => 'above',
'settings' => array(),
'type' => 'hidden',
'weight' => 0,
),
),
'entity_type' => 'node',
'field_name' => 'field_youtube_video',
'label' => 'Youtube video',
'required' => 0,
'settings' => array(
'description_field' => 0,
'file_directory' => '',
'file_extensions' => 'txt',
'max_filesize' => '',
'user_register_form' => FALSE,
),
'widget' => array(
'active' => 1,
'module' => 'media',
'settings' => array(
'allowed_schemes' => array(
'private' => 0,
'public' => 0,
'wordpress' => 0,
'youtube' => 'youtube',
),
'allowed_types' => array(
'audio' => 0,
'default' => 0,
'image' => 0,
'video' => 'video',
),
'progress_indicator' => 'throbber',
),
'type' => 'media_generic',
'weight' => '32',
),
),
);
// Translatables
// Included for use with string extractors like potx.
t('Body');
t('Document');
t('Media image');
t('Youtube video');
return $fields;
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* @file
* migrate_extras_media.features.inc
*/
/**
* Implements hook_node_info().
*/
function migrate_extras_media_node_info() {
$items = array(
'migrate_extras_media_example' => array(
'name' => t('Migrate Extras Media Example'),
'base' => 'node_content',
'description' => t('Content type for testing Migrate Extras support for the Media module.'),
'has_title' => '1',
'title_label' => t('Title'),
'help' => '',
),
);
return $items;
}

View File

@@ -0,0 +1,23 @@
core = "7.x"
dependencies[] = "features"
dependencies[] = "file"
dependencies[] = "media"
dependencies[] = "media_youtube"
dependencies[] = "migrate"
dependencies[] = "migrate_extras"
description = "Examples for migrating Media"
features[field][] = "node-migrate_extras_media_example-body"
features[field][] = "node-migrate_extras_media_example-field_document"
features[field][] = "node-migrate_extras_media_example-field_media_image"
features[field][] = "node-migrate_extras_media_example-field_youtube_video"
features[node][] = "migrate_extras_media_example"
files[] = migrate_extras_media.migrate.inc
name = "Migrate Extras Media"
package = "Migrate Examples"
; Information added by drupal.org packaging script on 2012-11-07
version = "7.x-2.5"
core = "7.x"
project = "migrate_extras"
datestamp = "1352299013"

View File

@@ -0,0 +1,247 @@
<?php
/**
* @file
* Examples and test fodder for migration of Media entities and fields.
*/
/**
* Migration class for media images.
*/
class MigrateExampleMediaImageMigration extends XMLMigration {
public function __construct() {
parent::__construct();
$this->description = t('Example migration of media images');
$this->map = new MigrateSQLMap($this->machineName,
array(
'filename' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'description' => 'Image filename',
)
),
MigrateDestinationMedia::getKeySchema()
);
// Source fields available in the XML file.
$fields = array(
'filename' => t('Image filename, relative to the source directory'),
'description' => t('Description of the image'),
);
// Our test data is in an XML file
$xml_folder = drupal_get_path('module', 'migrate_extras_media');
$items_url = $xml_folder . '/migrate_extras_media.xml';
$item_xpath = '/source_data/item/image';
$item_ID_xpath = 'filename';
$items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
$this->source = new MigrateSourceMultiItems($items_class, $fields);
// In the simplest case, just pass the media type.
$this->destination = new MigrateDestinationMedia('image');
// The source images are in a local directory - specify the parent.
$this->addFieldMapping('source_dir')
->defaultValue(drupal_get_path('module', 'migrate_extras_media') . '/source_files');
// The 'value' of the media destination is mapped to the source field
// representing the media itself - in this case, a filename relative to
// source_dir.
$this->addFieldMapping('value', 'filename')
->xpath('filename');
// Fields on the entity can be mapped in the usual way.
$this->addFieldMapping('field_image_description', 'description')
->xpath('description');
$this->addFieldMapping('uid')
->defaultValue(1);
$this->addUnmigratedDestinations(array('field_image_description:format',
'field_image_description:language', 'destination_dir', 'destination_file',
'file_replace', 'preserve_files', 'timestamp'));
if (module_exists('path')) {
$this->addUnmigratedDestinations(array('path'));
}
}
}
/**
* Migration class for media_youtube entities.
*/
class MigrateExampleMediaVideoMigration extends XMLMigration {
public function __construct() {
parent::__construct();
$this->description = t('Example migration of Youtube videos');
$this->map = new MigrateSQLMap($this->machineName,
array(
'uri' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'description' => 'YouTube URI',
)
),
MigrateDestinationMedia::getKeySchema()
);
// Source fields available in the XML file.
$fields = array(
'uri' => t('URI of a YouTube video'),
'description' => t('Description of the video'),
);
// Our test data is in an XML file
$xml_folder = drupal_get_path('module', 'migrate_extras_media');
$items_url = $xml_folder . '/migrate_extras_media.xml';
$item_xpath = '/source_data/item/video';
$item_ID_xpath = 'uri';
$items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
$this->source = new MigrateSourceMultiItems($items_class, $fields);
// In this case, we need to specify the file_class in the second paramter -
// this class understands how to translate a http://www.youtube.com/ URI
// into Drupal's Youtube file scheme (youtube://).
$this->destination = new MigrateDestinationMedia('video',
'MigrateExtrasFileYoutube');
// We just need to map the 'value' in the media destination to the Youtube
// URI.
$this->addFieldMapping('value', 'uri')
->xpath('uri');
$this->addFieldMapping('field_video_description', 'description')
->xpath('description');
$this->addFieldMapping('uid')
->defaultValue(1);
$this->addUnmigratedDestinations(array('field_video_description:format',
'field_video_description:language', 'timestamp'));
if (module_exists('path')) {
$this->addUnmigratedDestinations(array('path'));
}
}
}
/**
* Migration class for nodes with media fields.
*/
class MigrateExampleMediaNodeMigration extends XMLMigration {
public function __construct() {
parent::__construct();
$this->description = t('Example migration into the Media module');
$this->dependencies =
array('MigrateExampleMediaImage', 'MigrateExampleMediaVideo');
$this->map = new MigrateSQLMap($this->machineName,
array(
'id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Media ID',
)
),
MigrateDestinationNode::getKeySchema()
);
// Source fields available in the XML file.
$fields = array(
'id' => t('Source id'),
'title' => t('Title'),
'body' => t('Description'),
'video_uri' => t('A YouTube URI'),
'video_description' => t('Description for a YouTube video'),
'image_filename' => t('Image filename'),
'image_description' => t('Image description'),
'document_filename' => t('Document filename'),
);
// Our test data is in an XML file
$xml_folder = drupal_get_path('module', 'migrate_extras_media');
$items_url = $xml_folder . '/migrate_extras_media.xml';
$item_xpath = '/source_data/item';
$item_ID_xpath = 'id';
$items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
$this->source = new MigrateSourceMultiItems($items_class, $fields);
$this->destination = new MigrateDestinationNode('migrate_extras_media_example');
// Basic fields
$this->addFieldMapping('title', 'title')
->xpath('title');
$this->addFieldMapping('uid')
->defaultValue(1);
$this->addFieldMapping('body', 'body')
->xpath('body');
$this->addFieldMapping('body:format')
->defaultValue('filtered_html');
// The image and Youtube media entities are imported via their own
// migrations above, we just need to link the fields to them. We do this
// by mapping the primary keys of those migrations (URL and filename) to
// the primary field values, and specifying a file_class of MigrateFileFid.
$this->addFieldMapping('field_youtube_video', 'video_uri')
->xpath('video/uri')
->sourceMigration('MigrateExampleMediaVideo');
$this->addFieldMapping('field_youtube_video:file_class')
->defaultValue('MigrateFileFid');
$this->addFieldMapping('field_media_image', 'image_filename')
->xpath('image/filename')
->sourceMigration('MigrateExampleMediaImage');
$this->addFieldMapping('field_media_image:file_class')
->defaultValue('MigrateFileFid');
// We have not created a separate migration for documents, we're using the
// file field handler to get those. This works just like it does for regular
// file fields.
$this->addFieldMapping('field_document', 'document_filename')
->xpath('document/filename');
// This isn't technically necessary - MigrateFileUri is the default
$this->addFieldMapping('field_document:file_class')
->defaultValue('MigrateFileUri');
$this->addFieldMapping('field_document:source_dir')
->defaultValue(drupal_get_path('module', 'migrate_extras_media') . '/source_files');
$this->addFieldMapping('field_document:destination_file', 'document_filename')
->xpath('document/filename');
// Unmapped destination fields
$this->addUnmigratedDestinations(array('is_new', 'status', 'promote',
'revision', 'language', 'sticky', 'created', 'changed', 'revision_uid',
'log', 'tnid', 'body:summary', 'body:language',
'comment'));
$this->addUnmigratedDestinations(array('field_media_image:language',
'field_media_image:display', 'field_media_image:description',
'field_youtube_video:language', 'field_youtube_video:description',
'field_youtube_video:display', 'field_document:language', 'field_document:destination_dir',
'field_document:file_replace', 'field_document:preserve_files',
'field_document:description', 'field_document:display'));
if (module_exists('path')) {
$this->addUnmigratedDestinations(array('path'));
if (module_exists('pathauto')) {
$this->addUnmigratedDestinations(array('pathauto'));
}
}
$this->addUnmigratedSources(array('image_description', 'video_description'));
}
/**
* Implementation of Migration::prepare().
*/
public function prepare($node, $row) {
// This will replace any <img> tags in the body with the media module's
// JSON references.
MigrateDestinationMedia::rewriteImgTags($node);
}
}
/*
* Implementation of hook_migrate_api().
*/
function migrate_extras_media_migrate_api() {
$api = array(
'api' => 2,
);
return $api;
}

View File

@@ -0,0 +1,7 @@
<?php
/**
* @file
* Code for the Migrate Extras Media feature.
*/
include_once('migrate_extras_media.features.inc');

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<source_data>
<item>
<id>3</id>
<title>Simple example</title>
<body>This is pretty straight-forward.</body>
<document>
<filename>docs/test_doc.txt</filename>
</document>
<video>
<uri>http://www.youtube.com/watch?v=tgbNymZ7vqY</uri>
<description>Best video ever</description>
</video>
</item>
<item>
<id>8</id>
<title>Another example</title>
<body><![CDATA[So is this. Well, except for the image here, which we want to translate
from an &lt;img&gt; tag to Media module tagging:
<img src="images/watchdog-ok.png" />]]>
</body>
<document>
<filename>docs/random.docx</filename>
</document>
<image>
<filename>images/watchdog-ok.png</filename>
<description>A simple checkmark</description>
</image>
<video>
<uri>http://www.youtube.com/watch?v=kfVsfOSbJY0</uri>
<description>NOT the best video ever</description>
</video>
</item>
</source_data>

View File

@@ -0,0 +1,86 @@
<?php
/**
* Implementation of hook_field_default_fields().
*/
function migrate_extras_pathauto_field_default_fields() {
$fields = array();
// Exported field: 'node-migrate_example_pathauto-body'
$fields['node-migrate_example_pathauto-body'] = array(
'field_config' => array(
'active' => '1',
'cardinality' => '1',
'deleted' => '0',
'entity_types' => array(
'0' => 'node',
),
'field_name' => 'body',
'foreign keys' => array(
'format' => array(
'columns' => array(
'format' => 'format',
),
'table' => 'filter_format',
),
),
'indexes' => array(
'format' => array(
'0' => 'format',
),
),
'module' => 'text',
'settings' => array(),
'translatable' => '1',
'type' => 'text_with_summary',
),
'field_instance' => array(
'bundle' => 'migrate_example_pathauto',
'default_value' => NULL,
'deleted' => '0',
'description' => '',
'display' => array(
'default' => array(
'label' => 'hidden',
'module' => 'text',
'settings' => array(),
'type' => 'text_default',
'weight' => 0,
),
'teaser' => array(
'label' => 'hidden',
'module' => 'text',
'settings' => array(
'trim_length' => 600,
),
'type' => 'text_summary_or_trimmed',
'weight' => 0,
),
),
'entity_type' => 'node',
'field_name' => 'body',
'label' => 'Body',
'required' => FALSE,
'settings' => array(
'display_summary' => TRUE,
'text_processing' => 1,
'user_register_form' => FALSE,
),
'widget' => array(
'module' => 'text',
'settings' => array(
'rows' => 20,
'summary_rows' => 5,
),
'type' => 'text_textarea_with_summary',
'weight' => -4,
),
),
);
// Translatables
// Included for use with string extractors like potx.
t('Body');
return $fields;
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* Implementation of hook_node_info().
*/
function migrate_extras_pathauto_node_info() {
$items = array(
'migrate_example_pathauto' => array(
'name' => t('Migrate example - pathauto'),
'base' => 'node_content',
'description' => t('This content type is used for demonstrating and testing Migrate Example support for the Pathauto module.'),
'has_title' => '1',
'title_label' => t('Title'),
'help' => '',
),
);
return $items;
}

View File

@@ -0,0 +1,19 @@
core = "7.x"
dependencies[] = "features"
dependencies[] = "migrate_extras"
dependencies[] = "pathauto"
description = "Examples of migrating with the Pathauto module"
features[field][] = "node-migrate_example_pathauto-body"
features[node][] = "migrate_example_pathauto"
files[] = migrate_extras_pathauto.migrate.inc
name = "Migrate Extras Pathauto Example"
package = "Migrate Examples"
project = "migrate_extras_pathauto_example"
version = "7.x-2.1-beta1"
; Information added by drupal.org packaging script on 2012-11-07
version = "7.x-2.5"
core = "7.x"
project = "migrate_extras"
datestamp = "1352299013"

View File

@@ -0,0 +1,60 @@
<?php
/**
* @file
* Examples and test fodder for migration with Pathauto enabled.
*/
/**
* Migration class to test import with Pathauto enabled.
*/
class MigrateExamplePathautoMigration extends XMLMigration {
public function __construct() {
parent::__construct();
$this->description = t('Example migration with Pathauto enabled');
$this->map = new MigrateSQLMap($this->machineName,
array(
'id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Pathauto ID',
)
),
MigrateDestinationNode::getKeySchema()
);
// Source fields available in the XML file.
$fields = array(
'id' => t('Source id'),
'title' => t('Title'),
'body' => t('Description'),
);
// Our test data is in an XML file
$xml_folder = drupal_get_path('module', 'migrate_extras_pathauto');
$items_url = $xml_folder . '/migrate_extras_pathauto.xml';
$item_xpath = '/source_data/item';
$item_ID_xpath = 'id';
$items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
$this->source = new MigrateSourceMultiItems($items_class, $fields);
$this->destination = new MigrateDestinationNode('migrate_example_pathauto');
// Basic fields
$this->addFieldMapping('title', 'title')
->xpath('title');
$this->addFieldMapping('uid')
->defaultValue(1);
$this->addFieldMapping('body', 'body')
->xpath('body');
// Disable application of pathauto during migration
$this->addFieldMapping('pathauto')
->defaultValue(FALSE);
// Unmapped destination fields
$this->addUnmigratedDestinations(array('is_new', 'status', 'promote',
'revision', 'language', 'sticky', 'created', 'changed', 'revision_uid',
'path'));
}
}

View File

@@ -0,0 +1,13 @@
<?php
include_once('migrate_extras_pathauto.features.inc');
/*
* Implementation of hook_migrate_api().
*/
function migrate_extras_pathauto_migrate_api() {
$api = array(
'api' => 2,
);
return $api;
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<source_data>
<item>
<id>3</id>
<title>Simple example</title>
<body>This is pretty straight-forward.</body>
</item>
<item>
<id>8</id>
<title>Another example</title>
<body>So is this.</body>
</item>
</source_data>

View File

@@ -0,0 +1,14 @@
core = "7.x"
dependencies[] = "migrate_extras"
dependencies[] = "profile2"
description = "Examples of migrating into Profile2 entities"
files[] = migrate_extras_profile2.migrate.inc
name = "Migrate Extras Profile2 Example"
package = "Migrate Examples"
; Information added by drupal.org packaging script on 2012-11-07
version = "7.x-2.5"
core = "7.x"
project = "migrate_extras"
datestamp = "1352299013"

View File

@@ -0,0 +1,18 @@
<?php
function migrate_extras_profile2_install() {
$type = entity_create('profile2_type', array(
'type' => 'migrate_extras_profile2',
'label' => t('migrate_extras_profile2'),
'weight' => 0,
'data' => array('registration' => FALSE, 'use_page' => TRUE),
));
$type->save();
}
function migrate_extras_profile2_uninstall() {
if ($entities = entity_load_multiple_by_name('profile2_type', array('migrate_extras_profile2'))) {
list($id) = entity_extract_ids('profile2_type', reset($entities));
entity_delete('profile2_type', $id);
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* @file
* Examples and test fodder for migration into profile2 entities.
*/
/**
* Migration class to test import of various date fields.
*/
class MigrateExampleProfile2Migration extends Migration {
public function __construct() {
parent::__construct();
$this->description = t('Example migration into profile2 entities');
$this->map = new MigrateSQLMap($this->machineName,
array(
'id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
)
),
MigrateDestinationProfile2::getKeySchema()
);
// Our test data is in a CSV file
$this->source = new MigrateSourceCSV(__DIR__ . '/migrate_extras_profile2.csv', $this->csvcolumns(), array(), $this->fields());
$this->destination = new MigrateDestinationProfile2('migrate_extras_profile2');
$this->addFieldMapping('uid')
->defaultValue(1);
// Unmapped destination fields
$this->addUnmigratedDestinations(array('id'));
}
function csvcolumns() {
$columns[0] = array('id', 'Source ID');
$columns[1] = array('uid', 'User ID');
return $columns;
}
function fields() {
return array(
'id' => 'Source ID',
'uid' => 'User ID',
);
}
}

View File

@@ -0,0 +1,11 @@
<?php
/*
* Implementation of hook_migrate_api().
*/
function migrate_extras_profile2_migrate_api() {
$api = array(
'api' => 2,
);
return $api;
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Primary value passed to this field must be the 'given' name.
* it cannot be NULL, but may be an empty string.
*
* Arguments are used to specify all the other values:
* 'title' => array('source_field' => 'title'),
* 'middle' => array('source_field' => 'middle'),
* 'family' => array('source_field' => 'family'),
* 'generational' => array('source_field' => 'generational'),
* 'credentials' => array('source_field' => 'credentials'),
*
* Add the source field mappings to the argument array then add null mappings to
* avoid having fields flagged as as unmapped:
* @code
* $arguments = array(
* 'title' => array('source_field' => 'profile_title'),
* 'middle' => array('source_field' => 'profile_middle_name'),
* 'family' => array('source_field' => 'profile_last_name'),
* );
* // The given name should be passed in as the primary value.
* $this->addFieldMapping('field_name', 'profile_first_name')
* ->arguments($arguments);
* // Since the excerpt is mapped via an argument, add a null mapping so it's
* // not flagged as unmapped.
* $this->addFieldMapping(NULL, 'profile_title');
* $this->addFieldMapping(NULL, 'profile_middle_name');
* $this->addFieldMapping(NULL, 'profile_last_name');
* @endcode
*/
class MigrateNameHandler extends MigrateFieldHandler {
public function __construct() {
$this->registerTypes(array('name'));
}
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);
// Setup the standard Field API array for saving.
$delta = 0;
foreach ($values as $value) {
$return[$language][$delta] = array('given' => $value) + array_intersect_key($arguments, $field_info['columns']);
$delta++;
}
return isset($return) ? $return : NULL;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* @file
* Support for the Pathauto module.
*/
/**
* Field handler.
*/
class MigratePathautoHandler extends MigrateDestinationHandler {
public function __construct() {
$this->registerTypes(array('node', 'user', 'taxonomy_term'));
}
/**
* Make the destination field visible.
*/
public function fields() {
if (module_exists('pathauto')) {
return array(
'pathauto' =>
t('Pathauto: Perform aliasing (set to 0 to prevent alias generation during migration'),
);
}
else {
return array();
}
}
public function prepare($entity, stdClass $row) {
if (isset($entity->pathauto)) {
if (!isset($entity->path)) {
$entity->path = array();
}
elseif (is_string($entity->path)) {
// If MigratePathEntityHandler->prepare() hasn't run yet, support
// the alias (set as $entity->path as a string) being formatted properly
// in the path alias array.
$path = $entity->path;
$entity->path = array();
$entity->path['alias'] = $path;
}
$entity->path['pathauto'] = $entity->pathauto;
if (!isset($entity->path['alias'])) {
$entity->path['alias'] = '';
}
unset($entity->pathauto);
}
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* @file
* Privatemag module integration
*
* Limitations:
* - No updating of messages.
* - No threading
* - Messages are marked as deleted and not actually deleted. Thats the
* privatemsg API.
* - All these limitations can be helped by http://drupal.org/node/1184984.
*/
class MigrateDestinationPrivateMsg extends MigrateDestinationEntity {
/**
* An array with content ids of imported messages. Not yet used.
*/
var $importedIds = array();
static public function getKeySchema() {
return array(
'mid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
);
}
/**
* Basic initialization
*
* @param array $options
* Options applied to private messages.
*/
public function __construct(array $options = array()) {
parent::__construct('privatemsg_message', 'privatemsg_message', $options);
}
/**
* Returns a list of fields available to be mapped/
*
* @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(
// 'mid' => 'Message ID', // Updating not supported. See http://drupal.org/node/1184984.
'subject' => 'Subject',
'body' => 'Body',
'format' => 'Text format name for the Body',
'recipients' => 'User IDs of recipients',
'timestamp' => 'Timestamp',
'author' => 'User ID of author',
'is_new' => 'TRUE if unread by recipient, FALSE if read by recipient',
);
// Then add in anything provided by handlers
$fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType, $this->bundle);
$fields += migrate_handler_invoke_all('PrivateMsg', 'fields', $this->entityType, $this->bundle);
return $fields;
}
/**
* Mark provided message as deleted.
*
* @param $id
* IDs to be deleted.
*/
public function rollback(array $id) {
migrate_instrument_start(__METHOD__);
// Delete recipients of the message.
db_delete('pm_index')
->condition('mid', reset($id))
->execute();
// Delete message itself.
db_delete('pm_message')
->condition('mid', reset($id))
->execute();
migrate_instrument_stop(__METHOD__);
}
/**
* Import a single message.
*
* @param $entity
* 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.
*/
public function import(stdClass $entity, stdClass $row) {
$this->prepare($entity, $row);
// The privatemsg API does not support updating. See http://drupal.org/node/1184984
// $message['mid'] = $entity->mid;
// The two user_load() calls here could by slow. If so, one could experiment
// with entity cache module - http://drupal.org/project/entitycache.
$options = array();
if (isset($entity->timestamp)) $options['timestamp'] = Migration::timestamp($entity->timestamp);
if (isset($entity->author)) $options['author'] = user_load($entity->author);
if (isset($entity->format)) $options['format'] = $entity->format;
if (!is_array($entity->recipients)) {
$entity->recipients = array($entity->recipients);
}
foreach ($entity->recipients as $uid) {
$entity->to[] = user_load($uid);
}
// FYI, API is at http://api.worldempire.ch/api/privatemsg/privatemsg.module/function/privatemsg_new_thread/7-2
$return = privatemsg_new_thread($entity->to, $entity->subject, $entity->body, $options);
if (!empty($return['success'])) {
$this->complete((object)$return, $row);
// Set the read status for the recipient (defaults to unread, so only need
// to set if read)
$mid = $return['message']->mid;
if (isset($entity->is_new) && $entity->is_new == PRIVATEMSG_READ) {
foreach ($entity->to as $account) {
privatemsg_message_change_status($mid, $entity->is_new, $account);
}
}
return array($mid);
}
else {
$migration = Migration::currentMigration();
$migration->saveMessage(reset($return['messages']['error']));
return FALSE;
}
}
}

View File

@@ -0,0 +1,189 @@
<?php
/**
* @file
* Support for profile2 destinations.
*/
// TODO:
// Make sure this works with updates, explicit destination keys
/**
* Destination class implementing migration into nodes.
*/
class MigrateDestinationProfile2 extends MigrateDestinationEntity {
var $entity_type = 'profile2';
var $entity_info = NULL;
var $entity_key = NULL;
static public function getKeySchema() {
$key = 'pid'; // Hard coded since $this->entity_key not in object context.
return array(
$key => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
);
}
/**
* Return an options array for profile2 destinations.
*
* @param string $language
* Default language for profiles created via this destination class.
* @param string $text_format
* Default text format for profiles created via this destination class.
*/
static public function options($language, $text_format) {
return compact('language', 'text_format');
}
/**
* Basic initialization
*
* @param string $bundle
* A.k.a. the profile type.
* @param array $options
* Options applied to profiles.
*/
public function __construct($bundle, array $options = array()) {
parent::__construct($this->entity_type, $bundle, $options);
$this->entity_info = entity_get_info('profile2');
$this->entity_key = $this->entity_info['entity keys']['id'];
}
/**
* Returns a list of fields available to be mapped for the node type (bundle)
*
* @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();
$type = ucfirst($this->entity_type) . ': ';
// First the core (node table) properties
$fields[$this->entity_key] = $type . t('Existing profile ID');
$fields['uid'] = $type . t('Authored by (uid)');
$fields['revision_uid'] = $type . t('Modified (uid)');
//$fields['created'] = $type . t('Created timestamp');
//$fields['changed'] = $type . t('Modified timestamp');
//$fields['status'] = $type . t('Published');
//$fields['revision'] = $type . t('Create new revision');
$fields['language'] = $type . t('Language (fr, en, ...)');
// Then add in anything provided by handlers
$fields += migrate_handler_invoke_all('Entity', 'fields', $this->entityType, $this->bundle);
$fields += migrate_handler_invoke_all('Profile2', 'fields', $this->entityType, $this->bundle);
return $fields;
}
/**
* Delete a batch of profiles at once.
*
* @param $ids
* Array of profile IDs to be deleted.
*/
public function bulkRollback(array $ids) {
migrate_instrument_start('profile2_delete_multiple');
$this->prepareRollback($ids);
entity_delete_multiple($this->entity_type, $ids);
$this->completeRollback($ids);
migrate_instrument_stop('profile2_delete_multiple');
}
/**
* Import a single entity.
*
* @param $entity
* Entity 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 entity that was saved if
* successful. FALSE on failure.
*/
public function import(stdClass $entity, stdClass $row) {
$migration = Migration::currentMigration();
$type = $this->entity_info['entity keys']['bundle'];
$entity->$type = $this->bundle;
list($id, $vid, $bundle) = entity_extract_ids($this->entity_type, $entity);
// Updating previously-migrated content?
if (isset($row->migrate_map_destid1)) {
// Make sure is_new is off
$entity->is_new = FALSE;
if (!empty($id)) {
if ($id != $row->migrate_map_destid1) {
throw new MigrateException(t("Incoming id !id and map destination id !destid1 don't match",
array('!id' => $id, '!destid1' => $row->migrate_map_destid1)));
}
}
else {
$entity->{$this->entity_key} = $row->migrate_map_destid1;
}
}
if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
if (empty($id)) {
throw new MigrateException(t('System-of-record is DESTINATION, but no destination id provided'));
}
$old_entity = entity_load_single($this->entity_type, $id);
if (!isset($entity->created)) {
$entity->created = $old_entity->created;
}
if (!isset($entity->uid)) {
$entity->uid = $old_entity->uid;
}
}
// Invoke migration prepare handlers
$this->prepare($entity, $row);
// Trying to update an existing entity
if ($migration->getSystemOfRecord() == Migration::DESTINATION) {
// Incoming data overrides existing data.
foreach ($entity as $field => $value) {
$old_entity->$field = $value;
}
// Use the loaded entity from now on.
$entity = $old_entity;
}
else {
// Create a full profile class.
$entity = entity_create($this->entity_type, (array) $entity);
}
if (empty($id) && !(isset($entity->is_new) && $entity->is_new)) {
$updating = TRUE;
}
else {
$updating = FALSE;
}
migrate_instrument_start('entity_save');
entity_save($this->entity_type, $entity);
migrate_instrument_stop('entity_save');
list($id, $vid, $bundle) = entity_extract_ids($this->entity_type, $entity);
if (isset($id)) {
if ($updating) {
$this->numUpdated++;
}
else {
$this->numCreated++;
}
$return = array($id);
}
else {
$return = FALSE;
}
$this->complete($entity, $row);
return $return;
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* @file
* Rules module integration
*/
// Nothing yet. See migrate_extras_init() to disable rules during a migration.

View File

@@ -0,0 +1,38 @@
<?php
/**
* Test pathauto migration.
*/
class MigrateExtrasPathautoUnitTest extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Pathauto migration',
'description' => 'Test disabling of pathauto during migration',
'group' => 'Migrate',
);
}
function setUp() {
parent::setUp('migrate', 'migrate_extras', 'features', 'token', 'path',
'pathauto', 'migrate_extras_pathauto');
}
/**
* Verify that setting 'pathauto' to FALSE when migrating actually prevents
* pathauto from creating an alias.
*/
function testPathautoImport() {
$migration = Migration::getInstance('MigrateExamplePathauto');
$result = $migration->processImport();
$this->assertEqual($result, Migration::RESULT_COMPLETED,
t('Import returned RESULT_COMPLETED'));
$rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_pathauto'), TRUE);
$this->assertEqual(count($rawnodes), 2, t('Two sample nodes created'));
$count = db_select('url_alias', 'ua')
->fields('ua', array('source'))
->countQuery()
->execute()
->fetchField();
$this->assertEqual($count, 0, t('No aliases generated'));
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* @file
* Import User Relationships.
*/
/**
* Destination class implementing migration into user_relationships table.
*/
class MigrateDestinationUserRelationships extends MigrateDestination {
protected $typeID;
protected $typeName;
/**
* Construct a destination for the specified user relationship type. Interprets
* the type as a type name - if that fails, assumes it's a type ID (rtid).
*
* @param mixed $type_name
* Name of a user relationship type, or its rtid.
*/
public function __construct($type_name) {
parent::__construct();
$type = user_relationships_type_load(array('name' => $type_name));
if ($type) {
$this->typeName = $type_name;
$this->typeID = $type->rtid;
}
else {
$type = user_relationships_type_load($type_name);
$this->typeName = $type->name;
$this->typeID = $type_name;
}
}
static public function getKeySchema() {
return array(
'rid' => array(
'type' => 'int',
'not null' => TRUE,
'description' => 'Relationship ID',
),
);
}
/**
* Delete a membership.
*
* @param $id
* ID to be deleted.
*/
public function rollback(array $id) {
migrate_instrument_start(__METHOD__);
if ($relationship = user_relationships_load(array('rid' => $id['destid1']))) {
$account = new stdClass;
user_relationships_delete_relationship(current($relationship), $account);
}
migrate_instrument_stop(__METHOD__);
}
/**
* Import a single membership.
*
* @param $entity
* 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.
*/
public function import(stdClass $entity, stdClass $row) {
// Normalize to unix timestamps.
foreach (array('created_at', 'updated_at') as $property) {
if (isset($entity->$property)) {
$entity->$property = Migration::timestamp($entity->$property);
}
}
$entity->rtid = $this->typeID;
$op = isset($entity->op) ? $entity->op : 'approve';
if ($saved = user_relationships_save_relationship($entity, $op)) {
return array($saved->rid);
}
}
public function fields() {
return array(
'rid' => 'Relationship ID',
'requester_id' => 'User ID of relationship requestor',
'requestee_id' => 'User ID of relationship requestee',
'approved' => 'Whether the requestee approved the relationship',
'created_at' => 'Timestamp when the relationship was created',
'updated_at' => 'Timestamp when the relationship was last updated',
'flags' => 'UR_OK (0) or UR_BANNED (1)',
'op' => 'Default value is \'approve\'. Sent as second param to user_relationships_save_relationship().'
);
}
public function __toString() {
return t('User relationship type: !type', array('!type' => $this->typeName));
}
}

View File

@@ -0,0 +1,168 @@
<?php
// $Id$
/**
* @file
* VotingAPI module integration
*/
/**
* Destination class for the votingapi_vote table.
*/
class MigrateDestinationVotingapi extends MigrateDestination {
public function __toString() {
return t('votingapi');
}
/**
* An array with content ids of imported votes. Used for recalculating results.
*/
var $importedIds = array();
static public function getKeySchema() {
return array(
'vote_id' => array(
'type' => 'int',
'not null' => TRUE,
),
);
}
/**
* Delete the provided votes and recalculate the results.
*
* @param $id
* IDs to be deleted.
*/
public function bulkRollback(array $ids) {
migrate_instrument_start(__METHOD__);
foreach ($ids as $id) {
$votes[]['vote_id'] = $id;
}
votingapi_delete_votes($votes);
// foreach($votes as $vote) {
// votingapi_recalculate_results($vote['content_type'], $vote['content_id'], TRUE);
// }
migrate_instrument_stop(__METHOD__);
}
/**
* Import a single vote.
*
* @param $entity
* 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.
*/
public function import(stdClass $entity, stdClass $row) {
$this->prepare($entity, $row);
// Votes have to be assigned to an entity.
if (empty($entity->entity_id)) {
watchdog('VotingAPI Import', 'Skipped import due to empty entity_id for vote import: @serial', array('@serial' => json_encode($row)), WATCHDOG_ERROR);
return FALSE;
}
if (isset($entity->timestamp)) {
$entity->timestamp = Migration::timestamp($entity->timestamp);
}
// Just converting $entity to an array doesn't work...
$vote = array();
$vote['entity_type'] = $entity->entity_type;
$vote['entity_id'] = $entity->entity_id;
$vote['value_type'] = $entity->value_type;
$vote['value'] = $entity->value;
$vote['uid'] = $entity->uid;
$vote['tag'] = $entity->tag;
$vote['timestamp'] = $entity->timestamp;
$vote['vote_source'] = $entity->vote_source;
votingapi_add_votes($vote);
if (isset($vote[0]['vote_id'])) {
$entity->vote_id = $vote[0]['vote_id'];
$this->complete($entity, $row);
$this->importedIds[$entity->entity_type][] = $entity->entity_id;
return array($entity->vote_id);
}
else {
return FALSE;
}
}
/**
* We're done with importing votes, recalculate the results.
*/
function postImport() {
foreach ($this->importedIds as $entity_type => $entity_ids) {
$this->importedIds = array_unique($this->importedIds);
foreach ($entity_ids as $entity_id) {
votingapi_recalculate_results($entity_type, $entity_id, TRUE);
}
}
}
/**
* Returns a list of fields available to be mapped/
*
* @return array
* Keys: machine names of the fields (to be passed to addFieldMapping)
* Values: Human-friendly descriptions of the fields.
*/
public function fields() {
return array(
'vote_id' => 'Vote ID',
'entity_type' => "Entity Type (defaults to 'node')",
'entity_id' => 'Entity ID',
'value' => 'Numeric vote value',
'value_type' => "Value type (percent/points, defaults to 'percent')",
'tag' => "Tag (defaults to 'vote')",
'uid' => 'User ID',
'timestamp' => 'Timestamp',
'vote_source' => 'Vote Source IP Address',
);
}
/**
* Give handlers a shot at modifying the object before saving it.
*
* @param $entity
* Entity object to build. Prefilled with any fields mapped in the Migration.
* @param $source_row
* Raw source data object - passed through to prepare handlers.
*/
public function prepare(stdClass $entity, stdClass $source_row) {
$migration = Migration::currentMigration();
$entity->migrate = array(
'machineName' => $migration->getMachineName(),
);
// Call any prepare handler for this specific Migration.
if (method_exists($migration, 'prepare')) {
$migration->prepare($entity, $source_row);
}
}
/**
* Give handlers a shot at modifying the object (or taking additional action)
* after saving it.
*
* @param $object
* Entity object to build. This is the complete object after saving.
* @param $source_row
* Raw source data object - passed through to complete handlers.
*/
public function complete(stdClass $entity, stdClass $source_row) {
$migration = Migration::currentMigration();
// Call any complete handler for this specific Migration.
if (method_exists($migration, 'complete')) {
$migration->complete($entity, $source_row);
}
}
}

View File

@@ -0,0 +1,282 @@
<?php
/**
* Destination class for the webform_submissions table.
*
* Working component types:
* - email
* - date ('Y-m-d')
* - file (use the file id)
* - markup
* - pagebreak (content is ignored)
* - select (looks up the key by default, pass 'source_type' => 'value' as an
* argument to try to match the value)
* - textfield
* - textarea
* - time ('H:i:s')
* Untested/needs work:
* - grid
* - hidden
*/
class MigrateDestinationWebformSubmission extends MigrateDestination {
static public function getKeySchema() {
return array(
'sid' => array(
'type' => 'int',
'not null' => TRUE,
'unsigned' => TRUE,
),
);
}
/**
* The webform of the destination.
*
* @var string
*/
protected $node;
public function getWebform() {
return $this->node;
}
/**
* An array mapping our custom names to component ids.
*
* @var array
*/
protected $component_cids;
/**
* Constructs a destination for a given webform node.
*
* @param object $node
* A node object that's type has been enabled for webform use.
*/
public function __construct($node) {
parent::__construct();
if (empty($node)) {
throw new Exception(t("You must provide a webform node"));
}
// Make sure it's a webform node.
$types = webform_variable_get('webform_node_types');
if (!in_array($node->type, $types)) {
throw new Exception(t("The node must be configured to accept webform submissions but %type was not", array('%type' => $node->type)));
}
$this->node = $node;
// Webform expects the component values to be keyed by cid, so we need a
// hash to map prefixed field names to cid.
$this->component_cids = array();
foreach ($this->node->webform['components'] as $component) {
$this->component_cids['data_' . $component['form_key']] = $component['cid'];
}
// We use the functions in this file in import() but load it here so we
// only do it once.
module_load_include('inc', 'webform', 'includes/webform.submissions');
}
public function __toString() {
return t('Submission for the <a href="!link">%title</a> Webform', array(
'!link' => url('node/' . $this->node->nid),
'%title' => $this->node->title,
));
}
/**
* Returns a list of fields available to be mapped.
*
* @return array
* Keys: machine names of the fields (to be passed to addFieldMapping)
* Values: Human-friendly descriptions of the fields.
*/
public function fields() {
// Fields defined by the schema. nid is omitted since it should come from
// $this->node.
$fields = array(
'sid' => t('The unique identifier for this submission.'),
'uid' => t('The id of the user that completed this submission.'),
'is_draft' => t('Is this a draft of the submission?'),
'submitted' => t('Timestamp of when the form was submitted.'),
'remote_addr' => t('The IP address of the user that submitted the form.'),
);
// Create a field for each component on the webform.
foreach ($this->node->webform['components'] as $component) {
// TODO: Seems like we should skip over page break components.
$fields['data_' . $component['form_key']] = t('@type: @name', array('@type' => $component['type'], '@name' => $component['name']));
}
// Then add in anything provided by handlers.
$fields += migrate_handler_invoke_all('WebformSubmission', 'fields', $this->node);
return $fields;
}
/**
* Give handlers a shot at modifying the object before saving it.
*
* @param $entity
* Webform submission object to build. Prefilled with any fields mapped in
* the Migration.
* @param $source_row
* Raw source data object - passed through to prepare handlers.
*/
public function prepare($entity, stdClass $source_row) {
$migration = Migration::currentMigration();
$entity->migrate = array(
'machineName' => $migration->getMachineName(),
);
// Call any general object handlers.
migrate_handler_invoke_all('WebformSubmission', 'prepare', $entity, $source_row, $this->node);
// Then call any prepare handler for this specific Migration.
if (method_exists($migration, 'prepare')) {
$migration->prepare($entity, $source_row, $this->node);
}
}
/**
* Give handlers a shot at modifying the object (or taking additional action)
* after saving it.
*
* @param $entity
* Webform submission object to build. This is the complete object after
* saving.
* @param $source_row
* Raw source data object - passed through to complete handlers.
*/
public function complete($entity, stdClass $source_row) {
$migration = Migration::currentMigration();
// Call any general object handlers.
migrate_handler_invoke_all('WebformSubmission', 'complete', $entity, $source_row, $this->node);
// Then call any complete handler for this specific Migration.
if (method_exists($migration, 'complete')) {
$migration->complete($entity, $source_row, $this->node);
}
}
/**
* Import a record.
*
* @param $entity
* Webform submission object to build. This is the complete object after
* saving.
* @param $source_row
* Raw source data object - passed through to complete handlers.
*/
public function import(stdClass $entity, stdClass $row) {
// Updating previously-migrated content?
$migration = Migration::currentMigration();
if (isset($row->migrate_map_destid1)) {
if (isset($entity->sid) && $entity->sid != $row->migrate_map_destid1) {
throw new MigrateException(t("Incoming sid !sid and map destination sid !destid1 don't match",
array('!sid' => $entity->sid, '!destid1' => $row->migrate_map_destid1)));
}
else {
$entity->sid = $row->migrate_map_destid1;
}
}
$entity->nid = $this->node->nid;
// Move the data from our custom keys back to webform's component ids.
$data = array();
foreach ($this->component_cids as $field_name => $cid) {
if (isset($entity->$field_name)) {
// Move the arguments out and kill any extraneous wrapper arrays.
$value = $entity->$field_name;
$arguments = array();
if (is_array($value) && isset($value['arguments'])) {
$arguments = (array) $value['arguments'];
unset($value['arguments']);
$value = count($value) ? reset($value) : $value;
}
// Avoid a warning if they passed in an empty array.
$arguments += array('source_type' => 'key');
// By default passed to select components are assumed to be the
// key. If the key should be looked up use the add a
// array('source_type' => 'value') argument to the field mapping.
$component = $this->node->webform['components'][$cid];
if ($component['type'] == 'select' && $arguments['source_type'] == 'value') {
$options = _webform_select_options($component);
$id = array_search($value, $options);
$data[$cid] = ($id === FALSE) ? NULL : $id;
}
else {
$data[$cid] = $value;
}
unset($entity->$field_name);
}
}
$entity->data = webform_submission_data($this->node, $data);
// Invoke migration prepare handlers
$this->prepare($entity, $row);
migrate_instrument_start('webform_submission_update/insert');
// Determine if it's an insert or update.
if (empty($entity->sid)) {
$updating = FALSE;
$sid = webform_submission_insert($this->node, $entity);
}
else {
// If the sid was specified but doesn't exist we'll need to stick an
// empty record in so webform's update has something to stick to.
$status = db_merge('webform_submissions')
->key(array(
'sid' => $entity->sid,
))
->insertFields(array(
'sid' => $entity->sid,
'nid' => $entity->nid,
'submitted' => $entity->submitted,
'remote_addr' => $entity->remote_addr,
'is_draft' => $entity->is_draft,
'bundle' => $entity->bundle,
))
->execute();
// If db_merge() makes no changes $status is NULL so make a less
// elegant comparison.
$updating = MergeQuery::STATUS_INSERT !== $status;
$sid = webform_submission_update($this->node, $entity);
}
migrate_instrument_stop('webform_submission_update/insert');
if (isset($sid)) {
$entity->sid = $sid;
if ($updating) {
$this->numUpdated++;
}
else {
$this->numCreated++;
}
$return = array($sid);
}
else {
$return = FALSE;
}
// Invoke migration complete handlers
$this->complete($entity, $row);
return $return;
}
/**
* Delete a batch of submissions at once.
*
* @param $sids
* Array of submission IDs to be deleted.
*/
public function bulkRollback(array $sids) {
migrate_instrument_start(__METHOD__);
foreach (webform_get_submissions(array('sid' => $sids)) as $submission) {
webform_submission_delete($this->node, $submission);
}
migrate_instrument_stop(__METHOD__);
}
}