Join us on Agaric's Meet.coop BigBlueButton videochat & screenshare for presentation and discussion, February 2, Sunday, at 15:00 UTC (10am Eastern).
https://meet.agaric.coop/rooms/a8m-x61-skh-ift/join
How can a group of thousands of people talk about and decide anything? How's this 'community' concept supposed to work at scale, even in theory?
Any Free/Libre Open Source Software project will have elements of do-ocracy (rule of those who do the work), but not all decisions should devolve to implementors. A better ideal is that decisions should be made by the people who are most affected.
Particularly when a decision strongly impacts more than those who carry it out, we need better ways of making decisions that give everyone their say. This starts by letting people by heard by everyone else. Fortunately, we can scale conversations and decisions in a fair and truly democratic way.
Today we continue the conversation about migration dependencies with a hierarchical taxonomy terms example. Along the way, we will present the process and syntax for migrating into multivalue fields. The example consists of two separate migrations. One to import taxonomy terms accounting for term hierarchy. And another to import into a multivalue taxonomy term field. Following this approach, any node and taxonomy term created by the migration process will be removed from the system upon rollback.

You can get the full code example at https://github.com/dinarcon/ud_migrations The module to enable is UD multivalue taxonomy terms whose machine name is ud_migrations_multivalue_terms. The two migrations to execute are udm_dependencies_multivalue_term and udm_dependencies_multivalue_node. Notice that both migrations belong to the same module. Refer to this article to learn where the module should be placed.
The example assumes Drupal was installed using the standard installation profile. Particularly, a Tags (tags) taxonomy vocabulary, an Article (article) content type, and a Tags (field_tags) field that accepts multiple values. The words in parenthesis represent the machine name of each element.
The example data for the taxonomy terms migration is fruits and fruit varieties. Each row will contain the name and description of the fruit. Additionally, it is possible to define a parent term to establish hierarchy. For example, “Red grape” is a child of “Grape”. Note that no numerical identifier is provided. Instead, the value of the name is used as a string identifier for the migration. If term names could change over time, it is recommended to have another column that did not change (e.g., an autoincrementing number). The following snippet shows how the source section is configured:
source:
plugin: embedded_data
data_rows:
- fruit_name: 'Grape'
fruit_description: 'Eat fresh or prepare some jelly.'
- fruit_name: 'Red grape'
fruit_description: 'Sweet grape'
fruit_parent: 'Grape'
- fruit_name: 'Pear'
fruit_description: 'Eat fresh or prepare a jam.'
ids:
fruit_name:
type: stringThe destination is quite short. The target entity is set to taxonomy terms. Additionally, you indicate which vocabulary to migrate into. If you have terms that would be stored in different vocabularies, you can use the vid property in the process section to assign the target vocabulary. If you write to a single one, the default_bundle key in the destination can be used instead. The following snippet shows how the destination section is configured:
destination:
plugin: 'entity:taxonomy_term'
default_bundle: tagsFor the process section, three entity properties are set: name, description, and parent. The first two are strings copied directly from the source. In the case of parent, it is an entity reference to another taxonomy term. It stores the taxonomy term id (tid) of the parent term. To assign its value, the migration_lookup plugin is configured similar to the previous example. The difference is that, in this case, the migration to reference is the same one being defined. This sets an important consideration. Parent terms should be migrated before their children. This way, they can be found by the lookup operation. Also note that the lookup value is the term name itself, because that is what this migration set as the unique identifier in the source section. The following snippet shows how the process section is configured:
process:
name: fruit_name
description: fruit_description
parent:
plugin: migration_lookup
migration: udm_dependencies_multivalue_term
source: fruit_parentTechnical note: The taxonomy term entity contains other properties you can write to. For a list of available options check the baseFieldDefinitions() method of the Term class defining the entity. Note that more properties can be available up in the class hierarchy.
The next step is to create a node migration that can write to a multivalue taxonomy term field. To stay on point, only one more field will be set: the title, which is required by the node entity. Read this change record for more information on how the Migrate API processes Entity API validation. The following snippet shows how the source section is configured for the node migration:
source:
plugin: embedded_data
data_rows:
- unique_id: 1
thoughtful_title: 'Amazing recipe'
fruit_list: 'Green apple, Banana, Pear'
- unique_id: 2
thoughtful_title: 'Fruit-less recipe'
ids:
unique_id:
type: integerThe fruits column contains a comma separated list of taxonomies to apply. Note that the values match the unique identifiers of the taxonomy term migration. If you had used numbers as migration identifiers there, you would have to use those numbers in this migration to refer to the terms. An example of that was presented in the previous post. Also note that there is one record that has no terms associated. This will be considered during the field mapping. The following snippet shows how the process section is configured for the node migration:
process:
title: thoughtful_title
field_tags:
- plugin: skip_on_empty
source: fruit_list
method: process
message: 'Row does not contain fruit_list.'
- plugin: explode
delimiter: ','
- plugin: callback
callable: trim
- plugin: migration_lookup
migration: udm_dependencies_multivalue_term
no_stub: trueThe title of the node is a verbatim copy of the thoughtful_title column. The Tags fields, mapped using its machine name field_tags, uses three chained process plugins. The skip_on_empty plugin reads the value of the fruit_list column and skips the processing of this field if no value is provided. This is done to accommodate the fact that some records in the source do not specify tags. Note that the method configuration key is set to process. This indicates that only this field should be skipped and not the entire record. Ultimately, tags are optional in this context and nodes should still be imported even if no tag is associated.
The explode plugin allows you to break a string into an array, using a delimiter to determine where to make the cut. Later, the callback plugin will use the trim PHP function to remove any space from the start or end of the exploded taxonomy term name. Finally, this array is passed to the plugin specifying the term migration as the one to use for the lookup operation. Again, the taxonomy term names are used here because they are the unique identifiers of the term migration. The `no_stub` configuration should be set to `true` to prevent terms to be created if they are not found by the plugin. This would not occur in the example because we make sure a match is found. If we did not set this configuration and we do not include the trim step, some new terms would be created with spaces at the beginning. Note that neither of these plugins has a migration_lookupsource configuration. This is because when process plugins are chained, the result of one plugin is sent as source to be transformed by the next one in line. The end result is an array of taxonomy term ids that will be assigned to field_tags. The migration_lookup is able to process single values and arrays.
The last part of the migration is specifying the process section and any dependencies. Refer to this article for more details on setting migration dependencies. The following snippet shows how both are configured for the node migration:
destination:
plugin: 'entity:node'
default_bundle: article
migration_dependencies:
required:
- udm_dependencies_multivalue_term
optional: []One way to set multivalue fields in Drupal migrations is assigning its value to an array. Another option is to set each value manually using field deltas. Deltas are integer numbers starting with zero (0) and incrementing by one (1) for each element of a multivalue field. Although you could set any delta in the Migrate API, consider the field definition in Drupal. It is possible that limits had been set to the number of values a field could hold. You can specify deltas and subfields at the same time. The full syntax is field_name/field_delta/subfield. The following example shows the syntax for a multivalue image field:
process:
field_photos/0/target_id: source_fid_first
field_photos/0/alt: source_alt_first
field_photos/1/target_id: source_fid_second
field_photos/1/alt: source_alt_second
field_photos/2/target_id: source_fid_third
field_photos/2/alt: source_alt_thirdManually setting a multivalue field is less flexible and error-prone. In today’s example, we showed how to accommodate for the list of terms not being provided. Imagine having to that for each delta and subfield combination, but the functionality is there in case you need it. In the end, Drupal offers more syntactic sugar so you can write shorted field mappings. Additionally, there are various process plugins that can handle arrays for setting multivalue fields.
Note: There are other ways to migrate multivalue fields. For example, when using the entity_generate plugin provided by Migrate Plus, there is no need to create a separate taxonomy term migration. This plugin is able to create the terms on the fly while running the import process. The caveat is that terms created this way are not deleted upon rollback.
What did you learn in today’s blog post? Have you ever done a taxonomy term migration before? Were you aware of how to migrate hierarchical entities? Did you know you can manually import multivalue fields using deltas? Please share your answers in the comments. Also, I would be grateful if you shared this blog post with others.
Next: Migrating users into Drupal - Part 1
This blog post series, cross-posted at UnderstandDrupal.com as well as here on Agaric.coop, is made possible thanks to these generous sponsors. Contact Understand Drupal if your organization would like to support this documentation project, whether it is the migration series or other topics.
We're proud to have worked with designer Todd Linkner to produce a bold and unique web site worthy of the world-renowned architectural firm Studio Daniel Libeskind.
Master planner for the Ground Zero memorial and architect of numerous acclaimed museums, offices, and residences around the world, Daniel Libeskind needed to present his and his studio's amazing work with commensurate impact online.
The Studio Daniel Libeskind project was one of our most ambitious to date. The architect partners and their one-woman public relations powerhouse, Amanda Ice, are fantastic to work with, as is the project's driving force and designer, Todd Linkner. We worked through many challenges, remaining flexible to the business needs and the design needs (developed in parallel with the work on base functionality)— and missed a September 11, 2011 launch date despite putting all hands on deck for as the scope outpaced the resources available. We continued, and completed the site successfully for beautiful presentation across browsers, iPad, and smart phones.
Agaric architected, built and themed the redesigned site. In addition to bringing the bold design to life and further making the site work for mobile devices (iOS, Android, and even BlackBerry), Agaric vastly improved the content creation workflow and press inquiry handling capabilities of the site, as well as search and filtering. We added generating stylish PDF versions of project pages, custom cropping and ordering of images and kept hardware requirements low and user perceived performance high by adding Varnish HTTP caching to the server.
Claudina Sarahe and Benjamin Melançon presented on the challenges and successes of this project at Pacific Northwest Drupal Summit.
We're thrilled to have had the opportunity to be such a large part in giving Studio Daniel Libeskind an online home worthy of the inspiring places they create in the physical world.
Inspired by (OK, word-for-word stealing from) a bag of pretzels, with the full connivance if not outright help from co-founder Ben, Dan Hakimzadeh crafted a surprisingly not-entirely-fictional account of Agaric's origin, which graced our About page from around 2006 through 2012:
The Story on Agaric
I've always had a passion for good design and healthy coding, even back in the days of owning a web site cart in downtown Natick. Back then, my partner and I made all natural HTML roll-up web sites and, as an incentive for customers to wait in line, we baked Drupal into different flavored designs. The Drupal became remarkably popular and before we knew it, the Agaric Design Collective was born.
Today, you can enjoy Agaric Design sites in six great flavors. They are baked, all natural and totally delicious. So treat yourself well, and treat yourself often
Visit http://www.agaricdesign.com to find more about our other sites, discover great recipes, and stay in touch.
Dan Hakimzadeh
Co-Founder
Sign up to be notified when Agaric gives a migration training:
An original co-founder of Agaric, Dan spends his time and energy building on this mystical phenomenon popularly called the Internet. He believes in the principles of free open source software and develops primarily using the Drupal content management framework.
Find his latest at dhakimzadeh.com.
Impedit veniam consectetur dolores id provident. Voluptas non voluptates rerum. Aut et laudantium nisi quia pariatur vero nemo.
Enim aperiam dolor numquam saepe perferendis fugit nam veniam. Impedit rerum repellendus voluptatem voluptatem fugit consequatur. Omnis illum quaerat vel voluptatem error praesentium.
The entity_generate process plugin receives a value and checks if there is an entity with that name. If the term exists then it uses it and if it does not then creates it (which is precisely what I need).
So, here is a snippet of the article migration YAML file using the entity_generate plugin:
id: blog
migration_group: Drupal
label: Blog
source:
plugin: d7_node
node_type: blog
destination:
plugin: entity:node
process:
status: status
created: created
field_tags:
plugin: sub_process
source: field_tags
process:
target_id:
- plugin: entity_generate
source: name
value_key: name
bundle_key: vid
bundle: tags
entity_type: taxonomy_term
ignore_case: true
…
In our field_tags field we are using the Drupal 7 field_tags Values We are going to read the entities and pass that value into the entity_generate plugin to create the entities. In this example, there is a problem, the d7_node migrate plugin (included in the migrate module) provides the taxonomy terms IDs and this will make the entity_generate plugin to create some taxonomy terms using the IDs as the term names, and this is not what we want.
So what I need to do is to get from somewhere the terms names, not their ids, to do that I need to add an extra source property.
First, we need to create a new custom module and in there create a source plugin which extends the Node process plugin, something like this (let's say that our custom module’s name is my_migration):
Create the file:
my_migration/src/Plugin/migrate/source/MyNode.php
And the file should contain this code:
namespace Drupal\my_migration\Plugin\migrate\source;
use Drupal\migrate\Row;
use Drupal\node\Plugin\migrate\source\d7\Node;
/**
* Adds a source property with the taxonomy term names.
*
* @MigrateSource(
* id = “my_node",
* source_module = "node"
* )
*/
class MyNode extends Node {
public function prepareRow(Row $row) {
$nid = $row->getSourceProperty('nid');
// Get the taxonomy tags names.
$tags = $this->getFieldValues('node', 'field_tags', $nid);
$names = [];
foreach ($tags as $tag) {
$tids[] = $tag['tid'];
}
if (!$tids) {
$names = [];
}
else {
$query = $this->select('taxonomy_term_data', 't');
$query->condition('tid', $tids, 'IN');
$query->addField('t', 'name');
$result = $query->execute()->fetchCol();
$names[] = ['name' => $result['name']];
foreach ($result as $term_name) {
$names[] = ['name' => $term_name];
}
}
$row->setSourceProperty('field_tags_names', $names);
return parent::prepareRow($row);
}
}
It does the following things:
Now our rows will have a property called fields_tags_names with the terms names, and we can pass this data to the entity_generate plugin.
We need to make a few adjustments in our initial migration file, first and most important update the source plugin to use our new source plugin:
source:
plugin: my_node
…
And update the source in the field_tags field to use the new field_tags_names source property.
…
field_tags:
plugin: sub_process
source: field_tags_names
….
The final migration file looks like this:
id: blog
migration_group: Drupal
label: Blog
source:
plugin: my_node
node_type: blog
destination:
plugin: entity:node
process:
status: status
created: created
field_tags:
plugin: sub_process
source: field_tags_names
process:
target_id:
- plugin: entity_generate
source: name
value_key: name
bundle_key: vid
bundle: tags
entity_type: taxonomy_term
ignore_case: true
…
And that’s it; if we run the migration, it will create on the fly the terms if they do not exist and use them if they do exist.
With Drupal 7's third and final release candidate unleashed on us all this morning, it is long past time to help the #D7CX movement with a seasonal offering of our own.
The most fitting gift would be porting a Drupal 6 module, but it wouldn't be a modern winter holiday without an environmentally irresponsible brand new toy: Introducing the Xray module, designed to help site builders and module developers investigate a Drupal 7 site.
The feature i'd like to point out in relation to porting modules and developing for Drupal 7 is Xray's report showing permission machine names (screenshot below). Permissions in Drupal 7 have human-friendly translatable titles, which is awesome, but the machine names – which module developers must use – have disappeared entirely from the user interface.
A moderately complex Drupal site can have a Permissions page like a Las Vegas building-side of lightbulbs... but with checkboxes – so i don't like to create new permissions unless i know of a clear use case. In developing modules, therefore, i prefer to reuse existing permissions when applicable. For instance, the Xray module, instead of defining a special permission for its reports, reuses the "View site reports" permission.
To refer to a permission in code, we need its machine name, not the title we can see on the Permissions page (admin/people/permissions). My chapter on module development in the coming Definitive Guide to Drupal 7 covers (in the course of building the Xray module) how to find permission machine names in the database or in the code, but it wouldn't be Drupal if we couldn't say there's a module for that, so a permission machine name report is incorporated into Xray itself.
Note: Anything incorrect in this chapter may misinform tens of thousands, so please, code review Xray module mercilessly, and all comments and suggestions requested and welcome!
Further note: Please vote for the When There's Not a Module for That, the intermediate module building session at Drupalcon Chicago which will include parts of the making of Xray module, and for sessions involving other DGD7 authors, and then for every session that interests you because there's less than 24 hours left to vote!
By the way, the machine name for "View site reports" is access site reports. Drupal will always keep us on our toes.
Xray, in another feature interesting for module developers and sometimes for site builders, displays what function and arguments produce each page you visit.
The most up-to-date code for Xray can be downloaded directly (tar.gz) from Gitorious.

Have you ever been asked to log into a website while you are viewing a page? And after doing so you get redirected to some page other than the one you were reading? This is an obvious and rather common usability problem. When this happens people lose track of what they were doing and some might not even bother to go back. Let's find out how to solve this in Drupal 8.
In a recent project a client wisely requested exactly that: whenever a user logs into the site, redirect them to the page they were before clicking the login link. This seemed like a very common request so we looked for a contrib module that provided the functionality. Login Destination used to do it in Drupal 7. Sadly the Drupal 8 version of this module does not provide the functionality yet.
Other modules, and some combinations of them, were tested without success. Therefore, we built Login Return Page. It a very small module that just does one thing and it does it well: it appends destination=/current/page to all the links pointing to /user/login effectively redirecting users to the page they were viewing before login. The project is waiting to be approved before promoting it to full project.
Have you had a similar need? Are there other things you are requested to do after login? Please share them in the comments.
UPDATE: It seems to this is a regression from Drupal 7 and there is an issue that would fix it. Thanks to Wim Leers for letting me know about it.