Planet Drupal

Syndicate content - aggregated feeds in category Planet Drupal
Updated: 8 hours 54 min ago

CU Boulder - Webcentral: Drupal Deep Dives: Ignoring Your Slaves

Thu, 03/08/2018 - 23:40

If you’re like me, you don’t know much about Drupal 7’s database layer besides a few functions you need to use daily. After scanning the comments for those API pages, I can usually get done what I need to, and I’ve never really have any weird database errors or issues that made me look more closely into the database APIs.

db_insert(); db_query(); db_merge(); // etc...

I work at an organization now that runs a service for 800+ sites with thousands of content editors. On any given day, the service performs more reads and writes than any application I’ve ever worked on before. Even with that caveat, our service doesn’t make all that many writes to the databases each day. However, our database infrastructure is set up (poorly) by another IT group (whose name shall not be mentioned), and because of that, we had to recently program defensively while performing database transactions.

Drupal Database API

Drupal has a nice overview page of documentation about how a developer ought to use the database APIs. Included in that section are topics I’ve never really explored.

For example, I’ve felt the pain of using Views as a query builder only to find out how slow and inefficient the default queries tend to be. Granted it is meant as a visual tool for site builders who can’t or don’t know how to use the database API functions, but it makes me sad sometimes.

Could I potentially use SQL Views to create some virtual tables and simplify my queries partially avoiding Drupal’s “join all the field tables together” issue? Probably, now that I know about SQL Views.

I won’t go over a lot of the functionality covered in the docs, but it’s not a bad idea to read through all of that API documentation if you never have before. That’s what Friday afternoons are for, right? Your Drupal application performs a lot of queries every request/response cycle, and by finding optimizations in these docs, you may drastically increase your app’s performance with only a few lines of code.

Master/Slave? Sounds Kinky

In the title of this post, I mentioned “slaves” mainly for the clickbait factor, but what I meant was in the context of a “master/slave” database relationship. Now people, put down the stones you are about to throw at me for my use of the word “slave” in 2018. In Drupal 7, that is the terminology used in the codebase, although in Drupal 8, it has been updated to “primary/replica” which is more semantic and descriptive. You can read a detailed discussion on the topic within the Drupal community, but I will still use “master/slave” at points in this post since Drupal 7 makes me use it.

Your site might only have one database, and for local development, my sites generally only have one database. The default.settings.php file shipped in Drupal 7 has a lengthy section on database configurations and what options are available to you.

For each database, you may optionally specify multiple "target" databases. A target database allows Drupal to try to send certain queries to a different database if it can but fall back to the default connection if not. That is useful for master/slave replication, as Drupal may try to connect to a slave server when appropriate and if one is not available will simply fall back to the single master server. The general format for the $databases array is as follows: @code $databases['default']['default'] = $info_array; $databases['default']['slave'][] = $info_array; $databases['default']['slave'][] = $info_array; $databases['extra']['default'] = $info_array; @endcode In the above example, $info_array is an array of settings described above. The first line sets a "default" database that has one master database (the second level default). The second and third lines create an array of potential slave databases. Drupal will select one at random for a given request as needed. The fourth line creates a new database with a name of "extra".

That segment of comments might be the only place you’ve seen “slave” mentioned in Drupal before. Normally, you’ve probably only used the “default” database info $databases['default']['default'] = $info_array; to set up a site. That’s all I was accustomed to using.

The “slave” database acts as a “replica” of the “master” or “default” or better yet “primary” database…you might be noticing why using “master/slave” was a bad idea regardless of the generally negative connotation of the word “slave”. It’s just not all that semantic when describing the responsibilities for each type of connection.

The “replica” database’s job is to sync with the default “primary” database so that there is only one canonical source of information. Replicas allow for failovers during times of high database load. Generally, reads are more important for the functionality of your application. Writes, on say saving a form, can always roll back transactions or provide feedback to a user on why the data can’t be saved. If an anonymous user goes to a page on your site and Drupal can’t read anything then everyone gets a fatal error.

If we go back to the comments above, you can see a “default” connection with one master and two slave databases. Drupal has some documentation on how that type of a database configuration works.

"This definition provides a single "default" server and two "slave" servers. Note that the "slave" key is an array. If any target is defined as an array of connection information, one of the defined servers will be selected at random for that target for each page request. That is, on one-page request, all slave queries will be sent to dbserver2 while on the next they may all be sent to dbserver3. This means that during any request one of the three default connections in that example might be used. On a site with high traffic, you can probably see how this database setup would come in handy for times of high load."

You can even tell Drupal to target one of the connections during a query.

$query = db_select('node', 'n', array('target' => 'slave')); Original DB Error

My initial foray into looking at master/slave replication in Drupal 7 came with a bug report.

PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '60-36' for key 'PRIMARY': INSERT INTO {linkchecker_bean} (bid, lid) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1); Array ( [:db_insert_placeholder_0] => 60 [:db_insert_placeholder_1] => 36 ) in _linkchecker_bean_add_bean_links() (line 196 of /data/code/profiles/express/express-2.8.3/modules/contrib/linkchecker/modules/linkchecker_bean/linkchecker_bean.module)

After some investigation, we thought that the slave database was being read before the sync from the master happened. When queried there was no entry in the slave database; however, the master database already had an entry. The master database always makes the writes and so a duplication error occurred during the next attempted insertion.

// Remove all links from the links array already in the database and only // add missing links to database. $missing_links = _linkchecker_bean_links_missing($bean->bid, $links); // Ignore slave database briefly. variable_set('maximum_replication_lag', 300); db_ignore_slave(); // Only add unique links to database that do not exist. $i = 0; foreach ($missing_links as $url) { $urlhash = drupal_hash_base64($url); $link = db_query('SELECT lid FROM {linkchecker_link} WHERE urlhash = :urlhash', array(':urlhash' => $urlhash))->fetchObject(); if (!$link) { $link = new stdClass(); $link->urlhash = $urlhash; $link->url = $url; $link->status = _linkchecker_link_check_status_filter($url); drupal_write_record('linkchecker_link', $link); } db_insert('linkchecker_bean') ->fields(array( 'bid' => $bean->bid, 'lid' => $link->lid, )) ->execute(); // ...

The original code makes a db query for $missing_links that must have gone to a replica database that hadn’t yet synced with the primary database. That is why later in the code when the db_insert() happens, the insert fails.


My first thought when I looked at the code was to use db_merge() instead of db_insert(). By using a merge query you either make an update or insertion query to the database table. By providing the same primary keys as the ones you are inserting, you can ensure that the database query never fatal errors due to duplicate content existing in the table.

db_merge('linkchecker_bean') ->key(array( 'bid' => $bean->bid, 'lid' => $link->lid, )) ->fields(array( 'bid' => $bean->bid, 'lid' => $link->lid, )) ->execute();

However, this “solution” doesn’t really address the issue. The code isn’t supposed to update a value that could already exist in the table. In this case, the correct thing is happening by giving me a fatal error. The problem is that the error isn’t caught.

