non security modules update
This commit is contained in:
@@ -16,34 +16,41 @@
|
||||
* - Comments to be attached to the beer nodes are described in the source
|
||||
* migrate_example_beer_comment table.
|
||||
*
|
||||
* We will use the Migrate API to import and transform this data and turn it into
|
||||
* a working Drupal system.
|
||||
* We will use the Migrate API to import and transform this data and turn it
|
||||
* into a working Drupal site.
|
||||
*/
|
||||
|
||||
/**
|
||||
* To define a migration process from a set of source data to a particular
|
||||
* kind of Drupal object (for example, a specific node type), you define
|
||||
* a class derived from Migration. You must define a constructor to initialize
|
||||
* your migration object. By default, your class name will be the "machine name"
|
||||
* of the migration, by which you refer to it. Note that the machine name is
|
||||
* case-sensitive.
|
||||
* your migration object.
|
||||
*
|
||||
* For your classes to be instantiated so they can be used to import content,
|
||||
* you must register them - look at migrate_example.migrate.inc to see how
|
||||
* registration works. Right now, it's important to understand that each
|
||||
* migration will have a unique "machine name", which is displayed in the UI
|
||||
* and is used to reference the migration in drush commands.
|
||||
*
|
||||
* In any serious migration project, you will find there are some options
|
||||
* which are common to the individual migrations you're implementing. You can
|
||||
* define an abstract intermediate class derived from Migration, then derive your
|
||||
* individual migrations from that, to share settings, utility functions, etc.
|
||||
*/
|
||||
abstract class BasicExampleMigration extends DynamicMigration {
|
||||
public function __construct() {
|
||||
// Always call the parent constructor first for basic setup
|
||||
parent::__construct();
|
||||
abstract class BasicExampleMigration extends Migration {
|
||||
// A Migration constructor takes an array of arguments as its first parameter.
|
||||
// The arguments must be passed through to the parent constructor.
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
|
||||
// With migrate_ui enabled, migration pages will indicate people involved in
|
||||
// the particular migration, with their role and contact info. We default the
|
||||
// list in the shared class; it can be overridden for specific migrations.
|
||||
$this->team = array(
|
||||
new MigrateTeamMember('Liz Taster', 'ltaster@example.com', t('Product Owner')),
|
||||
new MigrateTeamMember('Larry Brewer', 'lbrewer@example.com', t('Implementor')),
|
||||
new MigrateTeamMember('Liz Taster', 'ltaster@example.com',
|
||||
t('Product Owner')),
|
||||
new MigrateTeamMember('Larry Brewer', 'lbrewer@example.com',
|
||||
t('Implementor')),
|
||||
);
|
||||
|
||||
// Individual mappings in a migration can be linked to a ticket or issue
|
||||
@@ -62,23 +69,45 @@ abstract class BasicExampleMigration extends DynamicMigration {
|
||||
* this will receive data that originated from the source and has been mapped
|
||||
* by the Migration class, and create Drupal objects.
|
||||
* $this->map - An instance of a class derived from MigrateMap, this will keep
|
||||
* track of which source items have been imported and what destination objects
|
||||
* they map to.
|
||||
* Mappings - Use $this->addFieldMapping to tell the Migration class what source
|
||||
* fields correspond to what destination fields, and additional information
|
||||
* associated with the mappings.
|
||||
* track of which source items have been imported and what destination
|
||||
* objects they map to.
|
||||
* Field mappings - Use $this->addFieldMapping to tell the Migration class what
|
||||
* source fields correspond to what destination fields, and additional
|
||||
* information associated with the mappings.
|
||||
*/
|
||||
class BeerTermMigration extends BasicExampleMigration {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
// Human-friendly description of your migration process. Be as detailed as you
|
||||
// like.
|
||||
$this->description = t('Migrate styles from the source database to taxonomy terms');
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
// Human-friendly description of your migration process. Be as detailed as
|
||||
// you like.
|
||||
$this->description =
|
||||
t('Migrate styles from the source database to taxonomy terms');
|
||||
|
||||
// In this example, we're using tables that have been added to the existing
|
||||
// Drupal database but which are not Drupal tables. You can examine the
|
||||
// various tables (starting here with migrate_example_beer_topic) using a
|
||||
// database browser such as phpMyAdmin.
|
||||
// First, we set up a query for this data. Note that by ordering on
|
||||
// style_parent, we guarantee root terms are migrated first, so the
|
||||
// parent_name mapping below will find that the parent exists.
|
||||
$query = db_select('migrate_example_beer_topic', 'met')
|
||||
->fields('met', array('style', 'details', 'style_parent', 'region',
|
||||
'hoppiness'))
|
||||
// This sort assures that parents are saved before children.
|
||||
->orderBy('style_parent', 'ASC');
|
||||
|
||||
// Create a MigrateSource object, which manages retrieving the input data.
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
|
||||
// Set up our destination - terms in the migrate_example_beer_styles
|
||||
// vocabulary (note that we pass the machine name of the vocabulary).
|
||||
$this->destination =
|
||||
new MigrateDestinationTerm('migrate_example_beer_styles');
|
||||
|
||||
// Create a map object for tracking the relationships between source rows
|
||||
// and their resulting Drupal objects. Usually, you'll use the MigrateSQLMap
|
||||
// class, which uses database tables for tracking. Pass the machine name
|
||||
// (BeerTerm) of this migration to use in generating map and message tables.
|
||||
// and their resulting Drupal objects. We will use the MigrateSQLMap class,
|
||||
// which uses database tables for tracking. Pass the machine name (BeerTerm)
|
||||
// of this migration to use in generating map and message table names.
|
||||
// And, pass schema definitions for the primary keys of the source and
|
||||
// destination - we need to be explicit for our source, but the destination
|
||||
// class knows its schema already.
|
||||
@@ -93,28 +122,11 @@ class BeerTermMigration extends BasicExampleMigration {
|
||||
MigrateDestinationTerm::getKeySchema()
|
||||
);
|
||||
|
||||
// In this example, we're using tables that have been added to the existing
|
||||
// Drupal database but which are not Drupal tables. You can examine the
|
||||
// various tables (starting here with migrate_example_beer_topic) using a
|
||||
// database browser like phpMyAdmin.
|
||||
// First, we set up a query for this data. Note that by ordering on
|
||||
// style_parent, we guarantee root terms are migrated first, so the
|
||||
// parent_name mapping below will find that the parent exists.
|
||||
$query = db_select('migrate_example_beer_topic', 'met')
|
||||
->fields('met', array('style', 'details', 'style_parent', 'region', 'hoppiness'))
|
||||
// This sort assures that parents are saved before children.
|
||||
->orderBy('style_parent', 'ASC');
|
||||
|
||||
// Create a MigrateSource object, which manages retrieving the input data.
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
|
||||
// Set up our destination - terms in the migrate_example_beer_styles vocabulary
|
||||
$this->destination = new MigrateDestinationTerm('migrate_example_beer_styles');
|
||||
|
||||
// Assign mappings TO destination fields FROM source fields. To discover
|
||||
// the names used in these calls, use the drush commands
|
||||
// drush migrate-fields-destination BeerTerm
|
||||
// drush migrate-fields-source BeerTerm
|
||||
// drush migrate-fields-destination BeerTerm
|
||||
// drush migrate-fields-source BeerTerm
|
||||
// or review the detail pages in the UI.
|
||||
$this->addFieldMapping('name', 'style');
|
||||
$this->addFieldMapping('description', 'details');
|
||||
|
||||
@@ -127,12 +139,13 @@ class BeerTermMigration extends BasicExampleMigration {
|
||||
// migration info page when the migrate_ui module is enabled. The default
|
||||
// is 'Done', indicating active mappings which need no attention. A
|
||||
// suggested practice is to use groups of:
|
||||
// Do Not Migrate (or DNM) to indicate source fields which are not being used,
|
||||
// or destination fields not to be populated by migration.
|
||||
// Do Not Migrate (or DNM) to indicate source fields which are not being
|
||||
// used, or destination fields not to be populated by migration.
|
||||
// Client Issues to indicate input from the client is needed to determine
|
||||
// how a given field is to be migrated.
|
||||
// Implementor Issues to indicate that the client has provided all the
|
||||
// necessary information, and now the implementor needs to complete the work.
|
||||
// necessary information, and now the implementor needs to complete the
|
||||
// work.
|
||||
$this->addFieldMapping(NULL, 'hoppiness')
|
||||
->description(t('This info will not be maintained in Drupal'))
|
||||
->issueGroup(t('DNM'));
|
||||
@@ -141,10 +154,11 @@ class BeerTermMigration extends BasicExampleMigration {
|
||||
// MigrateFieldMapping::ISSUE_PRIORITY_OK). If you're using an issue
|
||||
// tracking system, and have defined issuePattern (see BasicExampleMigration
|
||||
// above), you can specify a ticket/issue number in the system on the
|
||||
// mapping and migrate_ui will link directory to it.
|
||||
// mapping and migrate_ui will link directly to it.
|
||||
$this->addFieldMapping(NULL, 'region')
|
||||
->description('Will a field be added to the vocabulary for this?')
|
||||
->issueGroup(t('Client Issues'))
|
||||
// This priority wil cause the mapping to be highlighted in the UI.
|
||||
->issuePriority(MigrateFieldMapping::ISSUE_PRIORITY_MEDIUM)
|
||||
->issueNumber(770064);
|
||||
|
||||
@@ -152,8 +166,8 @@ class BeerTermMigration extends BasicExampleMigration {
|
||||
// explicitly - this makes sure that everyone understands exactly what is
|
||||
// being migrated and what is not. Also, migrate_ui highlights unmapped
|
||||
// fields, or mappings involving fields not in the source and destination,
|
||||
// so if (for example) a new field is added to the destination field it's
|
||||
// immediately visible, and you can find out if anything needs to be
|
||||
// so if (for example) a new field is added to the destination typ it's
|
||||
// immediately visible, and you can decide if anything needs to be
|
||||
// migrated into it.
|
||||
$this->addFieldMapping('format')
|
||||
->issueGroup(t('DNM'));
|
||||
@@ -163,11 +177,12 @@ class BeerTermMigration extends BasicExampleMigration {
|
||||
->issueGroup(t('DNM'));
|
||||
|
||||
// We conditionally DNM these fields, so your field mappings will be clean
|
||||
// whether or not you have path and or pathauto enabled
|
||||
if (module_exists('path')) {
|
||||
// whether or not you have path and/or pathauto enabled.
|
||||
$destination_fields = $this->destination->fields();
|
||||
if (isset($destination_fields['path'])) {
|
||||
$this->addFieldMapping('path')
|
||||
->issueGroup(t('DNM'));
|
||||
if (module_exists('pathauto')) {
|
||||
if (isset($destination_fields['pathauto'])) {
|
||||
$this->addFieldMapping('pathauto')
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
@@ -186,10 +201,15 @@ class BeerTermMigration extends BasicExampleMigration {
|
||||
* transformations of the data.
|
||||
*/
|
||||
class BeerUserMigration extends BasicExampleMigration {
|
||||
public function __construct() {
|
||||
public function __construct($arguments) {
|
||||
// The basic setup is similar to BeerTermMigraiton
|
||||
parent::__construct();
|
||||
parent::__construct($arguments);
|
||||
$this->description = t('Beer Drinkers of the world');
|
||||
$query = db_select('migrate_example_beer_account', 'mea')
|
||||
->fields('mea', array('aid', 'status', 'posted', 'name',
|
||||
'nickname', 'password', 'mail', 'sex', 'beers'));
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
$this->destination = new MigrateDestinationUser();
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array('aid' => array(
|
||||
'type' => 'int',
|
||||
@@ -199,18 +219,15 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
),
|
||||
MigrateDestinationUser::getKeySchema()
|
||||
);
|
||||
$query = db_select('migrate_example_beer_account', 'mea')
|
||||
->fields('mea', array('aid', 'status', 'posted', 'name', 'nickname', 'password', 'mail', 'sex', 'beers'));
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
$this->destination = new MigrateDestinationUser();
|
||||
|
||||
// One good way to organize your mappings is in three groups - mapped fields,
|
||||
// unmapped source fields, and unmapped destination fields
|
||||
// One good way to organize your mappings in the constructor is in three
|
||||
// groups - mapped fields, unmapped source fields, and unmapped destination
|
||||
// fields.
|
||||
|
||||
// Mapped fields
|
||||
|
||||
// This is a shortcut you can use when the source and destination field
|
||||
// names are identical (i.e., the email address field is named 'mail' in
|
||||
// names are identical (e.g., the email address field is named 'mail' in
|
||||
// both the source table and in Drupal).
|
||||
$this->addSimpleMappings(array('status', 'mail'));
|
||||
|
||||
@@ -224,7 +241,8 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
$this->addFieldMapping('name', 'name')
|
||||
->dedupe('users', 'name');
|
||||
|
||||
// The migrate module automatically converts date/time strings to UNIX timestamps.
|
||||
// The migrate module automatically converts date/time strings to UNIX
|
||||
// timestamps.
|
||||
$this->addFieldMapping('created', 'posted');
|
||||
|
||||
$this->addFieldMapping('pass', 'password');
|
||||
@@ -246,6 +264,10 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
$this->addFieldMapping('field_migrate_example_favbeers', 'beers')
|
||||
->separator('|');
|
||||
}
|
||||
else {
|
||||
$this->addFieldMapping(NULL, 'beers')
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
|
||||
// Unmapped source fields
|
||||
$this->addFieldMapping(NULL, 'nickname')
|
||||
@@ -254,10 +276,20 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
// Unmapped destination fields
|
||||
|
||||
// This is a shortcut you can use to mark several destination fields as DNM
|
||||
// at once
|
||||
$this->addUnmigratedDestinations(array('theme', 'signature', 'access', 'login',
|
||||
'timezone', 'language', 'picture', 'is_new', 'signature_format', 'role_names',
|
||||
'init', 'data'));
|
||||
// at once.
|
||||
$this->addUnmigratedDestinations(array(
|
||||
'access',
|
||||
'data',
|
||||
'is_new',
|
||||
'language',
|
||||
'login',
|
||||
'picture',
|
||||
'role_names',
|
||||
'signature',
|
||||
'signature_format',
|
||||
'theme',
|
||||
'timezone',
|
||||
));
|
||||
|
||||
// Oops, we made a typo - this should have been 'init'! If you have
|
||||
// migrate_ui enabled, look at the BeerUser info page - you'll see that it
|
||||
@@ -267,10 +299,11 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
$this->addFieldMapping('int')
|
||||
->issueGroup(t('DNM'));
|
||||
|
||||
if (module_exists('path')) {
|
||||
$destination_fields = $this->destination->fields();
|
||||
if (isset($destination_fields['path'])) {
|
||||
$this->addFieldMapping('path')
|
||||
->issueGroup(t('DNM'));
|
||||
if (module_exists('pathauto')) {
|
||||
if (isset($destination_fields['pathauto'])) {
|
||||
$this->addFieldMapping('pathauto')
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
@@ -283,35 +316,19 @@ class BeerUserMigration extends BasicExampleMigration {
|
||||
* and creates Drupal nodes of type 'Beer' as destination.
|
||||
*/
|
||||
class BeerNodeMigration extends BasicExampleMigration {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
$this->description = t('Beers of the world');
|
||||
|
||||
// You may optionally declare dependencies for your migration - other migrations
|
||||
// which should run first. In this case, terms assigned to our nodes and
|
||||
// the authors of the nodes should be migrated before the nodes themselves.
|
||||
$this->dependencies = array('BeerTerm', 'BeerUser');
|
||||
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array(
|
||||
'bid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Beer ID.',
|
||||
'alias' => 'b',
|
||||
)
|
||||
),
|
||||
MigrateDestinationNode::getKeySchema()
|
||||
);
|
||||
|
||||
// We have a more complicated query. The Migration class fundamentally
|
||||
// depends on taking a single source row and turning it into a single
|
||||
// Drupal object, so how do we deal with zero or more terms attached to
|
||||
// each node? One way (demonstrated for MySQL) is to pull them into a single
|
||||
// each node? One way (valid for MySQL only) is to pull them into a single
|
||||
// comma-separated list.
|
||||
$query = db_select('migrate_example_beer_node', 'b')
|
||||
->fields('b', array('bid', 'name', 'body', 'excerpt', 'aid', 'countries',
|
||||
'image', 'image_alt', 'image_title', 'image_description'));
|
||||
->fields('b', array('bid', 'name', 'body', 'excerpt', 'aid',
|
||||
'countries', 'image', 'image_alt', 'image_title',
|
||||
'image_description'));
|
||||
$query->leftJoin('migrate_example_beer_topic_node', 'tb', 'b.bid = tb.bid');
|
||||
// Gives a single comma-separated list of related terms
|
||||
$query->groupBy('tb.bid');
|
||||
@@ -331,6 +348,18 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
// Set up our destination - nodes of type migrate_example_beer
|
||||
$this->destination = new MigrateDestinationNode('migrate_example_beer');
|
||||
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array(
|
||||
'bid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Beer ID.',
|
||||
'alias' => 'b',
|
||||
)
|
||||
),
|
||||
MigrateDestinationNode::getKeySchema()
|
||||
);
|
||||
|
||||
// Mapped fields
|
||||
$this->addFieldMapping('title', 'name')
|
||||
->description(t('Mapping beer name in source to node title'));
|
||||
@@ -340,15 +369,6 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
->issueNumber(765736)
|
||||
->issuePriority(MigrateFieldMapping::ISSUE_PRIORITY_LOW);
|
||||
|
||||
// To maintain node identities between the old and new systems (i.e., have
|
||||
// the same unique IDs), map the ID column from the old system to nid and
|
||||
// set is_new to TRUE. This works only if we're importing into a system that
|
||||
// has no existing nodes with the nids being imported.
|
||||
$this->addFieldMapping('nid', 'bid')
|
||||
->description(t('Preserve old beer ID as nid in Drupal'));
|
||||
$this->addFieldMapping('is_new')
|
||||
->defaultValue(TRUE);
|
||||
|
||||
// References to related objects (such as the author of the content) are
|
||||
// most likely going to be identifiers from the source data, not Drupal
|
||||
// identifiers (such as uids). You can use the mapping from the relevant
|
||||
@@ -357,25 +377,37 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
// find a corresponding uid for the aid, the owner will be the administrative
|
||||
// account.
|
||||
$this->addFieldMapping('uid', 'aid')
|
||||
// Note this is the machine name of the user migration.
|
||||
->sourceMigration('BeerUser')
|
||||
->defaultValue(1);
|
||||
|
||||
// This is a multi-value text field
|
||||
// This is a multi-value text field - in the source data the values are
|
||||
// separated by |, so we tell migrate to split it by that character.
|
||||
$this->addFieldMapping('field_migrate_example_country', 'countries')
|
||||
->separator('|');
|
||||
// These are related terms, which by default will be looked up by name
|
||||
// These are related terms, which by default will be looked up by name.
|
||||
$this->addFieldMapping('migrate_example_beer_styles', 'terms')
|
||||
->separator(',');
|
||||
|
||||
// Some fields may have subfields such as text formats or summaries
|
||||
// (equivalent to teasers in previous Drupal versions).
|
||||
// These can be individually mapped as we see here.
|
||||
// Some fields may have subfields such as text formats or summaries. These
|
||||
// can be individually mapped as we see here.
|
||||
$this->addFieldMapping('body', 'body');
|
||||
$this->addFieldMapping('body:summary', 'excerpt');
|
||||
|
||||
// Copy an image file, write DB record to files table, and save in Field storage.
|
||||
// We map the filename (relative to the source_dir below) to the field itself.
|
||||
// File fields are more complex - the file needs to be copied, a Drupal
|
||||
// file entity (file_managed table row) created, and the field populated.
|
||||
// There are several different options involved. It's usually best to do
|
||||
// migrate the files themselves in their own migration (see wine.inc for an
|
||||
// example), but they can also be brought over through the field mapping.
|
||||
|
||||
// We map the filename (relative to the source_dir below) to the field
|
||||
// itself.
|
||||
$this->addFieldMapping('field_migrate_example_image', 'image');
|
||||
// The file_class determines how the 'image' value is interpreted, and what
|
||||
// other options are available. In this case, MigrateFileUri indicates that
|
||||
// the 'image' value is a URI.
|
||||
$this->addFieldMapping('field_migrate_example_image:file_class')
|
||||
->defaultValue('MigrateFileUri');
|
||||
// Here we specify the directory containing the source files.
|
||||
$this->addFieldMapping('field_migrate_example_image:source_dir')
|
||||
->defaultValue(drupal_get_path('module', 'migrate_example'));
|
||||
@@ -387,24 +419,46 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
$this->addUnmigratedSources(array('image_description'));
|
||||
|
||||
// Unmapped destination fields
|
||||
$this->addUnmigratedDestinations(array('created', 'changed', 'status',
|
||||
'promote', 'revision', 'language', 'revision_uid', 'log', 'tnid',
|
||||
'body:format', 'body:language', 'migrate_example_beer_styles:source_type',
|
||||
'migrate_example_beer_styles:create_term', 'field_migrate_example_image:destination_dir',
|
||||
'field_migrate_example_image:language', 'field_migrate_example_image:file_replace',
|
||||
'field_migrate_example_image:preserve_files', 'field_migrate_example_country:language', 'comment',
|
||||
'field_migrate_example_image:file_class', 'field_migrate_example_image:destination_file'));
|
||||
// Some conventions we use here: with a long list of fields to ignore, we
|
||||
// arrange them alphabetically, one distinct field per line (although
|
||||
// subfields of the same field may be grouped on the same line), and indent
|
||||
// subfields to distinguish them from top-level fields.
|
||||
$this->addUnmigratedDestinations(array(
|
||||
'body:format', 'body:language',
|
||||
'changed',
|
||||
'comment',
|
||||
'created',
|
||||
'field_migrate_example_country:language',
|
||||
'field_migrate_example_image:destination_dir',
|
||||
'field_migrate_example_image:destination_file',
|
||||
'field_migrate_example_image:file_replace',
|
||||
'field_migrate_example_image:language',
|
||||
'field_migrate_example_image:preserve_files',
|
||||
'field_migrate_example_image:urlencode',
|
||||
'is_new',
|
||||
'language',
|
||||
'log',
|
||||
'migrate_example_beer_styles:source_type',
|
||||
'migrate_example_beer_styles:create_term',
|
||||
'promote',
|
||||
'revision',
|
||||
'revision_uid',
|
||||
'status',
|
||||
'tnid',
|
||||
));
|
||||
|
||||
if (module_exists('path')) {
|
||||
$destination_fields = $this->destination->fields();
|
||||
if (isset($destination_fields['path'])) {
|
||||
$this->addFieldMapping('path')
|
||||
->issueGroup(t('DNM'));
|
||||
if (module_exists('pathauto')) {
|
||||
if (isset($destination_fields['pathauto'])) {
|
||||
$this->addFieldMapping('pathauto')
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
}
|
||||
if (module_exists('statistics')) {
|
||||
$this->addUnmigratedDestinations(array('totalcount', 'daycount', 'timestamp'));
|
||||
$this->addUnmigratedDestinations(
|
||||
array('totalcount', 'daycount', 'timestamp'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -414,10 +468,21 @@ class BeerNodeMigration extends BasicExampleMigration {
|
||||
* Drupal comment objects.
|
||||
*/
|
||||
class BeerCommentMigration extends BasicExampleMigration {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
public function __construct($arguments) {
|
||||
parent::__construct($arguments);
|
||||
$this->description = 'Comments about beers';
|
||||
$this->dependencies = array('BeerUser', 'BeerNode');
|
||||
|
||||
$query = db_select('migrate_example_beer_comment', 'mec')
|
||||
->fields('mec', array('cid', 'cid_parent', 'name', 'mail', 'aid',
|
||||
'body', 'bid', 'subject'))
|
||||
->orderBy('cid_parent', 'ASC');
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
// Note that the machine name passed for comment migrations is
|
||||
// 'comment_node_' followed by the machine name of the node type these
|
||||
// comments are attached to.
|
||||
$this->destination =
|
||||
new MigrateDestinationComment('comment_node_migrate_example_beer');
|
||||
|
||||
$this->map = new MigrateSQLMap($this->machineName,
|
||||
array('cid' => array(
|
||||
'type' => 'int',
|
||||
@@ -426,19 +491,14 @@ class BeerCommentMigration extends BasicExampleMigration {
|
||||
),
|
||||
MigrateDestinationComment::getKeySchema()
|
||||
);
|
||||
$query = db_select('migrate_example_beer_comment', 'mec')
|
||||
->fields('mec', array('cid', 'cid_parent', 'name', 'mail', 'aid', 'body', 'bid', 'subject'))
|
||||
->orderBy('cid_parent', 'ASC');
|
||||
$this->source = new MigrateSourceSQL($query);
|
||||
$this->destination = new MigrateDestinationComment('comment_node_migrate_example_beer');
|
||||
|
||||
// Mapped fields
|
||||
$this->addSimpleMappings(array('name', 'subject', 'mail'));
|
||||
$this->addFieldMapping('status')
|
||||
->defaultValue(COMMENT_PUBLISHED);
|
||||
|
||||
// We preserved bid => nid ids during BeerNode import so simple mapping suffices.
|
||||
$this->addFieldMapping('nid', 'bid');
|
||||
$this->addFieldMapping('nid', 'bid')
|
||||
->sourceMigration('BeerNode');
|
||||
|
||||
$this->addFieldMapping('uid', 'aid')
|
||||
->sourceMigration('BeerUser')
|
||||
@@ -453,7 +513,24 @@ class BeerCommentMigration extends BasicExampleMigration {
|
||||
// No unmapped source fields
|
||||
|
||||
// Unmapped destination fields
|
||||
$this->addUnmigratedDestinations(array('hostname', 'created', 'changed',
|
||||
'thread', 'homepage', 'language', 'comment_body:format', 'comment_body:language'));
|
||||
$this->addUnmigratedDestinations(array(
|
||||
'changed',
|
||||
'comment_body:format', 'comment_body:language',
|
||||
'created',
|
||||
'homepage',
|
||||
'hostname',
|
||||
'language',
|
||||
'thread',
|
||||
));
|
||||
|
||||
$destination_fields = $this->destination->fields();
|
||||
if (isset($destination_fields['path'])) {
|
||||
$this->addFieldMapping('path')
|
||||
->issueGroup(t('DNM'));
|
||||
if (isset($destination_fields['pathauto'])) {
|
||||
$this->addFieldMapping('pathauto')
|
||||
->issueGroup(t('DNM'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,7 @@ function migrate_example_beer_uninstall() {
|
||||
}
|
||||
|
||||
function migrate_example_beer_disable() {
|
||||
Migration::deregisterMigration('BeerTerm');
|
||||
Migration::deregisterMigration('BeerUser');
|
||||
Migration::deregisterMigration('BeerNode');
|
||||
Migration::deregisterMigration('BeerComment');
|
||||
MigrateGroup::deregister('beer');
|
||||
}
|
||||
|
||||
function migrate_example_beer_schema_node() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name = "Migrate Example"
|
||||
description = "Example migration data."
|
||||
package = "Development"
|
||||
package = "Migration"
|
||||
core = 7.x
|
||||
dependencies[] = taxonomy
|
||||
dependencies[] = image
|
||||
@@ -12,16 +12,15 @@ dependencies[] = number
|
||||
;node_reference is useful but not required
|
||||
;dependencies[] = node_reference
|
||||
|
||||
files[] = migrate_example.module
|
||||
files[] = beer.inc
|
||||
files[] = wine.inc
|
||||
|
||||
; For testing table_copy plugin. Since is infrequently used, we comment it out.
|
||||
; files[] = example.table_copy.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-07
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2015-02-09
|
||||
version = "7.x-2.7"
|
||||
core = "7.x"
|
||||
project = "migrate"
|
||||
datestamp = "1352299007"
|
||||
datestamp = "1423521491"
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ function migrate_example_install() {
|
||||
);
|
||||
$example_format = (object) $example_format;
|
||||
filter_format_save($example_format);
|
||||
migrate_static_registration();
|
||||
}
|
||||
|
||||
function migrate_example_uninstall() {
|
||||
|
||||
@@ -9,39 +9,238 @@
|
||||
|
||||
/*
|
||||
* You must implement hook_migrate_api(), setting the API level to 2, if you are
|
||||
* implementing any migration classes. As of Migrate 2.5, you should also
|
||||
* register your migration and handler classes explicitly here - the former
|
||||
* method of letting them get automatically registered on a cache clear will
|
||||
* break in certain environments (see http://drupal.org/node/1778952).
|
||||
* implementing any migration classes. If your migration application is static -
|
||||
* that is, you know at implementation time exactly what migrations must be
|
||||
* instantiated - then you should register your migrations here. If your
|
||||
* application is more dynamic (for example, if selections in the UI determine
|
||||
* exactly what migrations need to be instantiated), then you would register
|
||||
* your migrations using registerMigration() - see migrate_example_baseball for
|
||||
* more information.
|
||||
*/
|
||||
function migrate_example_migrate_api() {
|
||||
// Usually field mappings are established by code in the migration constructor -
|
||||
// a call to addFieldMapping(). They may also be passed as arguments when
|
||||
// registering a migration - in this case, they are stored in the database
|
||||
// and override any mappings for the same field in the code. To do this,
|
||||
// construct the field mapping object and configure it similarly to when
|
||||
// you call addFieldMapping, and pass your mappings as an array below.
|
||||
$translate_mapping = new MigrateFieldMapping('translate', NULL);
|
||||
$translate_mapping->defaultValue(0);
|
||||
$ignore_mapping = new MigrateFieldMapping('migrate_example_beer_styles:ignore_case', NULL);
|
||||
$ignore_mapping->defaultValue(1);
|
||||
|
||||
$api = array(
|
||||
// Required - tells the Migrate module that you are implementing version 2
|
||||
// of the Migrate API.
|
||||
'api' => 2,
|
||||
// Migrations can be organized into groups. The key used here will be the
|
||||
// machine name of the group, which can be used in Drush:
|
||||
// drush migrate-import --group=wine
|
||||
// The title is a required argument which is displayed for the group in the
|
||||
// UI. You may also have additional arguments for any other data which is
|
||||
// common to all migrations in the group.
|
||||
'groups' => array(
|
||||
'beer' => array(
|
||||
'title' => t('Beer Imports'),
|
||||
),
|
||||
'wine' => array(
|
||||
'title' => t('Wine Imports'),
|
||||
),
|
||||
),
|
||||
|
||||
// Here we register the individual migrations. The keys (BeerTerm, BeerUser,
|
||||
// etc.) are the machine names of the migrations, and the class_name
|
||||
// argument is required. The group_name is optional (defaulting to 'default')
|
||||
// but specifying it is a best practice.
|
||||
'migrations' => array(
|
||||
'BeerTerm' => array('class_name' => 'BeerTermMigration'),
|
||||
'BeerUser' => array('class_name' => 'BeerUserMigration'),
|
||||
'BeerNode' => array('class_name' => 'BeerNodeMigration'),
|
||||
'BeerComment' => array('class_name' => 'BeerCommentMigration'),
|
||||
'WinePrep' => array('class_name' => 'WinePrepMigration'),
|
||||
'WineVariety' => array('class_name' => 'WineVarietyMigration'),
|
||||
'WineRegion' => array('class_name' => 'WineRegionMigration'),
|
||||
'WineBestWith' => array('class_name' => 'WineBestWithMigration'),
|
||||
'WineFileCopy' => array('class_name' => 'WineFileCopyMigration'),
|
||||
'WineFileBlob' => array('class_name' => 'WineFileBlobMigration'),
|
||||
'WineRole' => array('class_name' => 'WineRoleMigration'),
|
||||
'WineUser' => array('class_name' => 'WineUserMigration'),
|
||||
'WineProducer' => array('class_name' => 'WineProducerMigration'),
|
||||
'WineProducerXML' => array('class_name' => 'WineProducerXMLMigration'),
|
||||
'WineProducerMultiXML' => array('class_name' => 'WineProducerMultiXMLMigration'),
|
||||
'WineProducerXMLPull' => array('class_name' => 'WineProducerXMLPullMigration'),
|
||||
'WineWine' => array('class_name' => 'WineWineMigration'),
|
||||
'WineComment' => array('class_name' => 'WineCommentMigration'),
|
||||
'WineTable' => array('class_name' => 'WineTableMigration'),
|
||||
'WineFinish' => array('class_name' => 'WineFinishMigration'),
|
||||
'WineUpdates' => array('class_name' => 'WineUpdatesMigration'),
|
||||
'WineCommentUpdates' => array('class_name' => 'WineCommentUpdatesMigration'),
|
||||
'WineVarietyUpdates' => array('class_name' => 'WineVarietyUpdatesMigration'),
|
||||
'WineUserUpdates' => array('class_name' => 'WineUserUpdatesMigration'),
|
||||
'BeerTerm' => array(
|
||||
'class_name' => 'BeerTermMigration',
|
||||
'group_name' => 'beer',
|
||||
),
|
||||
'BeerUser' => array(
|
||||
'class_name' => 'BeerUserMigration',
|
||||
'group_name' => 'beer',
|
||||
),
|
||||
'BeerNode' => array(
|
||||
'class_name' => 'BeerNodeMigration',
|
||||
'group_name' => 'beer',
|
||||
// You may optionally declare dependencies for your migration - other
|
||||
// migrations which should run first. In this case, terms assigned to our
|
||||
// nodes and the authors of the nodes should be migrated before the nodes
|
||||
// themselves.
|
||||
'dependencies' => array(
|
||||
'BeerTerm',
|
||||
'BeerUser',
|
||||
),
|
||||
// Here is where we add field mappings which may override those
|
||||
// specified in the group constructor.
|
||||
'field_mappings' => array(
|
||||
$translate_mapping,
|
||||
$ignore_mapping,
|
||||
),
|
||||
),
|
||||
'BeerComment' => array(
|
||||
'class_name' => 'BeerCommentMigration',
|
||||
'group_name' => 'beer',
|
||||
'dependencies' => array(
|
||||
'BeerUser',
|
||||
'BeerNode',
|
||||
),
|
||||
),
|
||||
'WinePrep' => array(
|
||||
'class_name' => 'WinePrepMigration',
|
||||
'group_name' => 'wine',
|
||||
),
|
||||
'WineVariety' => array(
|
||||
'class_name' => 'WineVarietyMigration',
|
||||
'group_name' => 'wine',
|
||||
),
|
||||
'WineRegion' => array(
|
||||
'class_name' => 'WineRegionMigration',
|
||||
'group_name' => 'wine',
|
||||
),
|
||||
'WineBestWith' => array(
|
||||
'class_name' => 'WineBestWithMigration',
|
||||
'group_name' => 'wine',
|
||||
),
|
||||
'WineFileCopy' => array(
|
||||
'class_name' => 'WineFileCopyMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WinePrep'),
|
||||
),
|
||||
'WineFileBlob' => array(
|
||||
'class_name' => 'WineFileBlobMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WinePrep'),
|
||||
),
|
||||
'WineRole' => array(
|
||||
'class_name' => 'WineRoleMigration',
|
||||
'group_name' => 'wine',
|
||||
// TIP: Regular dependencies, besides enforcing (in the absence of
|
||||
// --force) the run order of migrations, affect the sorting of
|
||||
// migrations on display. You can use soft dependencies to affect just
|
||||
// the display order when the migrations aren't technically required to
|
||||
// run in a certain order. In this case, we want the role migration to
|
||||
// appear after the file migrations.
|
||||
'soft_dependencies' => array('WineFileCopy'),
|
||||
),
|
||||
'WineUser' => array(
|
||||
'class_name' => 'WineUserMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineFileCopy',
|
||||
'WineRole',
|
||||
),
|
||||
),
|
||||
'WineProducer' => array(
|
||||
'class_name' => 'WineProducerMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerXML' => array(
|
||||
'class_name' => 'WineProducerXMLMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerNamespaceXML' => array(
|
||||
'class_name' => 'WineProducerNamespaceXMLMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerMultiXML' => array(
|
||||
'class_name' => 'WineProducerMultiXMLMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerMultiNamespaceXML' => array(
|
||||
'class_name' => 'WineProducerMultiNamespaceXMLMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerXMLPull' => array(
|
||||
'class_name' => 'WineProducerXMLPullMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineProducerNamespaceXMLPull' => array(
|
||||
'class_name' => 'WineProducerNamespaceXMLPullMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineUser',
|
||||
),
|
||||
),
|
||||
'WineWine' => array(
|
||||
'class_name' => 'WineWineMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineRegion',
|
||||
'WineVariety',
|
||||
'WineBestWith',
|
||||
'WineUser',
|
||||
'WineProducer',
|
||||
),
|
||||
),
|
||||
'WineComment' => array(
|
||||
'class_name' => 'WineCommentMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array(
|
||||
'WineUser',
|
||||
'WineWine',
|
||||
),
|
||||
),
|
||||
'WineTable' => array(
|
||||
'class_name' => 'WineTableMigration',
|
||||
'group_name' => 'wine',
|
||||
'soft_dependencies' => array('WineComment'),
|
||||
),
|
||||
'WineFinish' => array(
|
||||
'class_name' => 'WineFinishMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineComment'),
|
||||
),
|
||||
'WineUpdates' => array(
|
||||
'class_name' => 'WineUpdatesMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineWine'),
|
||||
'soft_dependencies' => array('WineFinish'),
|
||||
),
|
||||
'WineCommentUpdates' => array(
|
||||
'class_name' => 'WineCommentUpdatesMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineComment'),
|
||||
'soft_dependencies' => array('WineUpdates'),
|
||||
),
|
||||
'WineVarietyUpdates' => array(
|
||||
'class_name' => 'WineVarietyUpdatesMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineVariety'),
|
||||
'soft_dependencies' => array('WineUpdates'),
|
||||
),
|
||||
'WineUserUpdates' => array(
|
||||
'class_name' => 'WineUserUpdatesMigration',
|
||||
'group_name' => 'wine',
|
||||
'dependencies' => array('WineUser'),
|
||||
'soft_dependencies' => array('WineUpdates'),
|
||||
),
|
||||
),
|
||||
);
|
||||
return $api;
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* THIS SPACE INTENTIONALLY LEFT BLANK.
|
||||
* THIS FILE INTENTIONALLY LEFT BLANK.
|
||||
*
|
||||
* Yes, there is no code in the .module file. Migrate operates almost entirely
|
||||
* through classes, and by adding any files containing class definitions to the
|
||||
* .info file, those files are automatically included only when the classes they
|
||||
* contain are referenced. The one non-class piece you need to implement is
|
||||
* hook_migrate_api(), but because .migrate.inc is registered using hook_hook_info
|
||||
* by defining your implementation of that hook in mymodule.migrate.inc, it is
|
||||
* automatically invoked only when needed.
|
||||
* hook_migrate_api(), but because .migrate.inc is registered using
|
||||
* hook_hook_info, by defining your implementation of that hook in
|
||||
* example.migrate.inc, it is automatically invoked only when needed.
|
||||
*/
|
||||
|
||||
@@ -8,12 +8,12 @@ features[field][] = "node-migrate_example_oracle-field_mainimage"
|
||||
features[node][] = "migrate_example_oracle"
|
||||
files[] = "migrate_example_oracle.migrate.inc"
|
||||
name = "Migrate example - Oracle"
|
||||
package = "Migrate Examples"
|
||||
package = "Migration"
|
||||
project = "migrate_example_oracle"
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-11-07
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2015-02-09
|
||||
version = "7.x-2.7"
|
||||
core = "7.x"
|
||||
project = "migrate"
|
||||
datestamp = "1352299007"
|
||||
datestamp = "1423521491"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,26 +65,7 @@ function migrate_example_wine_uninstall() {
|
||||
}
|
||||
|
||||
function migrate_example_wine_disable() {
|
||||
MigrationBase::deregisterMigration('WinePrep');
|
||||
Migration::deregisterMigration('WineFileCopy');
|
||||
Migration::deregisterMigration('WineFileBlob');
|
||||
Migration::deregisterMigration('WineRegion');
|
||||
Migration::deregisterMigration('WineUser');
|
||||
Migration::deregisterMigration('WineVariety');
|
||||
Migration::deregisterMigration('WineBestWith');
|
||||
Migration::deregisterMigration('WineProducer');
|
||||
Migration::deregisterMigration('WineProducerXML');
|
||||
Migration::deregisterMigration('WineProducerXMLPull');
|
||||
Migration::deregisterMigration('WineProducerMultiXML');
|
||||
Migration::deregisterMigration('WineWine');
|
||||
Migration::deregisterMigration('WineComment');
|
||||
MigrationBase::deregisterMigration('WineFinish');
|
||||
Migration::deregisterMigration('WineUpdates');
|
||||
Migration::deregisterMigration('WineUserUpdates');
|
||||
Migration::deregisterMigration('WineVarietyUpdates');
|
||||
Migration::deregisterMigration('WineCommentUpdates');
|
||||
Migration::deregisterMigration('WineRole');
|
||||
Migration::deregisterMigration('WineTable');
|
||||
MigrateGroup::deregister('wine');
|
||||
}
|
||||
|
||||
function migrate_example_wine_schema_wine() {
|
||||
@@ -1212,7 +1193,7 @@ function migrate_example_wine_data_files() {
|
||||
$query = db_insert('migrate_example_wine_files')
|
||||
->fields($fields);
|
||||
$data = array(
|
||||
array(1, 'http://drupal.org/sites/all/modules/drupalorg/drupalorg/images/association-individual.png', NULL, NULL, NULL),
|
||||
array(1, 'http://placekitten.com/200/200', NULL, NULL, NULL),
|
||||
array(2, 'http://cyrve.com/files/penguin.jpeg', 'Penguin alt', 'Penguin title', 1),
|
||||
array(3, 'http://cyrve.com/files/rioja.jpeg', 'Rioja alt', 'Rioja title', 2),
|
||||
array(4, 'http://cyrve.com/files/boutisse_0.jpeg', 'Boutisse alt', 'Boutisse title', 2),
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<pr:producer xmlns:pr="http://www.wine.org/wine-producers">
|
||||
<pr:name>Château Latour</pr:name>
|
||||
<pr:description>Makers of grand vin Chateau Latour, Les Forts de Latour and Pauillac</pr:description>
|
||||
<pr:authorid>3</pr:authorid>
|
||||
<pr:region>Bordeaux</pr:region>
|
||||
</pr:producer>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<wn:content xmlns:wn="http://www.wine.org/wine">
|
||||
<wn:sourceid>0002</wn:sourceid>
|
||||
</wn:content>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<pr:producers xmlns:pr="http://www.wine.org/wine-producers">
|
||||
<pr:producer>
|
||||
<pr:sourceid>0009</pr:sourceid>
|
||||
<pr:name>Château Feytit Clinet</pr:name>
|
||||
<pr:description>Good things, they say, come in small packages; this is certainly the case at Château Feyit Clinet, the maker of thelabel Pomerol.</pr:description>
|
||||
<pr:authorid>1</pr:authorid>
|
||||
<pr:region>Pomerol</pr:region>
|
||||
</pr:producer>
|
||||
<pr:producer>
|
||||
<pr:sourceid>0010</pr:sourceid>
|
||||
<pr:name>Château Doisy-Védrines</pr:name>
|
||||
<pr:description>Blessed with ancient vines, the fruit here is often subject to high levels of Botrytis (or noble rot), which shrinks the grapes and concentrates sugar levels in the remaining juice.</pr:description>
|
||||
<pr:authorid>3</pr:authorid>
|
||||
<pr:region>Barsac</pr:region>
|
||||
</pr:producer>
|
||||
</pr:producers>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<pr:producers xmlns:pr="http://www.wine.org/wine-producers">
|
||||
<pr:producer>
|
||||
<pr:sourceid>0009</pr:sourceid>
|
||||
<pr:name>Château Bourgneuf</pr:name>
|
||||
<pr:description>Showing attractive red and dark fruits, cherry and plum, it is rich and even slightly racy, before concluding with classic Pomerol concentration and focus.</pr:description>
|
||||
<pr:authorid>3</pr:authorid>
|
||||
<pr:region>Pomerol</pr:region>
|
||||
</pr:producer>
|
||||
<pr:producer>
|
||||
<pr:sourceid>0010</pr:sourceid>
|
||||
<pr:name>Château Doisy-Daëne</pr:name>
|
||||
<pr:description>Medium bodied, with elegance rather than density there is a wide spectrum of flavours; apples, peaches, lemongrass and a touch of white spice. Delicious now.</pr:description>
|
||||
<pr:authorid>9</pr:authorid>
|
||||
<pr:region>Barsac</pr:region>
|
||||
</pr:producer>
|
||||
</pr:producers>
|
||||
Reference in New Issue
Block a user