Proper Exception Handling

You should always wrap any function call that might fail terribly in a try/catch statement. The try block of code acts just as it would without try {} wrapped around it. The catch block allows any potential error in the try block to be caught and dealt with without breaking execution of the PHP script.

$txn = db_transaction(); try { db_insert('linkchecker_bean') ->fields(array( 'bid' => $bean->bid, 'lid' => $link->lid, )) ->execute(); } catch (Exception $e) { $txn->rollback(); watchdog_exception('linkchecker_bean', $e); }

Now we have preserved the original db_insert()while catching the original fatal error. You’ll also notice that adb_transaction() object is used to rollback any transaction if the insert fails.

I never knew about that functionality in Drupal 7, but I have grown accustomed to being able to rollback database transactions in other PHP frameworks. Too bad most module developers don’t integrate a rollback on erroneous database transactions. Drupal core could be taking care of this under-the-hood, but I’d rather see it explicitly defined in contributed code. From now on, I’ll probably be using those functions in my hook_update() code. You can read more about database error handling in the Drupal database documentation.

I was pretty satisfied with submitting a patch the Linkchecker project based on the code above, except that it didn’t fix our issue. Since our theory revolved around database replication being slow, we had to go one step further and explicitly define the relationship between primary and replica database connections at the time of the missing link's query.

Finally, Ignore The Slaves

We finally get to do it, folks. Ignore those stupid slaves…and Twitter has gone wild again with hateful tweets directed at me…okay, okay, back to calling them replicas. You can tell Drupal to ignore the replica databases and only interact with the primary connection if you need to.

// Ignore slave database briefly. variable_set('maximum_replication_lag', 300); db_ignore_slave(); // Remove all links from the links array already in the database and only // add missing links to database. $missing_links = _linkchecker_bean_links_missing($bean->bid, $links); // Only add unique links to database that do not exist. $i = 0; foreach ($missing_links as $url) { $urlhash = drupal_hash_base64($url); $link = db_query('SELECT lid FROM {linkchecker_link} WHERE urlhash = :urlhash', array(':urlhash' => $urlhash))->fetchObject(); if (!$link) { $link = new stdClass(); $link->urlhash = $urlhash; $link->url = $url; $link->status = _linkchecker_link_check_status_filter($url); drupal_write_record('linkchecker_link', $link); } $txn = db_transaction(); try { db_insert('linkchecker_bean') ->fields(array( 'bid' => $bean->bid, 'lid' => $link->lid, )) ->execute(); } catch (Exception $e) { $txn->rollback(); watchdog_exception('linkchecker_bean', $e); } // Go back to using the slave database. // db_ignore_slave() sets this session variable that another function uses to see if the slave should be ignored. unset($_SESSION['ignore_slave_server']); // ...

Our final code ignores the replicas for a brief time using db_slave_ignore() and then returns querying back to normal by unsetting $_SESSION['ignore_slave_server'] after all of the database queries have run.

Internally, Drupal uses the session variable, which is a timestamp, to check whether the slave server should be ignored. This is done via hook_init() in the System module usingDatabase:ignoreTarget('default', 'slave'). There is also a nice note in the comments about how the ignoring works.

function system_init() { $path = drupal_get_path('module', 'system'); // Add the CSS for this module. These aren't in, because they // need to be in the CSS_SYSTEM group rather than the CSS_DEFAULT group. drupal_add_css($path . '/system.base.css', array('group' => CSS_SYSTEM, 'every_page' => TRUE)); if (path_is_admin(current_path())) { drupal_add_css($path . '/system.admin.css', array('group' => CSS_SYSTEM)); } drupal_add_css($path . '/system.menus.css', array('group' => CSS_SYSTEM, 'every_page' => TRUE)); drupal_add_css($path . '/system.messages.css', array('group' => CSS_SYSTEM, 'every_page' => TRUE)); drupal_add_css($path . '/system.theme.css', array('group' => CSS_SYSTEM, 'every_page' => TRUE)); // Ignore slave database servers for this request. // // In Drupal's distributed database structure, new data is written to the // master and then propagated to the slave servers. This means there is a // lag between when data is written to the master and when it is available on // the slave. At these times, we will want to avoid using a slave server // temporarily. For example, if a user posts a new node then we want to // disable the slave server for that user temporarily to allow the slave // server to catch up. That way, that user will see their changes immediately // while for other users we still get the benefits of having a slave server, // just with slightly stale data. Code that wants to disable the slave // server should use the db_ignore_slave() function to set // $_SESSION['ignore_slave_server'] to the timestamp after which the slave // can be re-enabled. if (isset($_SESSION['ignore_slave_server'])) { if ($_SESSION['ignore_slave_server'] >= REQUEST_TIME) { Database::ignoreTarget('default', 'slave'); } else { unset($_SESSION['ignore_slave_server']); } } // Add CSS/JS files from module .info files. system_add_module_assets(); } Wait, We’ve Already Executed hook_init()?

Since it happens in a hook_init(), then pray-tell how is the database ignored later in my Linkchecker code? I’m not sure either. Subsequent requests will ignore the replica for as long as the timeout is active, but the queries in my code could possibly still hit the slave database. Wait, so I haven’t fixed my issue. And you certainly don’t want to place db_ignore_slave() before the hook_init() is called, essentially always setting a timeout to ignore the replica.

In the comment above the session variable check, it explains that some users will see stale data. This is okay for the scenario where I save content via a node edit screen and expect it to show up on the next node view request. But what happens when there is no “user” saving content and the queries happen within a request cycle, not write on one request and then read on another.

I am one of the “users” who can’t get stale data because we are relying on it to make a subsequent database insert in the same request. What we really need to do is “target” the default connection when we make a query.

db_query('SELECT lid FROM {linkchecker_link} WHERE urlhash = :urlhash', array(':urlhash' => $urlhash), array('target' => 'default'))->fetchObject();

Up until this point, I had only used the $args array to pass in dynamic variables to database queries and avoid SQL injection, but there is another $options parameter you can use to identify the database target among other things. While the allowed values for $options can be hard to know from the db_query() API documentation, you can at least find the default values created when that parameter isn’t passed into db_query(). Based on the docs for the “target” key, you can have two values for the target: “slave” or< “default”.

"The database "target" against which to execute a query. Valid values are "default" or "slave". The system will first try to open a connection to a database specified with the user-supplied key. If one is not available, it will silently fall back to the "default" target. If multiple databases connections are specified with the same target, one will be selected at random for the duration of the request. So when you don’t explicitly specify a target and have more than one connection, e.g. adding a replica, the query will pick a target at random which might be a slave with stale data."

After adding a target in another Linckchecker query, my job was done…and I didn’t even have to ignore the slaves after all. Hopefully, you now know something about database replication in Drupal 7, how to use db_ignore_slave() properly, and how to explicitly target databases per query as well.

Developer Blog

Dries Buytaert: Thank you, Tiffany

Thu, 03/08/2018 - 21:17

I recently had the opportunity to read Tiffany Farriss' Drupal Association Retrospective. In addition to being the CEO of, Tiffany also served on the Drupal Association Board of Directors for nine years. In her retrospective post, Tiffany shares what the Drupal Association looked like when she joined the board in 2009, and how the Drupal Association continues to grow today.

What I really appreciate about Tiffany's retrospective is that it captures the evolution of the Drupal Association. It's easy to forget how far we've come. What started as a scrappy advisory board, with little to no funding, has matured into a nonprofit that can support and promote the mission of the Drupal project. While there is always work to be done, Tiffany's retrospective is a great testament of our community's progress.

I feel very lucky that the Drupal Association was able to benefit from Tiffany's leadership for nine years; she truly helped shape every aspect of the Drupal Association. I'm proud to have worked with Tiffany; she has been one of the most influential, talented members of our Board, and has been very generous by contributing both time and resources to the project.

Acro Media: Urban Hipster: Updates & What's New

Thu, 03/08/2018 - 20:44

The Urban Hipster (UH) Drupal 8 Commerce 2 demo site has been gaining a lot of traction lately due to some fanfare and sweet promo videos. An increasing number of people are trying it out and viewing the code (which is publicly available on GitHub). It was time for a bit of a content overhaul. Here's what's changed.

Public Domain Images

All of the imagery and icons used in the site are now created by Acro Media or taken from If you're not aware of Unsplash, they provide high quality images that are freely available to use in any way, shape or form. It's a pretty awesome service and supports the open source philosophy we in the Drupal community share.

Product Page Galleries

The biggest feature we added in this release has to do with the product pages. Products can now display a gallery of images on the product page instead of just showing the current variation. I won't get into the details here, but you can watch the video for more information. Here's an example.

More "Full" Product Examples

The UH demo was originally intended to be a sales tool for our staff. Because of this, we added a bunch of products, but only a handful were fully filled out with attributes, sample reviews, related products, etc. The rest were just placeholders to fill out the store. Now that more people are looking at it, we felt we needed more (if not all) of the products to be more robust so that it didn't matter which product you happen to look at. This was something we did in this content overhaul. Almost every product now includes multiple images, related products, variation options, etc. Here's an example.

Configuration Cleanup

Since we were adding all new content anyway, it was a good opportunity to clean up some of the attributes, product types and product variation types that weren't really needed or not well represented. A lot of people are looking to this demo for examples of configuration, so it's important that we try to keep it as clean as possible.

And that's about it! Enjoy the video and the site updates.

Related Links

Ben Marshall: Load JS & CSS Conditionally in Drupal 7

Thu, 03/08/2018 - 18:00

This post was originally published on May 22, 2013 and last updated March 8, 2018 thanks to some helpful input by Steve Elkins.

Drupal 7 is a haus at combining CSS & JS files. This can help boost page performance & optimization easily, but if not used right, can do the complete opposite. In this post, we’ll go over how to load JS & CSS files based on conditionals like URL, module, node, views and more.

Before we dive in, get somewhat familiar with the drupal_add_js and drupal_add_css functions. We’ll use these to load the actual JS and CSS files.

hook_init – runs on every page /** * Implements hook_init() * * @link */ function HOOK_init() { // Using the equivalent of Apache's $_SERVER['REQUEST_URI'] variable to load based on URL // @link! if (request_url() === 'your-url-path') { drupal_add_js( /* parameters */ ); drupal_add_css( /* parameters */ ); } }

Using hook_init is one of the simplest methods to load specific JS and CSS files (don’t forget to replace HOOK with the theme or module machine name).

Be careful, this method get’s ran on every page, so it’s best to use this method only when you actually need to check every page for your conditional. A good example, loading module CSS and JS files. A bad example, loading node-specific CSS and JS files. We’ll go over that next.

There’s also a similar preprocess function, template_preprocess_page you could use, but it too get’s ran on every page and is essentially the same as hook_init.

template_preprocess_node – runs on node pages /** * Implements template_preprocess_node() * * @link */ function TEMPLATE_preprocess_node(&$vars) { // Add JS & CSS by node type if( $vars['type'] == 'your-node-type') { drupal_add_js( /* parameters */ ); drupal_add_css( /* parameters */ ); } // Add JS & CSS to the front page if ($vars['is_front']) { drupal_add_js( /* parameters */ ); drupal_add_css( /* parameters */ ); } // Given an internal Drupal path, load based on node alias. if (drupal_get_path_alias("node/{$vars['#node']->nid}") == 'your-node-id') { drupal_add_js( /* parameters */ ); drupal_add_css( /* parameters */ ); } }

Using template_preprocess_node is perfect when loading JS and CSS files based on nodes (don’t forget to replace TEMPLATE with the theme machine name). Since it only get’s run on nodes, it’s great to use when you want to load CSS and JS files on specific node types, front pages, node URLs, etc.

template_preprocess_views_view – runs every view load /** * Implements template_preprocess_views_view() * * @link */ function TEMPLATE_preprocess_views_view(&$vars) { // Get the current view info $view = $vars['view']; // Add JS/CSS based on view name if ($view->name == 'view_name') { drupal_add_js( /* parameters */ ); drupal_add_css( /* parameters */ ); } // Add JS/CSS based on current view display if ($view->current_display == 'current_display_name') { drupal_add_js( /* parameters */ ); drupal_add_css( /* parameters */ ); } }

Using template_preprocess_node is useful when loading JS and CSS files when a particular view is being used (don’t forget to replace TEMPLATE with the theme machine name).

Helpful Methods for Conditionals

Here’s a few helpful Drupal methods you can use for your conditionals. Have one you use often? Let me know in the comments below.

  • request_uri – Returns the equivalent of Apache’s $_SERVER[‘REQUEST_URI’] variable.
  • drupal_get_path_alias – Given an internal Drupal path, return the alias set by the administrator.
Looking like a foreign language to you?

Not a developer or just lost looking at the code snipplets above? Shoot me a question in the comments below, or give these ‘plug-and-play’ modules a try for a GUI alternative:

The post Load JS & CSS Conditionally in Drupal 7 appeared first on Ben Marshall.

Flocon de toile | Freelance Drupal: Send transactional emails related to user account in HTML format with Drupal 8

Thu, 03/08/2018 - 11:00
We have several solutions to automatically send emails emitted by a Drupal 8 project in HTML format. Without being able to quote them all, we can use SwiftMailer, MimeMail to send mails from the server itself, or Mailjet API, MailGun, etc. to send emails from a third-party platform. In a few clicks, we can then emit different emails, whether they are transactional (account creation, order creation, subscription, etc.) or business (Newsletters, Activity Log, What you missed, etc. .), in HTML format. It will then remain to implement one (or more) responsive email template that will be correctly read and interpreted on most mail software. And it's probably the most important part. There remains a special case: that of all the emails emitted according to the different events related to the life of a user account (Creation, Waiting for approval, Deletion, Welcome Mail, etc.). Let's see how to simply send this emails in a HTML format too.

Amazee Labs: DrupalCamp London 2018 - Keeping it Cool

Thu, 03/08/2018 - 10:41
DrupalCamp London 2018 - Keeping it Cool

I attended DrupalCamp London this past weekend. This was the 6th consecutive year the event has been hosted, which is remarkable. My mission getting to the event turned out to be just as remarkable! Keep reading to find out more about both.

Fran Garcia Thu, 03/08/2018 - 10:41

Something that the organisers couldn’t control was the fact that the whole of the UK was facing the biggest freeze in recent years. This meant that trains and buses were canceled. Schools, shops, and roads were also closed. As you can imagine, getting from Birmingham to London wasn’t an easy task, but I was on a mission and decided to venture into the elements (as I wasn’t able to either teleport nor fly there - see below!)


To make a long story short, plan A and B failed due to multiple cancellations so I opted for no-plan at all - just do and keep doing until I got there. What would usually be a two-hour relaxing journey became a nearly six-hour bumpy, cold, and wet quest through the elements... but guess what? I made it to London!


But why did I want to get there so badly? Going to the DrupalCamp was reason enough but additionally, I was selected to do three different talks at the camp. I was really excited about the opportunity and wouldn't miss it for the world!

My three talks were completely different:

The slides are linked above and the videos will soon be available on the Camp YouTube channel.

I am happy with how the talks went. People were engaged and participative - I even missed lunch one day as I was answering questions for more than 40 minutes after the talk. But most importantly I think they made a difference to the people who attended. I’m glad that one way or another I could give something back to the Drupal community. A special thanks goes out to Joe Shindelar for his little push in Vienna :-)

The rest of the sessions were great too. We had amazing speakers, inspiring talks, live demos, in-depth discussions and a unique atmosphere over the whole weekend. I met people from France, Italy, Iceland, Finland, Spain and many other parts of the UK, which was great. I loved listening to their stories and knowing how people work and use Drupal too. I also realised how proud and happy I am to be an Amazee.

The keynotes were especially inspiring, stating once and again Drupal's core values and mission, talking about the community behind Drupal, its importance and the ways in which we can all help improve it. Thanks to Ryan Szrama (Commerce Guys), Chris Teitzel (Lockr) and Baddý Sonja Breidert (1xINTERNET) for sharing your experiences, visions, opinions and for being so close and approachable during the weekend.

And of course thanks, DrupalCamp London (@DrupalCampLDN #dclondon) for organising such a great event. Huge kudos to organizers and volunteers, who were all over the place ensuring that the event was successful and flawless. See you next year!

Community: Community Working Group Call for New Members

Thu, 03/08/2018 - 03:03

Are you interested in volunteer work primarily concerned with community health, conflict resolution, and Drupal Code of Conduct matters? Then the Drupal Community Working Group (CWG) can benefit from your help. We are looking to add new volunteer members to our small, but dedicated, team and we’d like to hear from you! 

About the CWG

As our Charter states “The mission of the Community Working Group (CWG) is to uphold the Drupal Code of Conduct in order to maintain a friendly and welcoming community for the Drupal project.”; this means we very often deal with conflict resolution and guidance in the handling of Code of Conduct violations involving Drupal Community members and Drupal Community spaces. Although this sometimes can take a big chunk of our time, we also work on proactive tasks involving community health, growth, and awareness. For example, we get to honor amazing members of the community with the prestigious Aaron Winborn Award as well as work on tools and processes to facilitate community health and growth.

We currently are a diverse group of five members from three continents and we are looking to continue to diversify. We come from different backgrounds, and have different experiences, perspectives, and opinions, but we all share a deep desire to help community members and have them treat one another with respect, dignity, and encouragement. 

About you

Characteristics that are in line with what we do and what we are looking for:

  • Patient and open-minded
    We are often involved in high-stress and/or highly emotional situations. As facilitators, we must be able to listen to all sides patiently and without judgment to try to fully understand the situation as best we can. Although the work we do is rewarding and necessary, sometimes the issues we deal with can be tough and emotionally draining, and individuals looking to join the CWG need to be aware that it can involve a fair amount of emotional labor.
  • Self-driven and self-motivated
    A typical time commitment for CWG members is a couple hours a week, though that can vary. We are all volunteer members and, as such, juggle our own work-life-community schedules and try to create a balance where we do as much as we can without getting burned-out. We encourage and support members taking breaks from CWG business when needed.
  • Discrete and confidential
    We often deal with sensitive and confidential information and all members are expected to safeguard any and all information we come in contact with in our roles as CWG members. Additionally, we disclose any potential conflicts of interest and recuse ourselves when appropriate.
  • Love for the community
    All members of the CWG are volunteers and donate our own time and efforts while doing work we consider important and necessary. The main drive behind our work is the health of the community and its members.
  • The desire to uphold the Drupal Code of Conduct and share the Drupal values and principles.
    This almost goes without saying, but we think it is worth it for you to take a(nother) look at the Drupal Code of Conduct.
Still with us?

If you think you’d be a good fit or know someone you think is, please reach out!

Dries Buytaert: Drupal 8.5.0 released

Thu, 03/08/2018 - 02:45

Earlier today, we released Drupal 8.5.0, which ships with improved features for content authors, site builders and developers.

Content authors can benefit from enhanced media support and content moderation workflows. It is now easier to upload, manage and reuse media assets, in addition to moving content between different workflow states (e.g. draft, archived, published, etc).

Drupal 8.5.0 also ships with a Settings Tray module, which improves the experience for site builders. Under the hood, the Settings Tray module uses Drupal 8.5's new off-canvas dialog library; Drupal module developers are encouraged to start using these new features to improve the end-user experience of their modules.

It's also exciting to see additional improvements to Drupal's REST API. With every new release, Drupal continues to extend investments in being an API-first platform, which makes it easier to integrate with JavaScript frameworks, mobile applications, marketing solutions and more.

Finally, Drupal 8.5 also ships with significant improvements for Drupal 7 to Drupal 8 migration. After four years of work, 1,300+ closed issues and contributions from over 570 Drualists, the migrate system's underlying architecture in Drupal 8.5 is fully stable. With the exception of sites with multilingual content, the migration path is now considered stable. Needless to say, this is a significant milestone.

These are just a few of the major highlights. For more details about what is new in Drupal 8.5, please check out the official release announcement and the detailed release notes.

What I'm probably most excited about is the fact that the new Drupal 8 release system is starting to hit its stride. The number of people contributing to Drupal continues to grow and the number of new features scheduled for Drupal 8.6 and beyond is exciting.

In future releases, we plan to add a media library, support for remote media types like YouTube videos, support for content staging, a layout builder, JSON API support, GraphQL support, a React-based administration application and a better out-of-the-box experience for evaluators. While we have made important progress on these features, they are not yet ready for core inclusion and/or production use. The layout builder is available in Drupal 8.5 as an experimental module; you can beta test the layout builder if you are interested in trying it out.

I want to extend a special thank you to the many contributors that helped make Drupal 8.5 possible. Hundreds of people and organizations have contributed to Drupal 8.5. It can be hard to appreciate what you can't see, but behind every bugfix and new feature there are a number of people and organizations that have given their time and resources to contribute back. Thank you!

Drupal blog: Drupal 8.5.0 is now available

Wed, 03/07/2018 - 22:51
What's new in Drupal 8.5.0?

This new version makes Media module available for all, improves migrations significantly, stabilizes the Content Moderation and Settings Tray modules, serves dynamic pages faster with BigPipe enabled by default, and introduces a new experimental entity layout user interface. The release includes several very important fixes for workflows of content translations and supports running on PHP 7.2.

Download Drupal 8.5.0

Media in core improved and available to all site builders

In Drupal 8.4, we added a Media API to core that drew on work from the contributed Media Entity module, but the module was hidden from the user interface due to user experience issues. In Drupal 8.5, many of the usability issues have been addressed, and the module now can be enabled normally. Media in Drupal 8.5 supports uploading and playing audio and video files, as well as listing and reusing media.

For an optimal user experience, we suggest enhancing the core feature set with the rich ecosystem of contributed modules that extends the core Media module. In future releases, we will improve the core user experience with a media library and other tools, add WYSIWYG integration, add support for remote media types like YouTube videos, and provide an upgrade path for existing basic File and Image field data on existing sites.

Settings Tray and Content Moderation now stable

Two experimental modules originally added with Drupal 8.2.0 have been steadily improving in past releases and are now stable. The Settings Tray module provides a quick solution to manage settings in context, such as moving items around in a menu block. The Content Moderation module allows defining content workflow states such as Draft, Archived, and Published, as well as which roles have the ability to move content between states. Drupal 8.5.0 also adds support for translations to be moderated independently.

New experimental Layout Builder module

The new experimental Layout Builder module provides display layout capabilities for articles, pages, user profiles, and other entity displays. Layout Builder uses the same "outside-in" user interface that Settings Tray module does, allowing site builders to edit their layouts on the actual page (rather than having to go to a separate form on the backend). The current user interface is a basic implementation but we expect it will improve significantly in the coming months.

Big steps for migrations

After over four years of work, this release marks the Migrate system's architecture stable. The Drupal Migrate and Drupal Migrate UI modules are also considered stable for upgrading monolingual sites. (Multilingual site upgrades are still not fully supported.) Support for incremental migrations is also included in this release. See the migrate announcement for further details on migrating to Drupal 8.

BigPipe by default

The BigPipe module provides an advanced implementation of Facebook's BigPipe page rendering strategy for greatly improved perceived performance for pages with dynamic, personalized, or uncacheable content. The module was added in Drupal 8.1.0 experimentally and became stable in Drupal 8.3.0. Following real-world testing, Big Pipe is now included as part of Drupal 8.5.0's Standard installation profile, so that all Drupal 8 sites will be faster by default. BigPipe is also the first new Drupal 8 feature to mature from an experimental prototype all the way to being part of a standard installation!

Groundwork for a Drupal 8 "Out of the Box" demo

Drupal 8.5.0 includes the groundwork for a new demo profile and theme from the Out of the Box Initiative, which will be a beautiful, modern demonstration of Drupal's capabilities. This will allow us to provide the demo experimentally, possibly in a future Drupal 8.5 release. (The demo profile and theme should not be used on actual production or development sites since no backwards compatibility or upgrade paths are provided.) If you'd like to see this demo in action, you can also see it in the 8.6.x development version.

PHP 7.2 now supported

Drupal 8.5.0 now runs on PHP 7.2, which comes with new features and improves performance over PHP 7.1. PHP 7.2 is now the recommended PHP version to use with Drupal 8.

What does this mean for me? Drupal 8 site owners

Update to 8.5.0 to continue receiving bug and security fixes. The next bugfix release (8.5.1) is scheduled for April 4, 2018.

Updating your site from 8.4.5 to 8.5.0 with update.php is exactly the same as updating from 8.4.4 to 8.4.5. Drupal 8.5.0 also has updates to several dependencies, including a backwards-compatible update to a Symfony long-term-support release (which will be supported for many years). Modules, themes, and translations may need updates for these and other changes in this minor release, so test the update carefully before updating your production site.

Note that Drupal 8 will require PHP 7 starting in March 2019, one year from now. If your site is hosted on PHP 5.5 or 5.6, you should begin planning to upgrade (and consider upgrading to PHP 7.2 now that it is supported). See the Drupal core announcement about the PHP 5 end-of-life for more information.

Drupal 6 and 7 site owners

Drupal 7 is still fully supported and will continue to receive bug and security fixes throughout all minor releases of Drupal 8. Drupal 6 is no longer supported. See the migrate announcement for further details on migrating to Drupal 8.

Translation, module, and theme contributors

Minor releases like Drupal 8.5.0 include backwards-compatible API additions for developers as well as new features. Read the 8.5.0 release notes for more details on the improvements for developers in this release.

Since minor releases are backwards-compatible, modules, themes, and translations that supported Drupal 8.4.x and earlier will be compatible with 8.5.x as well. However, the new version does include some changes to strings, user interfaces, internal APIs and API deprecations. This means that some small updates may be required for your translations, modules, and themes. See the announcement of the 8.5.0 release candidate for more background information.

Drupal blog: Big steps for migrations in Drupal 8.5.0

Wed, 03/07/2018 - 22:51

After over four years of work with over 570 contributors and 1300+ closed issues, Drupal 8.5.0 releases the Migrate system's architecture as fully stable. This means that developers can write migration paths without worrying for stability of the underlying system.

On top of that the Migrate Drupal and Migrate Drupal UI modules (providing Drupal 6 and 7 to Drupal 8 migrations) are considered stable for upgrading monolingual sites. All of the remaining critical issues for the Migrate Drupal module's upgrade paths and stability are related to multilingual migration support (so multilingual site upgrades are still not fully supported).

Support for incremental migrations is now also available, which means that site owners can work gradually on their new Drupal 8 site while content is still being added to the old site. When migrations (including incremental migrations) are run through the user interface, site owners will now see a warning if some data on the Drupal 8 site might be overwritten. (A similar fix for Drush is not yet available, so be careful not to overwrite data if you run a migration on the command line.) 

Upgrade instructions for Drupal 6 and Drupal 7 sites can be found in the Upgrading to Drupal 8 handbook. Your old site can still remain up and running while you test migrating your data into your new Drupal 8 site. If you happen to find a bug, that is not a known migrate issue, your detailed bug report with steps to reproduce is a big help!

Unlike previous versions, Drupal 8 stores translated content as single entities. Multilingual sites with reference fields (node_reference, entity_reference) or multilingual menus can upgrade to Drupal 8 using Drush, executing the desired migrations one by one. In this process you need to create and run a series of additional custom migrations to reflect the new entity identifiers assigned during earlier migrations. There is no automation implemented for this process yet.

Data can be migrated to Drupal 8 also from non-Drupal sources such as CSV, XML, JSON, or directly from 3rd party systems' databases. For instructions and examples, refer to Migrate API handbook.

Huge thanks again to all the contributors who made this possible.

InternetDevels: From Drupal 8.1.0 to Drupal 8.5.0: great features | Infographic

Wed, 03/07/2018 - 22:35

To honor the Drupal 8.5.0 release on March 7, we will not just describe what’s new in it. Drupal 8 has already had 5 awesome minor releases: Drupal 8.1.0, Drupal 8.2.0, Drupal 8.3.0, Drupal 8.4.0, and now Drupal 8.5.0. Each of these versions gives us great novelties that make Drupal 8 easier to use for editors, faster, more innovative, and more.

Read more blog: What’s new on - February 2018

Wed, 03/07/2018 - 21:53

Read our Roadmap to understand how this work falls into priorities set by the Drupal Association with direction and collaboration from the Board and community. Updates Reimagining's front pages to serve distinct personas

Drupal serves a wide audience of users, from developers to marketers to content editors and beyond. Historically, has been focused on our community of contributors, whether those contributions are in code, documentation, volunteer support, camp organizing, etc. However, only 1 in 15 visitors to are an authenticated user, and the rest are primarily visiting as representatives of an end-user organization that is evaluating Drupal. We want to serve these visitors better.

In February we held an off-site in Portland to consolidate our research about the personas within end-user organizations who make the decision to adopt Drupal. We identified three key roles:

  • Technical evaluators - who are often developer evangelists within their organization

  • Marketing and business users -who are evaluating Drupal as a platform. They are interested in the editorial experience and time-to-market for building a solution that integrates with tools they already use

  • Agencies - who are already using Drupal for their clients, or are considering making it central to their business.

From there, we developed some initial concepts for a reimagining of the front page of to better serve these first three personas.

This work will carry us through DrupalCon Nashville and beyond, so expect additional updates over the coming months.

Contribution credit update

The Drupal project has an innovative system of crediting users and sponsoring organizations for the work they contribute. However, as a system that we've pioneered, there is always room for additional improvements. One area that needed improvement was the date used for the assignment of credit. In the past, the credit for a user or organization would be tracked to the timestamp of the latest activity on the issue. This was a good approximation, but additional comments after issue resolution would bump the date of the credit.

We've updated the way that contribution credits are calculated - so that it is now based on the date that the issue was closed(status last changed) instead of the date of the last change to the issue. This change affects both individual contribution credits and the marketplace ranking.

Documentation improvements

As our new team member Dhanya has come on board, she has helped make some great improvements to the documentation system, including: fixing the display of sidebar lists of guide contents, increasing the visibility of the current page indicators, and swapping the grid treatment for a more readable guide contents layout

Accessibility and readability

We've made two additional small fixes.

One for accessibility - improving the keyboard 'skip to…' links in the top navigation.

… And one for navigating issues, fixing a bug that prevented links to comments on multi-page threads. Now, any user who receives an email notification about a multi-page issue should be properly linked to the correct comment.

Preparing our live-streaming capability for DrupalCon Nashville

For DrupalCon Nashville, rather than relying on a vendor, we are going to be managing the live stream of the keynotes and closing session ourselves, together with the AV staff of the venue. In February we spent some time putting together our equipment and running some streaming tests.

Continued work to reduce our PCI scope

In February we finished migrating our donation process for both USD and Euro donations to new payment processors to reduce our PCI scope and thus maintenance costs. We've also launched the beta of the membership system, and will hopefully complete the migration of existing memberships soon.

If you are not yet a member of the Drupal Association, and would like to support us both by joining and helping us test the new membership system, you can sign up here. (If you are an existing member, please continue to process your renewals on the original system for now).

Infrastructure Updates Git servers updated

We migrated our existing git infrastructure from bare metal servers to virtual machines, which will help to make our infrastructure more flexible and portable in the future. This has been an ongoing effort, and the git servers are among the last of the servers to be migrated.

Continued tuning of Perimeter X

PerimeterX helps to identify bad actor behavior and DDOS attempts and mitigate them at the edge of our network. We established our relationship in January, and throughout February have been monitoring and tuning our configuration to better protect We've already managed to mitigate a persistent DDOS attack which has recurred every couple of months, and hopefully we can make more improvements to protect, and reduce the pager burden on our team.


As always, we’d like to say thanks to all the volunteers who work with us, and to the Drupal Association Supporters, who make it possible for us to work on these projects. In particular we want to thank:

If you would like to support our work as an individual or an organization, consider becoming a member of the Drupal Association.

Follow us on Twitter for regular updates: @drupal_org, @drupal_infra

Lullabot: Better SVG Sprite Re-use with Twig in Drupal 8

Wed, 03/07/2018 - 20:57

There are many advantages to using SVG for the icons on a website. If you’re not familiar with SVG (aka Scalable Vector Graphics) it is an open-standard image format that uses XML to describe graphical images. SVG is great for performance, flexibility, and they look great on high-resolution displays. Though SVG can be really useful, one downside to implementing them can be the effort it takes to reuse them across a Drupal theme in various .html.twig files. SVG markup can be complex due to the number of attributes and, in some cases, there can also be a lot of markup. This issue can in part be mitigated by using inline SVG with the <use> element coupled with SVG Sprites. The combination of these two is a great solution—but unfortunately only gets part of the way to ideal reusability as the markup is still verbose for a single icon, and the number of SVG attributes needed can be hard to remember.

Wouldn’t it be nice if there was a way not to repeat all this code over again wherever we wanted to use an SVG? In this article we outline a way to reuse icons across template files in a Drupal 8 theme by creating a Twig helper function to reuse the markup.

Implementing a custom Twig custom function is fairly accessible to those comfortable with writing custom Drupal themes, but because of the way Drupal separates functionality between what can be defined in a module and what can be defined in a theme, we will need to create a module to contain our custom Twig function. To get started, create a new directory inside <drupalroot>/modules/custom with the following files and directories inside it. For the sake of this article I named the module svgy and the source is available on GitHub.

svgy ├── src │ └── TwigExtension │ └── Svgy.php ├── └──

One important thing to note here is that we do not actually have a .module file in our directory. This is not required by Drupal, and the Twig extension which adds our function will be defined as a service and autoload the Svgy.php within the src/TwigExtension directory. Don’t worry if you are unfamiliar with defining custom services in Drupal 8, this article explains all you need to know to get going with what we need for our Twig function.

Now that the directory and file structure is in place, we first need to add the correct metadata to

name: svgy type: module description: Add a Twig function to make using inline SVG easier. core: 8.x

Next the necessary information needs to be added to The contents of this file tells Drupal to autoload the Svgy.php file in the src/TwigExtension directory:

services: svgy.twig.extension: class: Drupal\svgy\TwigExtension\Svgy tags: - { name: twig.extension }

Now that Svgy.php is going to be loaded, add the code for our Twig function into it:

<?php namespace Drupal\svgy\TwigExtension; class Svgy extends \Twig_Extension { /** * List the custom Twig fuctions. * * @return array */ public function getFunctions() { return [ // Defines a new 'icon' function. new \Twig_SimpleFunction('icon', array($this, 'getInlineSvg')), ]; } /** * Get the name of the service listed in * * @return string */ public function getName() { return "svgy.twig.extension"; } /** * Callback for the icon() Twig function. * * @return array */ public static function getInlineSvg($name, $title) { return [ '#type' => 'inline_template', '#template' => '<span class="icon__wrapper"><svg class="icon icon--{{ name }}" role="img" title="{{ title }}" xmlns:xlink=""><use xlink:href="#{{ name }}"></use></svg></span>', '#context' => [ 'title' => $title, 'name' => $name, ], ]; } }

More information regarding defining custom Twig extensions is available here. For the purpose of this article the most important part to explain is the inlineSvgMarkup. This function takes two arguments:

  • $name = the unique #id of the icon
  • $title = a string used as the title of the element for better accessibility

When invoked, this function returns a render array with the #type as an inline_template. We use this template type for two reasons: Twig in Drupal 8 has built-in filtering for security reasons and using inline_template allows us to get around that more easily. Since this markup is fairly small and not going to be changed often, we don’t need to create an extra .html.twig file to contain our SVG code.

Implementing the Custom Function In A Theme

Now that the custom twig extension for our icon function is created, how is this implemented in a theme? An example of the following implemented in a theme can be found on GitHub.

The first thing you have to do is add an inline SVG to your theme. The icons inside this SVG should have unique ids. In most situations it is best to generate this SVG “sprite” using something like svgstore as part of your build process. But to keep today’s example simple, I’ve created a simple SVG with two icons and placed it in a theme at <themeroot>/images/icons.svg.

After the icons.svg is in place in the theme you can include it in in the rendered page with the following in the [<themename>.theme]( file:

function <theme_name>_preprocess_html(&$variables) { // Get the contents of the SVG sprite. $icons = file_get_contents(\Drupal::theme()->getActiveTheme()->getPath() . '/images/icons.svg'); // Add a new render array to page_bottom so the icons // get added to the page. $variables['page_bottom']['icons'] = array( '#type' => 'inline_template', '#template' => '<span class="hidden">' . $icons . '</span>', ); }

This appends a new render array to the page_bottom variable that is printed out inside Drupal core’s html.html.twig template file. If you haven’t overridden this template in your theme, the icons will get printed out automatically. If you have overridden html.html.twig in your theme—just make sure you are still printing page_bottom inside it: ({{ page_bottom }} is all you need.

The .hidden class that is used in the wrapping <span> here is one provided by Drupal 8 core. It applies display: none; to the element. This results in hiding it both visually and from screen readers. Since the individual icons from the SVG will be referenced elsewhere in the page this is the desired outcome.

In the example theme on GitHub, this results in the following output on the page:

<span class="hidden"><?xml version="1.0" encoding="UTF-8"?> <svg viewBox="0 0 130 117" version="1.1" xmlns="" xmlns:xlink=""> <polygon id="down-arrow" fill="#50E3C2" points="64.5 117 0 0 129 0"></polygon> <polygon id="up-arrow" fill="#50E3C2" points="65.5 0 130 117 1 117"></polygon> </svg></span>

Now that the SVG “sprite” is included on the page we can start referencing the individual icons within our SVG sprite with the custom Twig function. After you have cleared Drupal’s cache registry, icons can be added with this:

{{ icon('up-arrow', 'Navigate up') }}

The first argument passed into the function here, up-arrow, is used to reference an existing id in the SVG example included above. The second argument, Navigate up the page, is used as the title of the SVG to better describe contents of the element to users navigating with screen readers.

The resulting markup of this implementation of the icon() function when rendered in a page looks like:

<span class="icon__wrapper"> <svg class="icon icon--up-arrow" role="img" title="Navigate up" xmlns:xlink=""> <use xlink:href="#up-arrow"></use> </svg> </span>

As you can see, the resulting markup is much larger than just the single line where we used the icon() Twig function. This makes it a lot cleaner to reuse icons throughout our .html.twig files. In addition to making templates easier to read and not having to remember all the necessary <svg> attributes, we also ensure consistency in how icons get included throughout the project.

As Drupal themers, it is great to have flexibility like this provided by Twig. If you’re interested in digging deeper into other types of Twig extensions, I encourage you to checkout the Twig Tweak module, which has examples of other helpful Twig extensions.

Have more questions about SVG? Here are some other helpful articles

Frederic Marand: I'm going to Drupal Europe and so should you

Wed, 03/07/2018 - 18:25

So DrupalCon Europe is out for 2018. But that does not mean a EU-level event does not exist, to bind the community beyond the specialization of DevDays, FrontEnd United, CxO, GovDays, and all the DrupalCamps. Drupal Europe is that event, and to support the community who wants to prove a large Drupal conference can reasonably happen after all the trouble the Drupal Association had with it, the best way it to register for the conference.


So join me there :-)

read more

Promet Source: Promet Delivers Drupal Crash Course for Non-Developers at MidCamp 2018

Wed, 03/07/2018 - 18:09
It's that time of year again for our favorite Drupal camp - MidCamp Chicago!   Of all the camps in all the cities, this one is always our favorite - because of all the great Drupalists we get to meet but also because it's our hometown camp. 

Drupal Console: Drupal Console 1.7.0

Wed, 03/07/2018 - 17:15

Drupal Console 1.7.1 is out. The latest release contains several improvements to translations. Support for importing previously exported configuration after a site installation.

Chromatic: Decoupled Drupal – What You Need to Consider

Wed, 03/07/2018 - 15:00

“Decoupled Drupal” sounds cool and just about everyone else seems to be either doing it or talking about it, so it must be the best solution for you, right? Well, maybe. As with most things, the answer is more nuanced than one might think.

WeKnow: How to install Drupal 8 from an existing configuration

Wed, 03/07/2018 - 09:03
How to install Drupal 8 from an existing configuration

The Configuration Management (CM) system, is probably one of the most well known and exciting features of Drupal 8. But wouldn't be even more awesome to be able to install a site, export configuration and then re-install site from scratch importing the previously exported configuration?

For those who are not yet clear what we are talking about, this post is related to fixing the infamous exception error message when importing configuration:

"Site UUID in source storage does not match the target storage."

Why would you want to be able to install your site from an existing configuration?

Automate the creation of reproducible build/artifacts from scratch at any stage (Development, QA, Production) to test, launch or deploy your site.

jmolivas Wed, 03/07/2018 - 08:03

Acro Media: CiviCRM Installation in Drupal 8 & Commerce 2

Wed, 03/07/2018 - 01:46

It’s amazing how, just a couple of years ago, Drupal 8 was in its initial stages and was getting ready for its first stable release. But now, in 2018, we have stable releases and contributed module development on D8 has been progressing rapidly (as of this writing, we’re at nearly 5,000 D8 modules). CiviCRM, the best open source customer management tool, is one such community contributed integration that has been making such Drupal 8 strides.

When one of our clients needed their D7 CiviCRM ported over to their new D8 site, the prospects were not good: CiviCRM seemed to be lagging behind and the general online consensus was that things weren’t working, and boy are we glad the internet was wrong. The people behind the open-source content management framework has gotten its D8 version out and interestingly, most of it works beautifully. To our surprise (and relief!), the integration with the D8 site went incredibly well, of course, with a few kinks here and there. Those kinks are primarily due to the fact that Civi ships with its own Symfony version, which is a different version than what Drupal uses and that causes a conflict. For more information on it, read here.

We were able to do our Civi installation by following the steps mentioned in the wonderful blog by David Snopek. So, even though it was a bit complicated, it was definitely a learning experience for us and we thought we would share some of that knowledge here. In this blog we’ll be detailing how to install the richly packed Customer Relationship Management tool called CiviCRM on an existing Drupal 8 Commerce 2 ecommerce store. However, if you’d like to install a new Vanilla Drupal Commerce 8 site with CiviCRM already setup, we’d suggest you follow the easy-to-use instructions in Snopek’s blog.

Steps for Installing CiviCRM (Existing Installation)

In Drupal 8, installing CiviCRM is a bit more complicated than doing just a simple...

$ composer require drupal/civicrm you would for other modules.

If you are waiting for a more standardized policy/process for installing Civi, it might not be in the near future, as the maintainers feel that we already have a process that works and they want to spent their time concentrating on getting the actual module more stable and fixing functional bugs. They also need to come together and agree upon what is the best way to organize the code and do its build. So, the process to get it installed on a D8 installation requires quite a few steps, but if you do it right, it will work.

Below we’ve tried to slim down those steps and made it a bit more concise, so that, essentially, all you would need to do is copy and paste the commands and be on your way to using a fully integrated CiviCRM on top of Drupal 8. It took us a couple of attempts to get it right when we first installed it as some of the steps were in different places and missing a step would mean you had to start over.

Installing CiviCRM on an existing D8 site


  • Composer - not just the tool, but your Drupal 8 site should be using composer to manage dependencies.
  • Bower - another package management tool. Yes, we are rich with package managers in this process.
  • Git - Source control (managing the code that runs your site) is an essential part of every build, but particularly, it’s a requirement of Composer, because it uses that source control to lock down which packages you are building on.

Note: as CiviCRM Core hasn’t been added to Packagist yet, we’ll be using David Snopek’s fork.

  1. In your D8 installation, ensure that your "Vendor" directory is outside of your document root. So your directory structure would be like this:

    ├─ composer.json
    ├─ composer.lock
    ├─ vendor
    └─ web (your document root)

  2. Create a backup of your database.
  3. Go to our github repository, civicrm_d8_install_script, and copy the .env and files to your Drupal 8 project root directory.
  4. Run from your project root directory.
    bash ./civicrm_setup.shThis will download all the necessary modules and dependencies needed to install CiviCRM.
  5. Now, go to the "Extend" page (at /admin/modules) and install the CiviCRM module.
    1. This will create a civicrm.settings.php in your "/sites/default directory" which contains information about where the database is, etc.
    2. This will also create all the necessary tables in your Drupal database.
  6. Logout of Drupal and log back in again.
    1. Say what? Yeah, this is needed to sync your logged-in account with CiviCRM contacts.
  7. Get the Civi theme to apply by going to "/civicrm/admin/setting/url?reset=1" and set the CiviCRM Resource URL to "/vendor/civicrm/civicrm-core" and click “Save”.

  8. If everything worked correctly, you should see a CiviCRM link up at the top.

  9. Click on the CiviCRM link and you should be taken to a page like this:

  10. You can also confirm that the module is actually working by typing in the email of the user that you are logged in as, and it should bring up the contact in CiviCRM. This means that contacts are automatically being synced between Drupal and CiviCRM.

And that is it, you can throw your hands up in the air to celebrate, as you’ve just installed CiviCRM in a matter of minutes.

More from Acro Media Need a hand?

Would you like Quickbooks integrated into your Drupal Commerce website, but need a hand doing it? We've done it many times and would love to help.

Dries Buytaert: Cooking with Alexa and Drupal

Tue, 03/06/2018 - 19:51

When I'm home, one of the devices I use most frequently is the Amazon Echo. I use it to play music, check the weather, set timers, check traffic, and more. It's a gadget that is beginning to inform many of my daily habits.

Discovering how organizations can use a device like the Amazon Echo is big part of my professional life too. For the past two years, Acquia Labs has been helping customers take advantage of conversational interfaces, beacons and augmented reality to remove friction from user experiences. One of the most exciting examples of this was the development of Ask GeorgiaGov, an Alexa skill that enables Georgia state residents to use an Amazon Echo to easily interact with government agencies.

The demo video below shows another example. It features a shopper named Alex, who has just returned from Freshland Market (a fictional grocery store). After selecting a salmon recipe from Freshland Market's website, Alex has all the ingredients she needs to get started. Alex begins by asking Alexa how to make her preferred salmon recipe for eight people. The recipe on Freshland Market's Drupal website is for four people, so the Freshland Market Alexa skill automatically adjusts the number of ingredients needed to accommodate eight people. By simply asking Alexa a series of questions, Alex is able to preheat the oven, make ingredient substitutions and complete the recipe without ever looking at her phone or laptop. With Alexa, Alex is able to stay focused on the joy of cooking, instead of following a complex recipe.

This project was easy to implement because the team took advantage of the Alexa integration module, which allows Drupal to respond to Alexa skill requests. Originally created by Jakub Suchy (Acquia) and maintained by Chris Hamper (Acquia). The Alexa integration module enables Drupal to respond to custom voice commands, otherwise known as "skills".

Once an Amazon Echo user provides a verbal query, known as an "utterance", this vocal input is converted into a text-based request (the "intent") that is sent to the Freshland Market website (the "endpoint"). From there, a combination of custom code and the Alexa module for Drupal 8 responds to the Amazon Echo with the requested information.

Over the past year, it's been very exciting to see the Acquia Labs team build a connected customer journey using chatbots, augmented reality and now, voice assistance. It's a great example of how organizations can build cross-channel customer experiences that take place both online and offline, in store and at home, and across multiple touch points. While Freshland Market is a fictional store, any organization could begin creating these user experiences today.

Special thanks to Chris Hamper and Preston So for building the Freshland Market Alexa skill, and thank you to Ash Heath and Drew Robertson for producing the demo videos.