<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Nuvole</title><description>Nuvole is a software agency based in Italy specializing in Drupal development</description><link>https://www.nuvole.org/</link><item><title>Measure node completion in Drupal, part 2</title><link>https://www.nuvole.org/blog/measure-node-completion-drupal-part-2/</link><guid isPermaLink="true">https://www.nuvole.org/blog/measure-node-completion-drupal-part-2/</guid><pubDate>Thu, 08 Apr 2010 20:19:19 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This is the second part of the series on &lt;a href=&quot;http://drupal.org/project/content_complete&quot;&gt;Content Complete&lt;/a&gt;. If you have missed the introduction of the Content Complete module, please read the &lt;a href=&quot;https://nuvole.org/node/7/&quot;&gt;first part in this series&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this post, I’ll guide you to setting up views to show the completion of Drupal nodes using &lt;a href=&quot;http://drupal.org/project/views&quot;&gt;Views&lt;/a&gt;. If you don’t know how to work with Views, check out the documentation that is included within the module.&lt;/p&gt;
&lt;h2 id=&quot;a-list-view-on-node-completion&quot;&gt;A list view on node completion&lt;/h2&gt;
&lt;p&gt;Imagine we have 10 artist nodes filled in by different users on the website and we want to have a simple list of those nodes together with their completion status.&lt;/p&gt;
&lt;p&gt;Start by adding a new View of type ‘Node’ in your website. For a basic administration view, we add the post date and the title of the node to the ‘Fields’ and add a filter for nodes of content type ‘Album’. To show the completion, add the ‘Content Complete: Completeness’ field from the ‘Content Complete’ group. You can display the field as a Numeric Value or Bar and select the option to show a field that links to the next field to be completed. The module also provides a raw view of the data with ‘Content Complete: Completeness Data’ which will be discussed in the next paragraph. We select the Table style and make the completeness field sortable. Check out the result of this simple view in the second figure.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;views.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;Screen_shot_2010-04-12_at_18.04.53.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;list2.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;total-completion-of-a-list-of-nodes&quot;&gt;Total completion of a list of nodes&lt;/h2&gt;
&lt;p&gt;Another way to output data is to show the total completion on a list of selected nodes, e.g., on all the ‘Album’ nodes. For this we select the Views style ‘Content Complete’ and select the ‘Content Completeness Data’ as a field. The Style will process all the data it receives from the nodes and compute the total completeness and the next field to be completed. This is very useful if you want to show only one completion bar for all your content, as shown in the figure below.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;list2.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:1}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;
&lt;p&gt;In the &lt;a href=&quot;https://nuvole.org/node/11&quot;&gt;third and last part of this series&lt;/a&gt;, I’ll show you how to set up a user flow to drive users to complete their nodes by sending out email reminders.&lt;/p&gt;</content:encoded></item><item><title>Measure node completion in Drupal, part 3</title><link>https://www.nuvole.org/blog/measure-node-completion-drupal-part-3/</link><guid isPermaLink="true">https://www.nuvole.org/blog/measure-node-completion-drupal-part-3/</guid><pubDate>Mon, 12 Apr 2010 11:53:13 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This is the third part of the series on &lt;a href=&quot;http://drupal.org/project/content_complete&quot;&gt;Content Complete&lt;/a&gt;. If you have missed the introduction of the Content Complete module, please read the &lt;a href=&quot;https://nuvole.org/node/7&quot;&gt;first part in this series&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Thanks to the integration with the powerful &lt;a href=&quot;http://drupal.org/project/rules&quot;&gt;Rules&lt;/a&gt; module, you can set up complex workflows with data completion in a couple of minutes. I’ll show you how to set up email reminders sent automatically to authors of incomplete Album nodes (&amp;#x3C; 100%).&lt;/p&gt;
&lt;p&gt;If you’re not familiar with the Rules modules, please read this &lt;a href=&quot;http://drupal.org/node/298480&quot;&gt;introduction&lt;/a&gt; and check out &lt;a href=&quot;http://drupal.org/node/298482&quot;&gt;tutorials&lt;/a&gt; here.&lt;/p&gt;
&lt;h2 id=&quot;workflows-on-data-completion-using-rules&quot;&gt;Workflows on data completion using Rules&lt;/h2&gt;
&lt;p&gt;You need to download and install the &lt;a href=&quot;http://drupal.org/project/rules&quot;&gt;Rules&lt;/a&gt; module (Rules, Rules Administration UI, Rules Scheduler), the &lt;a href=&quot;http://drupal.org/project/views&quot;&gt;Views&lt;/a&gt; module and the &lt;a href=&quot;http://drupal.org/project/token&quot;&gt;Token&lt;/a&gt; Module.&lt;/p&gt;
&lt;p&gt;First, we will create a rule set that will continuously reschedule itself until the node is completed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to “admin/rules/rule_sets/add” and fill out the label with “CC Check (Set)” and the machine-readable name with “cc_check”.&lt;/li&gt;
&lt;li&gt;In the “Arguments” fieldset select “Content” and “User”.&lt;/li&gt;
&lt;li&gt;Back on the Rule Set page, click on the freshly created set and click “Add a new rule”. Set the label to “CC Check”.&lt;/li&gt;
&lt;li&gt;Every rule works with conditions (IF) and actions (THEN). Add the condition “Content Complete % is &gt;= than x” and fill out 100 under ”% to compare”. Click on “Negate” such that our condition translates to “if Content Complete % &amp;#x3C; 100”.&lt;/li&gt;
&lt;li&gt;Add the action “Send a mail to a user”. As recipient, select the content’s author. Fill out the subject with for example “Complete your node [node:title]” and the message with something like “Click on [node:site-url]/node/[node:nid]/edit to complete your node.” Check out the Token Replacement Patterns for more options.&lt;/li&gt;
&lt;li&gt;We need to reschedule our rule continuously. Add another action “Schedule ‘CC Check (Set)’”, give it an identifier “CC Check ‘[node:title]’” and as scheduled evaluation date put “+1 day”. Make sure you have cron configured to have email reminders sent out daily.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;Screen_shot_2010-04-12_at_13.33.09.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;We now have a rule set that checks the completion of the node and, if needed, sends an email reminder to the author of that node. Now we need to trigger that rule set on a Drupal event, for example, every time a node of type ‘Album’ is updated.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to “admin/rules/trigger/add” and fill out the label with “CC Check”. As a trigger event, we choose “After updating existing content”.&lt;/li&gt;
&lt;li&gt;Add the condition “Content has type” and select the “Album” content type.&lt;/li&gt;
&lt;li&gt;Add the action “CC Check (Set)” with Content set to “created content” and User set to “content’s author”.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;Screen_shot_2010-04-12_at_18.31.17.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Test your new workflow by writing an additional message to watchdog and setting the scheduled evaluation date to for example “+1 min”.
You can also enable the debugging mode of the Rules module: at the settings page click on “Debug rule evaluation”.&lt;/p&gt;
&lt;h2 id=&quot;future-of-content-complete-a-complete-api&quot;&gt;Future of Content Complete: a Complete API&lt;/h2&gt;
&lt;p&gt;Checking completion for content is just one approach to having your Drupal websites as “complete” as possible. We have already experimented with different ways of “completion”, e.g. by counting user actions as steps in processes that need completion. If you’re interested in for example having your users each add 3 nodes of type ‘Album’ and every node at least filled for 75%, check out the &lt;a href=&quot;http://drupal.org/project/complete&quot;&gt;Complete&lt;/a&gt; module. That module is related to the &lt;a href=&quot;http://drupal.org/project/content_complete&quot;&gt;Content Complete&lt;/a&gt; module, but measures completeness of &lt;em&gt;actions&lt;/em&gt; (as measured by the Rules module) instead of completeness of &lt;em&gt;nodes&lt;/em&gt;. A combination of both modules can lead to a “Complete API” and some preliminary ideas are under discussion &lt;a href=&quot;http://drupal.org/node/582112&quot;&gt;here&lt;/a&gt;. Looking forward to your feedback!&lt;/p&gt;</content:encoded></item><item><title>Towards a 100% database free development workflow</title><link>https://www.nuvole.org/blog/towards-100-database-free-development-workflow/</link><guid isPermaLink="true">https://www.nuvole.org/blog/towards-100-database-free-development-workflow/</guid><pubDate>Mon, 24 May 2010 09:01:07 GMT</pubDate><content:encoded>&lt;p&gt;Building a website using Drupal is essentially about tuning and extending its core functionalities to meet the project’s specific requirements. During its development phase we create content types with &lt;a href=&quot;http://drupal.org/project/cck&quot;&gt;CCK&lt;/a&gt;, display them using &lt;a href=&quot;http://drupal.org/project/views&quot;&gt;Views&lt;/a&gt; and extend the basic functionalities by setting up &lt;a href=&quot;http://drupal.org/project/modules&quot;&gt;contributed modules&lt;/a&gt;. All these actions are recorded into the database in the form of a very heterogeneous collection of settings, each specific to the module it belongs to.&lt;/p&gt;
&lt;p&gt;When the development phase is completed the database probably contains, besides all our valuable configuration, other Drupal “entities” such as content, users, etc… that we don’t really want to carry along once the website goes live: a careful and time consuming cleanup is then necessary.&lt;/p&gt;
&lt;p&gt;With time our website naturally grows in size; it contains much more content and several users are active on it: soon it might be necessary to extend its functionalities. How to do it when both settings and content are now valuable, when really nothing can be lost during development?&lt;/p&gt;
&lt;p&gt;The immediate solution could be to work on a &lt;a href=&quot;https://nuvole.org/blog/2010/apr/03/drupal-database-backup&quot;&gt;copy of the production database&lt;/a&gt; and restore it afterwards. This, obviously, means that activities on the production website have to be suspended until the new development is completed. Additionally, a pre-live clean-up will probably be necessary also in this case.&lt;/p&gt;
&lt;p&gt;When a team of developers works on the same project issues multiply: if each developer keeps and works on his own database dump the risk of overriding each other’s changes is high. Another option is to work all together the same shared database, but still hoping that nobody will mess things up.&lt;/p&gt;
&lt;p&gt;It is clear that relying on the database makes the development flow really error prone, not easily scalable and definitely difficult to maintain.&lt;/p&gt;
&lt;h2 id=&quot;settings-in-code-the-key-to-drupal-heaven&quot;&gt;Settings in code: the key to Drupal heaven&lt;/h2&gt;
&lt;p&gt;Keeping track of changes, working concurrently on the same project and resolving conflicts are all issues that are usually addressed by the adoption of a &lt;a href=&quot;http://en.wikipedia.org/wiki/Revision_control&quot;&gt;version control systems&lt;/a&gt; (VCS). Unfortunately they are not that useful when it comes to working with configuration stored in database: you can surely version a dump but change tracking and conflict solving are practically impossible.&lt;/p&gt;
&lt;p&gt;Everything changes if configuration is stored “in code”, meaning that, instead to have settings spread across different tables, they are translated to understandable PHP code that Drupal modules can easily consume. Exportable entities are not something new in the Drupal universe: modules like Views and CCK have already supported exporting setting for quite some time. What’s missing is to streamline the export/import operations to, eventually, switch from a database to a code driven development. All this is today possible thanks to the &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt; project. This project introduces the concept of &lt;a href=&quot;https://community.openatrium.com/documentation-en/node/1943&quot;&gt;feature&lt;/a&gt; as a collection of settings and configuration that describe a very well defined functionality. In doing so it implements a very solid system of keeping track of changes that occurs to settings “owned” by a particular feature making easy to dump them to code. That’s basically it, the rest is all about wisely organizing our project in features which keeps all our settings safely in code. At this point, by using our favorite VCS, changes get traceable and conflicts resolvable, exactly how it happens with standard PHP code.&lt;/p&gt;
&lt;h2 id=&quot;introducing-codepower&quot;&gt;Introducing #codepower&lt;/h2&gt;
&lt;p&gt;Projects like &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt;, &lt;a href=&quot;http://drupal.org/project/ctools&quot;&gt;Chaos tool suite&lt;/a&gt; or &lt;a href=&quot;http://drupal.org/project/strongarm&quot;&gt;Strongarm&lt;/a&gt; are at the heart of the code driven revolution, even though a lot still depends on how actually we are going to use this technology: good code conventions, well defined features boundaries and other good practices can really make the difference, at the end of the day.&lt;/p&gt;
&lt;p&gt;What we really need is to share each other’s best practices to develop together patterns for a solid code driven development. For this reason &lt;a href=&quot;https://nuvole.org&quot;&gt;Nuvole&lt;/a&gt; introduced, at &lt;a href=&quot;http://volcon.drupal.be&quot;&gt;Drupal VolCon&lt;/a&gt; in Antwerp, the &lt;a href=&quot;http://twitter.com/search?q=%23codepower&quot;&gt;#codepower&lt;/a&gt; Twitter tag. The idea is simple: we will tweet and tag with &lt;a href=&quot;http://twitter.com/search?q=%23codepower&quot;&gt;#codepower&lt;/a&gt; whatever we deem interesting to be shared with the community regarding code driven development. We ask you to do the same.&lt;/p&gt;
&lt;h2 id=&quot;the-movement&quot;&gt;The Movement&lt;/h2&gt;
&lt;p&gt;Today we all have the chance to push Drupal even further by changing the way we work and develop applications with it. At &lt;a href=&quot;https://nuvole.org&quot;&gt;Nuvole&lt;/a&gt; the adoption of a code driven development has completely revolutionized our working flow, making it more maintainable, solid and scalable. For this reason we want to contribute back to the community our experience about code driven development with a series of blog posts, starting in occasion of the first event on the matter: &lt;a href=&quot;http://krimson.be/articles/2010/05/21/developer-session-code-driven-development&quot;&gt;“Developer Session: Code Driven Development”&lt;/a&gt;, the 27th of May 2010, in Antwerp (Belgium) hosted by the great guys at &lt;a href=&quot;http://krimson.be/&quot;&gt;Krimson&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>First Code Driven Development meet-up in Antwerp</title><link>https://www.nuvole.org/blog/first-code-driven-development-meet-antwerp/</link><guid isPermaLink="true">https://www.nuvole.org/blog/first-code-driven-development-meet-antwerp/</guid><pubDate>Fri, 28 May 2010 10:43:02 GMT</pubDate><content:encoded>&lt;p&gt;Yesterday night the first “Code Driven Development” meet-up took place at the &lt;a href=&quot;http://www.krimon.be&quot;&gt;Krimson&lt;/a&gt; office in Antwerp (Belgium). Around 30 people were attending the meeting, most of them to learn more about this new “hype”. &lt;a href=&quot;https://nuvole.org&quot;&gt;Nuvole&lt;/a&gt; shared its experience in the field presenting “First Steps in Code Driven Development”, both an introduction and a collection of tips and examples for a quick start.&lt;/p&gt;
&lt;div&gt;&lt;div id=&quot;__ss_4341652&quot;&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For more read our &lt;a href=&quot;https://nuvole.org/blog/2010/may/24/towards-a-100-database-free-development-workflow&quot;&gt;introductory blog post&lt;/a&gt; on Code Driven Development.&lt;/p&gt;
&lt;p&gt;Straight after the presentation people were participating in an Open Space about the topic.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;106816172.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;(Picture by &lt;a href=&quot;http://twitpic.com/photos/netsensei&quot;&gt;netsensei&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nuvole.org&quot;&gt;Nuvole&lt;/a&gt; chose the following two sessions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;S.O.S. Features&lt;/strong&gt;: beginner session about features and code driven development where we had the chance to answer people’s more specific questions about the flow showcasing real-life examples from &lt;a href=&quot;https://nuvole.org&quot;&gt;Nuvole&lt;/a&gt;’s projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base Features&lt;/strong&gt;: session focused on features re-usability and followed by a brainstorming about how features should be designed in order to be more extendible and re-usable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then time was up and I had a train to catch. Thanks everybody for the great inspiring evening and, especially, to all the &lt;a href=&quot;http://www.krimson.be&quot;&gt;Krimson&lt;/a&gt; folks for putting this together.&lt;/p&gt;</content:encoded></item><item><title>Code driven development session proposal at DrupalCon Copenhagen 2010</title><link>https://www.nuvole.org/blog/code-driven-development-session-proposal-drupalcon-copenhagen-2010/</link><guid isPermaLink="true">https://www.nuvole.org/blog/code-driven-development-session-proposal-drupalcon-copenhagen-2010/</guid><pubDate>Sun, 11 Jul 2010 08:08:37 GMT</pubDate><content:encoded>&lt;p&gt;At Nuvole the adoption of a code driven development has completely revolutionized our working flow making it more maintainable, solid and scalable. Projects like &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt; and &lt;a href=&quot;http://drupal.org/project/ctools&quot;&gt;Chaos tool suite&lt;/a&gt; are at the heart of it, even though a lot depends still on how actually you are going to use them: good code conventions (see the &lt;a href=&quot;http://drupal.org/project/kit&quot;&gt;Kit&lt;/a&gt; project), well defined features boundaries and other good practices can really make the difference at the end of the day.&lt;/p&gt;
&lt;p&gt;Therefore, Nuvole is proposing the session &lt;a href=&quot;http://bit.ly/9cXKea&quot;&gt;“Code Driven Development: a 100% DB-free development workflow”&lt;/a&gt; at DrupalCon Copenhagen 2010 with the aim of sharing good design decisions, code snippets and anything else related to a pure 100% database free development. More specifically, the session will cover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;database driven vs. code driven approach in a nutshell&lt;/li&gt;
&lt;li&gt;what code driven development is all about: &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt;, &lt;a href=&quot;http://drupal.org/project/strongarm&quot;&gt;Strongarm&lt;/a&gt;, &lt;a href=&quot;http://drupal.org/project/drush&quot;&gt;Drush&lt;/a&gt;, exportables…&lt;/li&gt;
&lt;li&gt;coding conventions: make the world a better place&lt;/li&gt;
&lt;li&gt;your friends hook_install() and hook_update_N()&lt;/li&gt;
&lt;li&gt;the controller feature: one feature to rule them all&lt;/li&gt;
&lt;li&gt;lots of tips &amp;#x26; tricks to take back home&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are interested in the topic help this session going through the selection process by &lt;a href=&quot;http://bit.ly/9cXKea&quot;&gt;voting for it&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Measure node completion in Drupal, part 1</title><link>https://www.nuvole.org/blog/measure-node-completion-in-drupal-part-1/</link><guid isPermaLink="true">https://www.nuvole.org/blog/measure-node-completion-in-drupal-part-1/</guid><pubDate>Mon, 05 Apr 2010 16:44:31 GMT</pubDate><content:encoded>&lt;p&gt;Ever needed to measure the completeness of your Drupal nodes? Or wanted to motivate your users to complete their content until it reaches a 100%? In this post, I’ll outline the basic configuration of &lt;a href=&quot;http://drupal.org/project/content_complete&quot;&gt;Content Complete&lt;/a&gt;, a new Drupal module that does exactly that. &lt;a href=&quot;http://drupal.org/project/content_complete&quot;&gt;Content Complete&lt;/a&gt; started as a simple module to show completeness of nodes, but over time has included &lt;a href=&quot;http://drupal.org/project/rules&quot;&gt;Rules&lt;/a&gt; support to manage complex user flows, dynamic caching and &lt;a href=&quot;http://drupal.org/project/views&quot;&gt;Views&lt;/a&gt; integration. In this first post, I’ll guide you through the steps to configure the module, output the percentage of completeness and show you how to theme the percentage bar. In the second post of this series, I’ll show you how to output all this data using &lt;a href=&quot;http://drupal.org/project/views&quot;&gt;Views&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To start, you need to download and install the module from the &lt;a href=&quot;http://drupal.org/project/content_complete&quot;&gt;download page&lt;/a&gt; or directly via &lt;a href=&quot;http://drupal.org/project/drush&quot;&gt;Drush&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;configuring-the-module&quot;&gt;Configuring the module&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;Screen_shot_2010-04-05_at_17.40.17.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Completeness of nodes is measured per content type. For each content type, you need to select the fields you want to be included in the percentage. For example, you have a content type “Artist” with the fields “Title”, “Release Date”, “Label” and “Website”. You want all of those fields to be completed by your users, such that the node is at 100%. If, for example, a user only fills in a value for “Title” and “Website”, the completeness will be at 50%.&lt;/p&gt;
&lt;p&gt;To activate content completion checks, go to Administer &gt; Content Management &gt; Content types, and click ‘edit’ on the content type you wish to have checked. Enable the checks, save the content type and reload the page to find the fields you can check for completeness, as shown in Figure 1.&lt;/p&gt;
&lt;p&gt;If you are using the &lt;a href=&quot;http://drupal.org/project/content_profile&quot;&gt;Content Profile&lt;/a&gt; module to manage user profiles, the module will figure out the profile node that belongs to the logged in user.&lt;/p&gt;
&lt;h2 id=&quot;displaying-completeness&quot;&gt;Displaying completeness&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;Screen_shot_2010-04-05_at_18.32.52.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Content Complete provides various ways to display data like the percentage, a percentage bar and the next field to be completed. These are the different blocks provided by the module:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Content Complete: current node&lt;/code&gt;: show completeness for the current node. Can be shown only at the node page.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content Complete: Album (first node)&lt;/code&gt;: show completeness for the first node it finds of the specified content type and for which the logged in user has edit permissions. Can be shown at any page. Use this only if you have one node of that content type, for example, the associated profile node of that user.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/views&quot;&gt;Views&lt;/a&gt;. Will be reviewed in the next post of this series.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To enable a block, go to Administer &gt; Site building &gt; Blocks and drag and drop &lt;code&gt;Content Complete: current node&lt;/code&gt; to the region you want to display the block at. Then, navigate to your node to see the block appear (see Figure 2). You can also configure the block to be hidden when 100% is reached.&lt;/p&gt;
&lt;h2 id=&quot;who-gets-to-see-what-configuring-permissions&quot;&gt;Who gets to see what: configuring permissions&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;Screen_shot_2010-04-05_at_18.53.13.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The completeness block will be shown to every user including the anonymous user. Often, you will want to provide this block only to users who can edit the content. You can change the
permissions at Administer &gt; User management &gt; Permissions.&lt;/p&gt;
&lt;h2 id=&quot;theming&quot;&gt;Theming&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;Screen_shot_2010-04-05_at_19.06.11.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The module provides several CSS classes and ids for the themer. For example, you can use &lt;code&gt;&apos;.cck-complete-percent-bar-leq-25&apos;&lt;/code&gt; to style the appearance of the percent bar if the
percent is lower or equal (leq) to 25. Likewise, there are classes for &lt;code&gt;leq-50&lt;/code&gt;, &lt;code&gt;leq-75&lt;/code&gt; and &lt;code&gt;leq-100&lt;/code&gt;. Absolute numbers can be styled using &lt;code&gt;.cck-complete-percent-bar-x&lt;/code&gt; (with x replacing the actual percentage). Here is an example we used in one of our projects:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.content-complete-percent-bar-wrapper&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  background&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#333333&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  border&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; solid&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; #666666&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  padding&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  margin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;.5&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.content-complete-percent-bar&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  height&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  background-color&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;#8CC101&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;
&lt;p&gt;In the &lt;a href=&quot;https://nuvole.org/node/9&quot;&gt;second part of this series&lt;/a&gt;, I will show you how to use Views to set up different views on the completeness of your nodes.&lt;/p&gt;</content:encoded></item><item><title>Introducing OpenLayers Geocoder</title><link>https://www.nuvole.org/blog/introducing-openlayers-geocoder/</link><guid isPermaLink="true">https://www.nuvole.org/blog/introducing-openlayers-geocoder/</guid><pubDate>Wed, 07 Apr 2010 14:25:28 GMT</pubDate><content:encoded>&lt;p&gt;The &lt;a href=&quot;http://drupal.org/project/openlayers&quot;&gt;OpenLayers&lt;/a&gt; suite represents a breakthrough in the Drupal mapping solutions. The module allows to combine maps coming from different providers (Google Maps, Yahoo, and many others…) and, by using services like &lt;a href=&quot;http://cloudmade.com&quot;&gt;CloudMade&lt;/a&gt; or &lt;a href=&quot;http://mapbox.com/&quot;&gt;MapBox&lt;/a&gt;, it is finally possible to have maps perfectly integrated into the website look and feel. In order to input geospatial information using OpenLayers we have to enable the OpenLayers CCK module: the input widget provides a map where users can easily mark the desired location.&lt;/p&gt;
&lt;p&gt;Unfortunately, this approach does not scale to deal with more complex scenarios. Imagine a website where users need to geo-locate restaurants in different cities: the input process will result in a series of very tedious drag-and-zoom operations. Ideally, the only information the user would need to provide is the address or the name of the location he or she is looking for.&lt;/p&gt;
&lt;h2 id=&quot;google-geocoding-web-service&quot;&gt;Google Geocoding Web Service&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;http://code.google.com/apis/maps/documentation/geocoding/index.html&quot;&gt;Google Geocoding Web Service V3&lt;/a&gt; is a very powerful service that accepts full street addresses, location names and even known places (i.e. airports, train stations etc.), returning a complete set of information about the location we are looking for. For instance, Let’s have a closer look to the web service response for &lt;em&gt;Pizzeria da Vittorio, Rome&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;http://maps.google.com/maps/api/geocode/json?address=Pizzeria+Da+Vittorio,+Rome&amp;#x26;sensor=false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;results in:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;status&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;results&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;point_of_interest&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;establishment&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;formatted_address&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Pizzeria da Vittorio di Martino Enzo, Via Benedetto Croce, 123, 00142 Rome, Italy&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;address_components&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;long_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Pizzeria da Vittorio di Martino Enzo&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;short_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Pizzeria da Vittorio di Martino Enzo&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;point_of_interest&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;establishment&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;long_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;short_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;street_number&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;long_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Via Benedetto Croce&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;short_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Via Benedetto Croce&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;route&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;long_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Rome&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;short_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Rome&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;locality&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;political&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;long_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Rome&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;short_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;RM&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;administrative_area_level_2&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;political&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;long_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Lazio&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;short_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Lazio&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;administrative_area_level_1&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;political&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;long_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Italy&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;short_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;IT&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;country&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;political&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;long_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;00142&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;short_name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;00142&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;types&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;postal_code&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    } ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;geometry&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;location&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;lat&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;41.8428020&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;lng&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;12.4858480&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;location_type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;APPROXIMATE&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;viewport&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;southwest&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;          &quot;lat&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;41.8344890&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;          &quot;lng&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;12.4698406&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;northeast&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;          &quot;lat&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;41.8511139&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;          &quot;lng&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;12.5018554&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;partial_match&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  } ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the web service tells us pretty much everything we want to know about the location. It’s time to use all this awesomeness to make the user’s life shining again!&lt;/p&gt;
&lt;h2 id=&quot;meet-openlayers-geocoder&quot;&gt;Meet OpenLayers Geocoder&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://drupal.org/project/openlayers_geocoder&quot;&gt;OpenLayers Geocoder&lt;/a&gt; provides a new input widget for OpenLayers CCK fields that makes location spotting a fast and painless experience. All we need to do is to select the OpenLayers Geocoder input widget from within the CCK field setting page.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ol_settings.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;After enabling the OpenLayers Geocoder widget, adding a restaurant is all about providing its name or address: OpenLayers Geocoder will provide the user with a list of possible locations.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ol_searching.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;get-the-most-out-of-the-web-service-response&quot;&gt;Get the most out of the web service response&lt;/h2&gt;
&lt;p&gt;We have only used the geospatial information from the web service response so far: latitude and longitude to center the map and the boundary box to nicely fit the desired location. Besides that, the response contains really valuable information about the location we were looking for, like postal code, administrative area, city, country, etc.&lt;/p&gt;
&lt;p&gt;OpenLayers Geocoder gives the possibility to fill CCK text fields automatically on the node submission form with data coming from the response object. This is possible thanks to the integration with the &lt;a href=&quot;http://drupal.org/project/token&quot;&gt;Token&lt;/a&gt; module: all address parts are exposed and ready to be used as replacement patterns. To enable the autofilling we have to visit the OpenLayers CCK field setting page in order to map which token is going to fill which text field.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ol_mapping.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;When we will be looking for a place, information like city, country, etc. will be automatically fetched into the selected text fields.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ol_filling.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;This gives us a great flexibility: we can display our nodes by city using &lt;a href=&quot;http://drupal.org/project/views&quot;&gt;Views&lt;/a&gt; or search through them using &lt;a href=&quot;http://drupal.org/project/faceted_search&quot;&gt;Faceted Search&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;future-development-reverse-geocoding&quot;&gt;Future development: Reverse geocoding&lt;/h2&gt;
&lt;p&gt;Another very interesting feature of the Google Geocoding Web Service is the possibility to perform &lt;a href=&quot;http://code.google.com/apis/maps/documentation/geocoding/index.html#ReverseGeocoding&quot;&gt;reverse geocoding&lt;/a&gt;. As the name suggests, this time the user will spot a location on the map and the web service will return the closest address to the given point. OpenLayers Geocoder will have support for reverse geocoding in one its next releases.&lt;/p&gt;</content:encoded></item><item><title>Drupal database backup</title><link>https://www.nuvole.org/blog/drupal-database-backup/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-database-backup/</guid><pubDate>Sat, 03 Apr 2010 15:50:59 GMT</pubDate><content:encoded>&lt;p&gt;In order to backup a Drupal site you need to take care of both code and database. The first problem is usually solved by placing the Drupal directory trees under &lt;a href=&quot;http://en.wikipedia.org/wiki/Revision_control&quot;&gt;Revision Control&lt;/a&gt;. The second requires a more careful analysis, and this post describes optimal stategies for MySQL.&lt;/p&gt;
&lt;h2 id=&quot;before-you-start-common-caveats&quot;&gt;Before you start: common caveats&lt;/h2&gt;
&lt;h3 id=&quot;use-a-lock-file&quot;&gt;Use a lock file&lt;/h3&gt;
&lt;p&gt;While the backup of files is usually more predictable, the database dumps can take more time than you expect: il will depend on site traffic and activity too; the usual good practice of &lt;a href=&quot;http://en.wikipedia.org/wiki/File_locking#Lock_files&quot;&gt;using a lock file&lt;/a&gt; is a must in this case.&lt;/p&gt;
&lt;h3 id=&quot;keep-your-database-clean&quot;&gt;Keep your database clean&lt;/h3&gt;
&lt;p&gt;Drupal has &lt;a href=&quot;http://drupal.org/node/226728&quot;&gt;known problems&lt;/a&gt; in clearing some cache tables, that are left unaffected by the normal cache clearing in Drupal or &lt;a href=&quot;http://drupal.org/project/drush&quot;&gt;Drush&lt;/a&gt;. If you extensively use AJAX in forms, your cache tables can very quickly grow to hundreds of MBytes. Solve this by &lt;a href=&quot;http://acquia.com/node/88388&quot;&gt;clearing expired cache elements&lt;/a&gt; manually; for example, with Drush, just run&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; sql-query&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;DELETE FROM cache_form WHERE expire &amp;#x3C; UNIX_TIMESTAMP(NOW())&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;in your site directory tree to clean the DB.&lt;/p&gt;
&lt;h2 id=&quot;what-do-you-need-the-database-dump-for&quot;&gt;What do you need the database dump for?&lt;/h2&gt;
&lt;p&gt;The best database backup stategy depends on how you actually expect to use the dump.&lt;/p&gt;
&lt;h3 id=&quot;use-case-1-verbatim-copy-of-the-database&quot;&gt;Use case 1: Verbatim copy of the database&lt;/h3&gt;
&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; what you want ordinarily. Even if you do a good job of keeping your cache clear and deal manually with the tables that Drupal doesn’t clear, the database will still contain a lot of information that is useful only for forensic-like examinations, like session information and watchdog messages.&lt;/p&gt;
&lt;p&gt;If this is what you want, meet &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/mysqlhotcopy.html&quot;&gt;mysqlhotcopy&lt;/a&gt;, the most efficient way to perform a verbatim dump.&lt;/p&gt;
&lt;p&gt;Examples (assuming that everything happens on localhost, that your database username is &lt;code&gt;drupal&lt;/code&gt;, password is &lt;code&gt;xyz&lt;/code&gt;, database name is &lt;code&gt;dbname&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;mysqlhotcopy&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --user=drupal&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --password=xyz&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dbname&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will (re)create a database named &lt;code&gt;dbname_copy&lt;/code&gt; and populate it with an exact copy of the &lt;code&gt;dbname&lt;/code&gt; database. To restore the site database exactly like it was before the dump you merely need to edit &lt;code&gt;settings.php&lt;/code&gt; and replace &lt;code&gt;dbname&lt;/code&gt; with &lt;code&gt;dbname_copy&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;mysqlhotcopy&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --user=drupal&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --password=xyz&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dbname&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; directory&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;copies the full database contents into the specified &lt;code&gt;directory&lt;/code&gt;. This way you can store, by changing directory name, an arbitrary number of backups. Backups are restored by copying the files back to the MySQL database directory with &lt;code&gt;cp -r&lt;/code&gt; or &lt;code&gt;rsync -a&lt;/code&gt; (recommended, especially for remote use, since it will compress data and it won’t replace a file with an identical copy, thus giving the same result in much less time).&lt;/p&gt;
&lt;p&gt;Main problems with &lt;code&gt;mysqlhotcopy&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need suitable privileges on the filesystem: not applicable on shared hosting most of the times.&lt;/li&gt;
&lt;li&gt;The dump is not human-readable: you won’t be able to easily compare two dumps.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-case-2-copying-the-meaningful-tables-from-the-database&quot;&gt;Use case 2: Copying the meaningful tables from the database&lt;/h3&gt;
&lt;p&gt;If your backup is more for safety than for archival, then you will probably want to be more careful in selecting what to dump and how to dump it. In most cases the best solution is a selective SQL dump.&lt;/p&gt;
&lt;p&gt;Investigate at least the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/drush&quot;&gt;Drush&lt;/a&gt; (namely, &lt;code&gt;drush sql-dump&lt;/code&gt;) can be enough for you. You can tell to only dump meaningful tables with the &lt;code&gt;--structure-tables-key=structure-tables&lt;/code&gt; option (to use it, rename the file &lt;code&gt;example.drushrc.php&lt;/code&gt; to &lt;code&gt;drushrc.php&lt;/code&gt;), and you can enable slower but more usefully formatted dumps (one line per insert, to make the comparison of different dumps much easier) with &lt;code&gt;--ordered-dump&lt;/code&gt;. A typical dump command would thus look like&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; sql-dump&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --structure-tables-key=structure-tables&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --ordered-dump&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --result-file=dumpfile.sql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://drupal.org/project/backup_migrate&quot;&gt;Backup and migrate&lt;/a&gt; is an ordinary Drupal module, with very flexible backup options and scheduling. All of this is configured and used from within Drupal, so no command line hacking needed. Depending on your infrastructure, Backup and migrate will take advantage of existing server capabilities like FTP transfers and backup encryption.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Main problems with this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You still need command-line access for Drush and enough flexibility for Backup and Migrate; though, you will be able to use Backup and Migrate on most shared hosting solutions.&lt;/li&gt;
&lt;li&gt;The dump is much more computationally expensive than the “raw” dump of use case 1: you will pay for the tuning possibilities and the readable SQL output with significantly longer execution times.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-case-3-sharing-a-database-with-other-sites-or-developers&quot;&gt;Use case 3: Sharing a database with other sites or developers&lt;/h3&gt;
&lt;p&gt;Once upon a time, database dumps were the only way to transfer a site, complete with full configuration, to other developers or to develop a feature on a staging site a migrating it to production.&lt;/p&gt;
&lt;p&gt;Fortunately, this has changed in recent times: the &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt; module allows to export most configuration in code and enable it on a new site as easily as enabling an ordinary module. Take the time to investigate Features, or simpler dedicated modules like the built-in export functionalities in &lt;a href=&quot;http://drupal.org/project/cck&quot;&gt;CCK&lt;/a&gt; for  content type definitions, or &lt;a href=&quot;http://drupal.org/project/drush_views&quot;&gt;Drush Views&lt;/a&gt; to export and import views from database to code and vice versa; don’t rely on database dumps if at all possible.&lt;/p&gt;
&lt;p&gt;If you do want to share a database for this purpose, take a look at &lt;a href=&quot;http://drupal.org/project/dbscripts&quot;&gt;dbscripts&lt;/a&gt;; configuration is a bit tedious, but it will allow you to define what you dump in different scenarios and how to merge the dumped database with the “production” one.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This article is released under the Creative Commons &lt;a href=&quot;http://creativecommons.org/licenses/by-nc-nd/3.0/&quot;&gt;Attribution Noncommercial No Derivative Works 3.0&lt;/a&gt; license.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Development sandbox with Git and Drush</title><link>https://www.nuvole.org/blog/development-sandbox-git-and-drush/</link><guid isPermaLink="true">https://www.nuvole.org/blog/development-sandbox-git-and-drush/</guid><pubDate>Tue, 13 Apr 2010 08:19:44 GMT</pubDate><content:encoded>&lt;p&gt;I bet every Drupal developer keeps a Drupal installation as local sandbox where they try out code snippets, maintain modules or generate patches to be contributed back to the community. If not well maintained this installation will, day after day, grow into a chaotic mess of content and settings.  Having separate Drupal installations for each project we work on would be a good solution here.&lt;/p&gt;
&lt;h2 id=&quot;divide-et-impera-with-git&quot;&gt;”Divide et impera” with Git&lt;/h2&gt;
&lt;p&gt;Git is one of the rising stars in the version control system world and the main candidate in replacing the obsolete Drupal CVS infrastructure. After &lt;a href=&quot;http://book.git-scm.com/2_installing_git.html&quot;&gt;installing Git&lt;/a&gt;, setting up a repository is a piece of cake, in your project root type:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; init&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and there you go, you are now ready to commit, branch, merge or revert your sandbox code. Having your code under version control is generally a good practice: you can be sure that nothing will get really lost and, more important, you can branch your code according to what you are working on, to help keeping things in order. To create a branch using Git type:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; branch&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; my_module&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can list all your branches by typing &lt;code&gt;git branch&lt;/code&gt;, you’ll be working on &lt;code&gt;master&lt;/code&gt; branch by default:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; branch&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; my_module&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  my_module&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To actually move the development to that branch type:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; checkout&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; my_module&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After applying your changes you can commit to the branch and move back to &lt;code&gt;master&lt;/code&gt; to do something else. You can learn more about a normal Git workflow &lt;a href=&quot;http://book.git-scm.com/3_normal_workflow.html&quot;&gt;here&lt;/a&gt;, for more information about branching have a look at &lt;a href=&quot;http://book.git-scm.com/3_basic_branching_and_merging.html&quot;&gt;Basic Branching and Merging&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can branch your sandbox per module, appending version information if you wish, and everything will be safely separated. On my local sandbox the situation looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; branch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  context-6.x-3.0-beta4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  faceted_search-6.x-1.x-dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  feeds-6.x-1.x-dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  node_widget-6.x-1.x-dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  openlayers_geocoder-6.x-1.x-dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  openlayers_geocoder-6.x-2.x-dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  timeline-6.x-2.x-dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  unlimited_css-6.x-1.x-dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sandbox is divided per module and version: this division makes it easy to change the development focus quickly just by checking out different branches. Let’s say a new bug needs to be fixed in the OpenLayers Geocoder module, 2.x branch, we type:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; checkout&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; openlayers_geocoder-6.x-2.x-dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;a href=&quot;http://drupal.org/node/638988/cvs-instructions/DRUPAL-6--2&quot;&gt;CVS checkout&lt;/a&gt; of the module is available under &lt;code&gt;sites/all/modules/devel/openlayers_geocoder/&lt;/code&gt; (CVS metadata are also committed to Git). After fixing the bug we run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; commit&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -a&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Fixing a bug.&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and we are now ready to focus on something else. What shown so far is not going to work correctly for the very simple reason: code is not (&lt;a href=&quot;http://drupal.org/project/features&quot;&gt;yet&lt;/a&gt;) the king in Drupal, the database is. All settings and content you use to develop and test your code is, obviously, stored into the database, what we need to do is to &lt;a href=&quot;https://nuvole.org/node/5&quot;&gt;dump&lt;/a&gt; and commit the database:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; checkout&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; sql-dump&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --result-file=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;dump.sql&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dump.sql&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; commit&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -a&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Adding dump.&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This must be done in the &lt;code&gt;master&lt;/code&gt; branch &lt;strong&gt;before&lt;/strong&gt; branching, so we will always carry our dump along. After changing branch (e.g.: &lt;code&gt;git checkout my_module&lt;/code&gt;) we must remember to restore the dump in order to have our Drupal sandbox work correctly. It’s clear that the database restoring must happen after every checkout.&lt;/p&gt;
&lt;h2 id=&quot;automation-with-drush-and-git-hooks&quot;&gt;Automation with Drush and Git hooks&lt;/h2&gt;
&lt;p&gt;Git, as other version control systems, exposes a series of &lt;a href=&quot;http://book.git-scm.com/5_git_hooks.html&quot;&gt;hooks&lt;/a&gt;. Hooks are bash scripts that are ran in conjunction with a specific Git event. The main Git hooks here are located under &lt;code&gt;.git/hooks&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  applypatch-msg.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  commit-msg.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  post-checkout.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  post-commit.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  post-receive.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  post-update.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  pre-applypatch.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  pre-commit.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  pre-rebase.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  prepare-commit-msg.sample&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  update.sample&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we are interested in &lt;code&gt;post-checkout.sample&lt;/code&gt;: to activate a hook we just need to remove the &lt;code&gt;.sample&lt;/code&gt; extension. In order to automate our dump restore we paste into &lt;code&gt;post-checkout&lt;/code&gt; the following code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  #!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # $3 is equal to 1 in case of a branch switch or after git clone, 0 otherwise&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;$3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]; &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Restoring database.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; sql-cli&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; sites/all/database/development.sql&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Clearing cache.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cc&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; all&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Done.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  fi&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This bash script restores the database and clears the cache. We are now able to switch from one branch to another carrying along all our content, settings, etc.:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; checkout&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; node_widget-6.x-1.x-dev&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Checking&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; files:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 100%&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (933/933), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Switched&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; branch&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;node_widget-6.x-1.x-dev&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Restoring&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; database.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Clearing&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cache.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;&apos;all&apos;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cache&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; was&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cleared&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; checkout&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; openlayers_geocoder-6.x-2.x-dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Switched&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; branch&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;openlayers_geocoder-6.x-2.x-dev&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Restoring&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; database.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Clearing&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cache.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;&apos;all&apos;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cache&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; was&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cleared&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Done.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the other hand, dumping and committing the database &lt;strong&gt;before&lt;/strong&gt; switching branch is not automatic since we want to have control of what changes need to stay: it will happen that you just checkout a branch, make a quick test and you want to forget about it.&lt;/p&gt;
&lt;h2 id=&quot;getting-to-the-speed-of-light-git-bash-completion&quot;&gt;Getting to the speed of light: Git bash completion&lt;/h2&gt;
&lt;p&gt;If you want to attach name code version to your branch or other information you will end up having really long branch names. If you don’t want to type them all the time get to the speed of light with &lt;a href=&quot;http://www.simplicidade.org/notes/archives/2008/02/git_bash_comple.html&quot;&gt;Git bash completion&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Share changes with your team using Features and upgrade paths</title><link>https://www.nuvole.org/blog/share-changes-your-team-using-features-and-upgrade-paths/</link><guid isPermaLink="true">https://www.nuvole.org/blog/share-changes-your-team-using-features-and-upgrade-paths/</guid><pubDate>Mon, 05 Jul 2010 11:51:03 GMT</pubDate><content:encoded>&lt;p&gt;The &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt; module is great for keeping configuration in code, with just a few clicks you can bundle your settings together and be sure that changes will be tracked and safely dumped to code at the next feature update.&lt;/p&gt;
&lt;p&gt;However, often you will want to share other kinds of modifications with your team. Imagine you suddenly need to add a Taxonomy vocabulary or to enable a couple of modules your feature will be dependent on. Since your team’s workflow is completely 100% database free, you cannot pass those changes by simply sharing your database. Still, you need to push those changes to other developers, who already enabled the feature you have been modifying in their development environment. How do you do that?&lt;/p&gt;
&lt;h2 id=&quot;make-your-workflow-solid-meet-hook_update_n&quot;&gt;Make your workflow solid: meet hook_update_N()&lt;/h2&gt;
&lt;p&gt;Features are modules and modules can have their own upgrade path by implementing &lt;code&gt;[hook_update_N()](http://api.drupal.org/api/function/hook_update_N)&lt;/code&gt;. That’s exactly what you need.&lt;/p&gt;
&lt;p&gt;All the changes the Features module is not keeping track of &lt;strong&gt;must&lt;/strong&gt; be stored in sequential implementation of the &lt;code&gt;hook_update_N()&lt;/code&gt; to be sure that other developers will have them replicated in their database by simply visiting update.php.&lt;/p&gt;
&lt;p&gt;Have a look at the following example: we want to add a free-tagging taxonomy vocabulary to our feature. First of all we need to enable the Taxonomy module add it as dependency in its &lt;code&gt;.info&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dependencies[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;taxonomy&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding a dependency will not enable the new module on the other developers’ local copy, since the feature has already been enabled and dependencies will not be rechecked. To be sure the module will be enabled we have to write an update function:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Enabling Taxonomy module.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; feature_example_update_6001&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $return &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  drupal_install_modules&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;taxonomy&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $return[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;success&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; TRUE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;query&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Enabling Taxonomy module.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $return;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it’s time to create our free-tagging vocabulary. Since taxonomies are still not supported by the Features module we have to find another way to put this change in code. One way to do it is to create the vocabulary in an update function:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Create &quot;Tags&quot; vocabulary.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; feature_example_update_6002&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $return &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $vocab &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;name&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Tags&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;multiple&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;required&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;hierarchy&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;relations&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;weight&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;nodes&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;story&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;tags&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; TRUE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;help&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Enter tags related to your post.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  taxonomy_save_vocabulary&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($vocab);  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $return[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;success&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; TRUE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;query&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Create &quot;Tags&quot; vocabulary.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $return;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now commit our changes. Once the other developers pull the latest version of our feature in their local environment all they need to do is to run a Drupal update by visiting update.php.&lt;/p&gt;
&lt;p&gt;The comment on top of the update functions above plays an important role in the workflow since both Drush and update.php are actually able to display it when running &lt;code&gt;drush updatedb&lt;/code&gt; or running &lt;code&gt;update.php&lt;/code&gt;, giving valuable information to the other team members:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; updatedb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; following&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; updates&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pending:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; feature_example&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;               &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; 6001&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Enabling&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Taxonomy&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; module.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; 6002&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Create&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Tags&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; vocabulary.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Do&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; you&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; wish&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; all&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pending&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; updates?&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (y/n): &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;make-your-features-database-free-meet-hook_install&quot;&gt;Make your features database free: meet hook_install()&lt;/h2&gt;
&lt;p&gt;Storing your changes in &lt;code&gt;hook_update_N()&lt;/code&gt; will allow your team to be always up-to-date with the latest development of your feature, making your development workflow solid and maintainable. But what happens if a new developer wants to hop in? Our rule of thumb is “No database sharing”, hence the new developer needs to install our project from scratch and, still, we need to guarantee that he will get the complete latest status of the project. To do that we need to copy some of the configuration we have been placing in update functions to the &lt;code&gt;hook_install()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Implementation of hook_install()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; feature_example_install&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $vocab &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;name&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Tags&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;multiple&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;required&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;hierarchy&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;relations&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;weight&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;nodes&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;story&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;tags&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; TRUE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;help&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Enter tags related to your post.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  taxonomy_save_vocabulary&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($vocab);  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you might have noticed &lt;code&gt;hook_install()&lt;/code&gt; copies code from the &lt;code&gt;feature_example_update_6002()&lt;/code&gt; and not from
&lt;code&gt;feature_example_update_6001()&lt;/code&gt;. This is because the two updates have a different nature: &lt;code&gt;6002&lt;/code&gt; is a &lt;strong&gt;structural update&lt;/strong&gt;, meaning that it is something we must guarantee even if the feature will be installed from scratch; &lt;code&gt;6001&lt;/code&gt; is a &lt;strong&gt;development update&lt;/strong&gt; which only aims to upgrade an already working development copy.&lt;/p&gt;
&lt;p&gt;When writing your upgrade paths, it’s good practice to distinguish between two kinds of updates, and in the case of a &lt;strong&gt;structural update&lt;/strong&gt;, make sure you copy changes to the &lt;code&gt;hook_install()&lt;/code&gt; of your feature.&lt;/p&gt;
&lt;h2 id=&quot;real-life-examples&quot;&gt;Real life examples&lt;/h2&gt;
&lt;p&gt;Upgrade and install hooks are really powerful, you can put virtually anything in there. Below we list some real life example of upgrade paths we use in our projects at Nuvole.&lt;/p&gt;
&lt;h3 id=&quot;add-a-menu-and-menu-items&quot;&gt;Add a menu and menu items&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Implementation of hook_install()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; master_site_install&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Create a custom menu called &quot;Manage Content&quot;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  db_query&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;INSERT INTO&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; {menu_custom} (menu_name, title, &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;description&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;) &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            VALUES&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; (&apos;%s&apos;, &apos;%s&apos;, &apos;%s&apos;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;menu-content&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;Manage Content&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;Manage your site content.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Add &quot;Home&quot; menu item to Primary Links menu. &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $item[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;link_title&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Home&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $item[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;link_path&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $item[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;menu_name&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;primary-links&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $item[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;weight&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  menu_link_save&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($item);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;add-openid-to-admin-account&quot;&gt;Add OpenId to admin account&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Add Nuvole OpenID to admin account.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; nuvole_site_update_6004&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $return &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Delete any other association of the OpenId account to avoid conflicts.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $return[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; update_sql&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;DELETE&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; FROM&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; {authmap} &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                          WHERE&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; authname &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;http://nuvole.myopenid.com/&apos;&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Bind OpenId account to admin user.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $return[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; update_sql&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;INSERT INTO&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; {authmap} (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;uid&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;, authname, module) &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                          VALUES&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;, &apos;http://nuvole.myopenid.com/&apos;, &apos;openid&apos;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $return; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;upgrading-data-from-openlayers-1x-to-2x&quot;&gt;Upgrading data from OpenLayers 1.x to 2.x&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Upgrade OpenLayers 1.x to 2.x: WKT data in &quot;content_type_opera&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; publicopera_site_update_6007&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // OpenLayers 2.x stores WKT values as geometry collection.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Update data accordingly.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  db_query&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;UPDATE&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; {content_type_opera} &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;SET&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; field_opera_map_openlayers_wkt &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;            CONCAT&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;(&apos;GEOMETRYCOLLECTION(&apos;, field_opera_map_openlayers_wkt, &apos;)&apos;)&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;success&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; TRUE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;query&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;All WKT content updated.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Features based development workflow</title><link>https://www.nuvole.org/blog/features-based-development-workflow/</link><guid isPermaLink="true">https://www.nuvole.org/blog/features-based-development-workflow/</guid><pubDate>Tue, 24 Aug 2010 07:41:34 GMT</pubDate><content:encoded>&lt;p&gt;Nuvole is going to present and discuss its development workflow, entirely based on code and Features, at the DrupalCon Copenhagen 2010 in a few minutes!&lt;/p&gt;
&lt;p&gt;Please find it as a Slideshare embed below, or attached in PDF format.&lt;/p&gt;
&lt;p&gt;drupalcon2010.pdf (1.9 MB) &lt;a href=&quot;/sites/default/files/drupalcon2010.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>DrupalCon Chicago training: Use Features Effectively</title><link>https://www.nuvole.org/blog/drupalcon-chicago-training-use-features-effectively/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupalcon-chicago-training-use-features-effectively/</guid><pubDate>Wed, 03 Nov 2010 18:57:41 GMT</pubDate><content:encoded>&lt;p&gt;Nuvole was selected as a trainer for the DrupalCon Chicago 2011 &lt;a href=&quot;http://chicago2011.drupal.org/training&quot;&gt;pre-conference training&lt;/a&gt; day!&lt;/p&gt;
&lt;p&gt;In a full-day training aimed at Drupal developers we will show how to improve the Drupal development workflow, especially regarding traceability of changes, smooth upgrade of production sites and distributed team workflow, by using code-driven development techniques and Features.&lt;/p&gt;
&lt;p&gt;The program is a slightly condensed version of our &lt;a href=&quot;https://nuvole.org/page/code-driven-drupal-development&quot;&gt;Code-driven Drupal Development&lt;/a&gt; course, focused on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how to put the whole site configuration in code, even when a module does not offer a proper Features integration;&lt;/li&gt;
&lt;li&gt;the usage of a controller feature to manage site updates;&lt;/li&gt;
&lt;li&gt;the update process and the difference between structural and incremental updates;&lt;/li&gt;
&lt;li&gt;tips to deploy features faster by using Drush and an optimal shell configuration;&lt;/li&gt;
&lt;li&gt;how to write reusable features.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details see &lt;a href=&quot;http://bit.ly/cUJoPK&quot;&gt;the DrupalCon page about this training&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Typing less in Drush: aliases and autocompletion</title><link>https://www.nuvole.org/blog/typing-less-drush-aliases-and-autocompletion/</link><guid isPermaLink="true">https://www.nuvole.org/blog/typing-less-drush-aliases-and-autocompletion/</guid><pubDate>Tue, 09 Nov 2010 13:55:07 GMT</pubDate><content:encoded>&lt;p&gt;While &lt;a href=&quot;http://www.drupal.org/project/drush&quot;&gt;Drush&lt;/a&gt; is a big &lt;a href=&quot;http://developmentseed.org/blog/2009/jun/19/drush-more-beer-less-effort&quot;&gt;time saver&lt;/a&gt;, the textbook version of some commands can become lenghty at times:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pm-enable&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; faceted_search&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; features-revert&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nuvole_news&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An out-of-the-box &lt;a href=&quot;http://drupal.org/node/437568&quot;&gt;Bash completion for Drush&lt;/a&gt; is in the works, and the Drush shell (“&lt;code&gt;drush core-cli&lt;/code&gt;”) already offers fast access to commands and fancy features. However, if you prefer not to leave your shell and use a stock Drush, here are some tips for a faster Drush experience involving aliases and autocompletion.&lt;/p&gt;
&lt;h1 id=&quot;drush-and-bash-aliases&quot;&gt;Drush and Bash Aliases&lt;/h1&gt;
&lt;p&gt;In this section we’ll assume you use the &lt;a href=&quot;http://www.gnu.org/software/bash&quot;&gt;Bash&lt;/a&gt; shell, which may not be &lt;a href=&quot;https://wiki.ubuntu.com/DashAsBinSh&quot;&gt;the&lt;/a&gt; &lt;a href=&quot;http://release.debian.org/lenny/goals.txt&quot;&gt;default&lt;/a&gt; on your system; however, the techniques listed here can work, or be adapted to work, on other shells too.&lt;/p&gt;
&lt;h2 id=&quot;drush-aliases&quot;&gt;Drush aliases&lt;/h2&gt;
&lt;p&gt;Most Drush commands provide aliases, so it’s relatively rare that you have to type the full command. Just look them up in the &lt;a href=&quot;http://drush.ws&quot;&gt;Drush help page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, the short form of &lt;code&gt;pm-enable&lt;/code&gt; is just &lt;code&gt;en&lt;/code&gt;, while the short form of &lt;code&gt;pm-disable&lt;/code&gt; is just &lt;code&gt;dis&lt;/code&gt;. Typing&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pm-enable&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; faceted_search&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pm-disable&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; faceted_search&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;can thus be shortened to simply&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; en&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; faceted_search&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dis&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; faceted_search&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;bash-aliases-for-drush&quot;&gt;Bash aliases for Drush&lt;/h2&gt;
&lt;p&gt;You can configure Bash in order to define shortcuts for the most frequently used commands, including Drush constructs. This is done using the &lt;code&gt;alias&lt;/code&gt; builtin. Normally you’ll want to add the list of aliases to the &lt;code&gt;.bashrc&lt;/code&gt; file in your home directory (or/and to &lt;code&gt;.bash_profile&lt;/code&gt; or &lt;code&gt;.profile&lt;/code&gt;, depending on &lt;a href=&quot;http://stefaanlippens.net/bashrc_and_others&quot;&gt;your&lt;/a&gt; &lt;a href=&quot;http://www.joshstaiger.org/archives/2005/07/bash_profile_vs.html&quot;&gt;configuration&lt;/a&gt;; you might have to create it from scratch too).&lt;/p&gt;
&lt;p&gt;Here is a sample configuration for a &lt;a href=&quot;https://nuvole.org/node/16&quot;&gt;workflow based on Features and Drush&lt;/a&gt;; of course, adapt to taste.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Drupal and Drush aliases.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# To be added at the end of .bashrc.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drsp&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;cp sites/default/default.settings.php sites/default/settings.php&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drcc&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush cache-clear all&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drdb&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush updb &amp;#x26;&amp;#x26; drush cc all&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drdu&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush sql-dump --ordered-dump --result-file=dump.sql&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; dren&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush pm-enable&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drdis&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush pm-disable&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drf&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush features&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drfd&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush features-diff&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drfu&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush -y features-update&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drfr&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush -y features-revert&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drfra&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush -y features-revert all&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; dr&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;drush&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To be able to use the new aliases, you need to run &lt;code&gt;source .bashrc&lt;/code&gt;, or just logout and login again. Note that by typing &lt;code&gt;dr&lt;/code&gt; and then the &lt;code&gt;TAB&lt;/code&gt; key twice you will see all aliases.&lt;/p&gt;
&lt;h1 id=&quot;using-autocompletion&quot;&gt;Using autocompletion&lt;/h1&gt;
&lt;p&gt;The advanced &lt;a href=&quot;http://bash-completion.alioth.debian.org/&quot;&gt;Bash Completion&lt;/a&gt; is a tremendously efficient and addictive extra functionality for Bash: it extends the traditional Bash &lt;code&gt;TAB&lt;/code&gt; key completion in a smart way, so that by typing &lt;code&gt;ssh&lt;/code&gt; and then pressing &lt;code&gt;TAB&lt;/code&gt; you get a list of known hosts, and similarly &lt;code&gt;gunzip&lt;/code&gt; and then &lt;code&gt;TAB&lt;/code&gt; will show you the list of &lt;code&gt;.gz&lt;/code&gt; files only. We want to use this powerful functionality to autocomplete modules (or themes, easily extendable) and features names in Drush.&lt;/p&gt;
&lt;p&gt;We will first need a function that generates all project names. This could be done in a semantic way with &lt;code&gt;drush pm-list --pipe&lt;/code&gt;, but this would involve bootstrapping Drupal and it can at times be too slow when typing. We thus choose a syntactic approach and complete based on results of the &lt;code&gt;find&lt;/code&gt; utility, which is less smart since it cannot query the Drupal database and act accordingly, but is generally much faster.&lt;/p&gt;
&lt;p&gt;We assume we are in the Drupal root for the time being; then we will remove this limitation. A &lt;a href=&quot;http://www.debian-administration.org/articles/316&quot;&gt;textbook completion function&lt;/a&gt; is:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;_drupal_modules_in_dir&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  COMPREPLY&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;( $( &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;compgen&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -W&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;$( command find $1 -regex &quot;.*\.module&quot; -exec basename {} .module \; 2&gt; /dev/null )&apos;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $cur  ) )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;_drupal_modules&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; cur&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  COMPREPLY&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  cur&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;${COMP_WORDS[COMP_CWORD]}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # We temporarily assume we are in the Drupal root.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  _drupal_modules_in_dir&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;sites profiles modules&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To specify that this completion function is to be used to autocomplete the argument of, say, &lt;code&gt;dren&lt;/code&gt;, we will just have to specify (a full example will follow):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;complete&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -F&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; _drupal_modules&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dren&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let’s see how to find the Drupal root. As a byproduct we will be able to type &lt;code&gt;cdd views&lt;/code&gt; and go from anywhere in the Drupal root to the directory containing the Views module. Again, the smart way would involve a (slow) call to Drupal, so we use filesystem-based heuristics.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;_drupal_root&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Go up until we find index.php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  current_dir&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;pwd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  while&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ${current_dir} &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -a&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;current_dir&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -a&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          ! -f &quot;${current_dir}/index.php&quot; ] ; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    current_dir&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;dirname&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;current_dir&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$current_dir&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ] ; &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    exit&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$current_dir&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  fi&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These snippets can now be assembled to obtain a full solution, ready to be added to your &lt;code&gt;.bashrc&lt;/code&gt; file and able to handle &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt; and all aliases.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;#attachments&quot;&gt;full package of Drush-Bash tricks&lt;/a&gt; to add to your &lt;code&gt;.bashrc&lt;/code&gt; file can be found in &lt;a href=&quot;#attachments&quot;&gt;attachment&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But remember that everything should be adapted to your needs and configurations: for example, you might want to handle themes too, or use conventions to place features in a &lt;code&gt;features/&lt;/code&gt; subdirectory and thus improve performance, or add support for site aliases…&lt;/p&gt;
&lt;p&gt;For a system-wide and cleaner implementation, you can copy the second part of the &lt;a href=&quot;#attachments&quot;&gt;Drush-Bash tricks file&lt;/a&gt; to a file named &lt;code&gt;/etc/bash_completion.d/drush_custom&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&quot;examples&quot;&gt;Examples&lt;/h1&gt;
&lt;p&gt;Time to play with our new functions! Here are a few use cases.&lt;/p&gt;
&lt;p&gt;Change to the Drupal root from anywhere within the Drupal directory tree:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cdd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Autocomplete a module name (example: “fac” for “faceted_search”) and change into its directory:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cdd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; fac&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Then press TAB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cdd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; faceted_search&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Autocomplete a module name (example: “fac” for “faceted_search”) and enable it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dren&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; fac&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Then press TAB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dren&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; faceted_search&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Autocomplete a feature name and revert it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drfr&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Then press TAB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drfr&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nuvole_&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nuvole_cases&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      nuvole_site&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       nuvole_solutions&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From anywhere within the Drupal tree, go back to the Drupal root, run &lt;code&gt;svn update&lt;/code&gt; and come back again to the previous working directory:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (cdd &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;svn&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; up&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;The techniques explained in this article are part of the Nuvole &lt;a href=&quot;https://nuvole.org/trainings&quot;&gt;Code-Driven Drupal Development course&lt;/a&gt; and will be covered in deeper detail in the DrupalCon Chicago 2011 training by Nuvole: &lt;a href=&quot;http://chicago2011.drupal.org/training/code-driven-development-use-features-effectively&quot;&gt;Code-driven Development: Use Features Effectively&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Advanced Open Atrium customization</title><link>https://www.nuvole.org/blog/advanced-open-atrium-customization/</link><guid isPermaLink="true">https://www.nuvole.org/blog/advanced-open-atrium-customization/</guid><pubDate>Fri, 17 Dec 2010 16:49:39 GMT</pubDate><content:encoded>&lt;p&gt;Open Atrium comes, out of the box, with a rather complete set of features which allow to bootstrap a functional intranet solution in a few minutes. Still, to be actually usable in a real life context, an Open Atrium installation must be adapted to each organization’s needs and structure.&lt;/p&gt;
&lt;p&gt;In this article we will learn how to customize an Open Atrium installation in a code-driven fashion, without hacking its core and making sure that an upgrade to a newer version will not swipe away our valuable changes; we will cover:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How to add custom user profile fields;&lt;/li&gt;
&lt;li&gt;How to override Atrium’s core configuration;&lt;/li&gt;
&lt;li&gt;How to create custom group types.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For a smooth reading you should be familiar with Features and Exportables concepts. If you are not, just check out our Code Drive Development blog post series.&lt;/p&gt;
&lt;h2 id=&quot;1-how-to-add-custom-user-profile-fields&quot;&gt;1. How to add custom user profile fields&lt;/h2&gt;
&lt;p&gt;One of the greatest changes introduced with the Features module 1.0 stable release is that, when exporting, you can actually decouple CCK fields definition from the node type they belong to, or in other words: CCK fields and node type can be exported to different features.&lt;/p&gt;
&lt;p&gt;As a direct consequence of this approach we can store our custom user profile fields in a feature (call it &lt;code&gt;custom_profile&lt;/code&gt;) that extends the Atrium’s core &lt;code&gt;atrium_profile&lt;/code&gt;. Let’s have a look to its .info file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;core &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;6.x&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dependencies[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;features&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dependencies[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;text&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;description &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Custom profile feature.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[content][] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;profile-field_profile_city&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[content][] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;profile-field_profile_country&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Custom Profile&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Features&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;project &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;custom_profile&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;version &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;6.x-1.0&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes sure that, when Open Atrium will be upgraded, we will still find our custom fields (namely “City” and “Country”) since they belong to the &lt;code&gt;custom_profile&lt;/code&gt; feature.&lt;/p&gt;
&lt;h2 id=&quot;2-how-to-override-atriums-core-configuration&quot;&gt;2. How to override Atrium’s core configuration&lt;/h2&gt;
&lt;p&gt;The Features module exposes several hooks that alter the default settings of exported components, like variables, permissions, contexts, spaces, etc… A correct use of these hooks makes it possible to safely override default component settings being sure that they will not get lost after an Atrium core upgrade.&lt;/p&gt;
&lt;p&gt;Let’s say we want to change the system’s default user picture: Open Atrium stores it into a variable, so all we need to do is to implement &lt;code&gt;hook_strongarm_alter()&lt;/code&gt; and override its value:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Implementation of hook_strongarm_alter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; custom_profile_strongarm_alter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$items) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isset&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($items[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;user_picture_default&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;])) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $items[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;user_picture_default&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;value &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;sites/all/themes/custom/user.png&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After clearing the cache we will see our new user picture appearing everywhere on the site. Same goes for the default blocks we have on the dashboard of a newly created group: we can choose, for example, to replace the “Welcome” video with the “Latest discussion” block. Here is how to do that:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Implementation of hook_spaces_presets_alter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; custom_group_spaces_presets_alter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$items) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Store a reference to our target block section.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $blocks &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$items[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;atrium_groups_private&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;value[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;context&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;spaces_dashboard-custom-1:reaction:block&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;blocks&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Remove &quot;Welcome&quot; block.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  unset&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;atrium-welcome_member&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Add &quot;Latest discussions&quot; block.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;views-blog_listing-block_1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;module&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;views&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;delta&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;blog_listing-block_2&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;region&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;content&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;weight&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After clearing the cache all new groups (and those that didn’t have their dashboard customized by an user) will inherit the new settings. If we now have a look at our features status we’ll see that several Atrium core features have been marked as “Overridden”:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; $&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; features&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Name&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                 Feature&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;              Status&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    State&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;               atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;               Enabled&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   Overridden&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Activity&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      atrium_activity&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      Enabled&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Blog&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          atrium_blog&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          Enabled&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Notebook&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      atrium_book&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          Enabled&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Calendar&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      atrium_calendar&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      Enabled&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Case&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Tracker&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  atrium_casetracker&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   Enabled&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Groups&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        atrium_groups&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        Enabled&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   Overridden&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Members&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       atrium_members&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       Enabled&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   Overridden&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; News&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          atrium_news&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          Enabled&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Pages&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;         atrium_pages&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;         Enabled&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Atrium&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Profile&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       atrium_profile&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       Enabled&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a totally safe situation since your configuration has been altered via code, thus it is 100% upgrade-safe. To learn more about altering default hook have a look at Feature’s &lt;a href=&quot;http://bit.ly/ejUUOQ&quot;&gt;feature.api.php&lt;/a&gt; file.&lt;/p&gt;
&lt;h2 id=&quot;3-how-to-create-custom-group-types&quot;&gt;3. How to create custom group types&lt;/h2&gt;
&lt;p&gt;In the example above we learnt how to replace a block in the default “Private group” space preset. What if we need a completely new group type, with custom features and specific configurations, something that we cannot really map into one of the two group types (“Public group” and “Private group”) provided by Open Atrium? Easy, we just need to provide a custom space preset that describes the kind of group we need.&lt;/p&gt;
&lt;p&gt;For example: we want to have a “Portal group” which will be used as the public website of our organization, it will have news and static pages and it must look slightly different than the intranet.  The Spaces module is powerful enough to allow you to turn each group into a completely different website, customizing settings, menus, theme, etc…&lt;/p&gt;
&lt;p&gt;To do that, we start by cloning the “Controlled group” space preset into our new “Portal group” preset. We then export it to a new feature called &lt;code&gt;atrium_portal&lt;/code&gt;, having an &lt;code&gt;.info&lt;/code&gt; file similar to:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;core&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;6.x&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dependencies[] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;atrium_news&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dependencies[] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;atrium_pages&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dependencies[] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;context&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dependencies[] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;menu&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dependencies[] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;spaces&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[context][] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;layout_portal&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[ctools][] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;context:context:3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[ctools][] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;spaces:ctools:3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[menu_custom][] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;menu-portal&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[menu_links][] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;menu-portal:calendar&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[menu_links][] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;menu-portal:dashboard&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;features[spaces_presets][] = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;atrium_portal&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;atrium_portal&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Features&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you might have noticed, the file contains a dependence on two features that are not part of the Open Atrium core: &lt;code&gt;atrium_news&lt;/code&gt; and &lt;code&gt;atrium_pages&lt;/code&gt;, which respectively add news publishing and usual static pages to our new portal.&lt;/p&gt;
&lt;p&gt;So let’s have now a closer look at the heart of this feature, the &lt;code&gt;atrium_portal&lt;/code&gt; Spaces preset;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Implementation of hook_spaces_presets().&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_portal_spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $export &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; stdClass&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;disabled &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; FALSE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;api_version &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;atrium_portal&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;title &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Portal group&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;description &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Turn a group into a public portal. All users may view public content from this group. Users must request to join this group.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;space_type &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;og&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;value &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;variable&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_og_selective&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_og_register&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_og_directory&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_og_private&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_features&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_blog&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_book&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_calendar&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_casetracker&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_members&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_news&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_pages&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_shoutbox&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;spaces_dashboard&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_setting_home&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;dashboard&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;site_frontpage&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;dashboard&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;menu_primary_links_source&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;menu-portal&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;menu_default_node_menu&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;menu-portal&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;designkit_color&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;background&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;#3399aa&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;designkit_image&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;logo&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;context&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_dashboard-custom-1:reaction:block&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;blocks&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          &apos;views-atrium_news-block_3&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;module&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;views&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;delta&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;atrium_news-block_3&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;region&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;content&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;weight&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          &apos;views-calendar_listing-block_1&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;module&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;views&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;delta&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;calendar_listing-block_1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;region&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;right&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;            &apos;weight&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_dashboard-custom-2:reaction:block&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;blocks&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_dashboard-custom-3:reaction:block&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;blocks&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_dashboard-custom-4:reaction:block&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;blocks&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_dashboard-custom-5:reaction:block&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;blocks&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Translatables&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Included for use with string extractors like potx.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Portal group&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Turn a group into a public portal. All users may view public content from this group. Users must request to join this group.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $export[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;atrium_portal&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $spaces_presets;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $export;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first important customization is the set of features we are enabling for our new Portal preset. Since the set of features available to each group is stored into the &lt;code&gt;spaces_features&lt;/code&gt; variable the Spaces module can override its value when we are browsing a Portal group. Here what we have enabled:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;spaces_features&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;atrium_blog&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;atrium_book&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;atrium_calendar&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;atrium_casetracker&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;atrium_members&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;atrium_news&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;atrium_pages&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;atrium_shoutbox&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;0&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;spaces_dashboard&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is generally true with all those settings that are stored into variables, like default theme, primary links menu, default user picture, etc…&lt;/p&gt;
&lt;p&gt;With a space preset we can also override the default block settings of the Open Atrium dashbaord. For example, for our Portal we want to show a news block on the content region and a calendar with the latest events on the right sidebar region. Here is how to do that:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;context&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;spaces_dashboard-custom-1:reaction:block&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;blocks&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;views-atrium_news-block_3&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;module&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;views&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;delta&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;atrium_news-block_3&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;region&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;content&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;weight&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;views-calendar_listing-block_1&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;module&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;views&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;delta&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;calendar_listing-block_1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;region&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;right&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;weight&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this simple space preset we can actually turn an Open Atrium group into a real public portal for our organization.&lt;/p&gt;
&lt;h2 id=&quot;a-different-way-to-look-at-open-atrium&quot;&gt;A different way to look at Open Atrium&lt;/h2&gt;
&lt;p&gt;A stable code-driven customization joint to the powerful Spaces module could push Open Atrium much far behind its original private-intranet. This great distribution, in fact, can be seen as a versatile platform that can answer to the most diverse needs of an organization providing, for example, groups that can be turn into full featured public portals or used to organize events, etc… With the right customization each group could also be heavily customizable, letting the administrator to completely change their look and feel to serve the most different purposes.&lt;/p&gt;
&lt;p&gt;If you are interested in learning more about the topic covered in this blog post you can check out our Drupal Con Chicago 2011 session proposal &lt;a href=&quot;http://chicago2011.drupal.org/sessions/extending-and-customizing-open-atrium&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Open Atrium, Beyond the Intranet</title><link>https://www.nuvole.org/blog/open-atrium-beyond-intranet/</link><guid isPermaLink="true">https://www.nuvole.org/blog/open-atrium-beyond-intranet/</guid><pubDate>Mon, 07 Feb 2011 16:12:45 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;http://openatrium.com&quot;&gt;Open Atrium&lt;/a&gt; is designed to be a powerful Drupal-based Intranet solution, but the underlying technology allows to do much more: creating a &lt;strong&gt;public portal&lt;/strong&gt; with a totally different graphic design and complementing it with a private Intranet; creating other &lt;strong&gt;group presets&lt;/strong&gt; than the default “public” and “private” group; customizing the &lt;strong&gt;profile fields&lt;/strong&gt; in a clean and modular way, and several other possibilities.&lt;/p&gt;
&lt;p&gt;Here is the Nuvole presentation from the &lt;a href=&quot;http://bxl2011.drupaldays.org/node/309&quot;&gt;Drupal Dev Days&lt;/a&gt; in Brussels (Slideshare embed below, PDF attached to this post).&lt;/p&gt;
&lt;p&gt;If you are interested in discussing Open Atrium from a developer’s point of view at &lt;a href=&quot;http://chicago2011.drupal.org/&quot;&gt;DrupalCon Chicago&lt;/a&gt; next month, please &lt;a href=&quot;http://chicago2011.drupal.org/forum/bof-open-atrium-developers&quot;&gt;join the BOF&lt;/a&gt;.&lt;/p&gt;
&lt;iframe src=&quot;https://www.slideshare.net/slideshow/embed_code/key/3rxJeQT136B4jI?startSlide=1&quot; width=&quot;597&quot; height=&quot;486&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border: var(--border-1) solid #CCC; border-width:1px; margin-bottom:5px;max-width: 100%;&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;drupaldevdays_oa.pdf (2.83 MB) &lt;a href=&quot;/sites/default/files/drupaldevdays_oa.pdf&quot;&gt;Download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Drupal Solutions for Public Works Monitoring and Remote Collaboration</title><link>https://www.nuvole.org/blog/drupal-solutions-public-works-monitoring-and-remote-collaboration/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-solutions-public-works-monitoring-and-remote-collaboration/</guid><pubDate>Tue, 12 Apr 2011 16:21:36 GMT</pubDate><content:encoded>&lt;p&gt;The &lt;a href=&quot;http://www.drupalgovdays.org/&quot;&gt;Drupal Government Days&lt;/a&gt; held last week in Brussels were a good occasion to showcase two solutions by Nuvole, born as services for local and European institutions.&lt;/p&gt;
&lt;h2 id=&quot;public-works-monitoring-in-drupal&quot;&gt;Public Works Monitoring in Drupal&lt;/h2&gt;
&lt;p&gt;Nuvole built a dedicated platform, based on the widely used Drupal system, to make it easy for municipalities to monitor public works in real time and optionally share selected information with citizens.&lt;/p&gt;
&lt;p&gt;Please find the Slideshare embed below.&lt;/p&gt;
&lt;iframe src=&quot;https://www.slideshare.net/slideshow/embed_code/key/EujSSz5v4WuwiL?startSlide=1&quot; width=&quot;597&quot; height=&quot;486&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border: var(--border-1) solid #CCC; border-width:1px; margin-bottom:5px;max-width: 100%;&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://www.slideshare.net/nuvoleweb/public-works-monitoring&quot;&gt;Public Works Monitoring&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;remote-collaboration-and-institutional-intranet&quot;&gt;Remote Collaboration and Institutional Intranet&lt;/h2&gt;
&lt;p&gt;Nuvole created a solution for the European Commission based on Drupal and Open Atrium to allow several hundreds experts to discuss in a Virtual Community about the harmonization of higher education in Europe. Besides the Virtual Community, several websites are created for the periodical seminars, all based on a common template and single sign-on.&lt;/p&gt;
&lt;p&gt;Please find the Slideshare embed below.&lt;/p&gt;
&lt;iframe src=&quot;https://www.slideshare.net/slideshow/embed_code/key/5gJJmfIqLnXCjr?startSlide=1&quot; width=&quot;597&quot; height=&quot;486&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border: var(--border-1) solid #CCC; border-width:1px; margin-bottom:5px;max-width: 100%;&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://www.slideshare.net/nuvoleweb/remote-collaboration-and-institutional-intranets-with-drupal-and-open-atrium-7565152&quot;&gt;Remote Collaboration and Institutional Intranets with Drupal and Open Atrium&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title>Five reasons code-driven development and Features are good for you</title><link>https://www.nuvole.org/blog/five-reasons-code-driven-development-and-features-are-good-you/</link><guid isPermaLink="true">https://www.nuvole.org/blog/five-reasons-code-driven-development-and-features-are-good-you/</guid><pubDate>Thu, 13 Jan 2011 15:18:40 GMT</pubDate><content:encoded>&lt;p&gt;With the DrupalCon Chicago 2011 registrations going at full speed (early bird deadline expires tomorrow, hurry up!) we are receiving questions from people who are considering to sign-up for the &lt;a href=&quot;http://chicago2011.drupal.org/training/code-driven-development-use-features-effectively&quot;&gt;Code-driven Development: Use Features Effectively&lt;/a&gt; pre-conference training by Nuvole.&lt;/p&gt;
&lt;p&gt;As a general answer, here are 5 benefits in adopting a code-driven development workflow. The topics below are discussed in our &lt;a href=&quot;https://nuvole.org/blog/code-driven-development&quot;&gt;relevant blog posts&lt;/a&gt; too and all will be covered at length, with practical examples, in our DrupalCon training.&lt;/p&gt;
&lt;h2 id=&quot;1-keeping-track-of-all-changes&quot;&gt;1: Keeping track of all changes&lt;/h2&gt;
&lt;p&gt;When did a certain bit of configuration (such as a variable, or the allowed values for a content type) change? Was it a side-effect of something else or an intended modification? When all your site configuration is in code instead of being buried in a huge SQL dump, and you couple it with a proper version management tool, comparing site revisions is easy and effective.&lt;/p&gt;
&lt;h2 id=&quot;2-working-in-a-distributed-team&quot;&gt;2: Working in a distributed team&lt;/h2&gt;
&lt;p&gt;Why can’t I work on setting up all content types and views for the Blog section of a website while someone else does the same for News? With no SQL dumps around, developers are free to experiment and restart from scratch without damaging each other’s work. And they can work in a modular way on the same project in parallel (thus speeding up the development pace), even from separate countries!&lt;/p&gt;
&lt;h2 id=&quot;3-reusing-components-and-settings&quot;&gt;3: Reusing components and settings&lt;/h2&gt;
&lt;p&gt;Isn’t this site feature you are developing so similar to what you did in a previous project? Or this complex and tedious module configuration exactly the same across all your projects, so that you have resorted to write down the steps and repeat them manually? With a code-driven approach, replicating settings is just a matter of reusing the same, modular, extensible, components, avoiding tedious and error-prone manual work.&lt;/p&gt;
&lt;h2 id=&quot;4-updating-production-sites-cleanly&quot;&gt;4: Updating production sites cleanly&lt;/h2&gt;
&lt;p&gt;What if a client asks for a complex modification on a production site? Do you need to take the site offline, dump the database for backup, repeat manually all the changes you had tested with an older dump and then finally put the site back online? Code-driven development allows you to use the same mechanism employed by the standard Drupal module updates, with virtually no downtime needed and a much cleaner log of changes.&lt;/p&gt;
&lt;h2 id=&quot;5-following-the-drupal-future&quot;&gt;5: Following the Drupal future&lt;/h2&gt;
&lt;p&gt;Worried that code-driven development could not be in the direction Drupal is taking? No need to worry! Some &lt;a href=&quot;http://developmentseed.org/blog/2010/sep/30/features-and-exportables-drupal-7&quot;&gt;patches&lt;/a&gt; by the good guys at &lt;a href=&quot;http://developmentseed.org/&quot;&gt;Development Seed&lt;/a&gt; made (or are making) their way into Drupal 7 and beyond, enabling a better support for a code-driven workflow out of the box. So code-driven development is not just cool, but future-proof too!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The DrupalCon training will be hands-on and will follow the model of &lt;a href=&quot;https://nuvole.org/page/code-driven-drupal-development&quot;&gt;our standard code-driven development training&lt;/a&gt;, with short presentations and practical assignments based on concrete examples, monitored by the Nuvole team.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Code-driven development is an approach that works in our case and addresses our needs; though, you may have different needs and thus find beneficial only &lt;a href=&quot;http://awebfactory.com.ar/node/458&quot;&gt;selected portions of the workflow&lt;/a&gt;, for example; this is fine too!&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Building and Maintaining Drupal Distributions</title><link>https://www.nuvole.org/blog/building-and-maintaining-drupal-distributions/</link><guid isPermaLink="true">https://www.nuvole.org/blog/building-and-maintaining-drupal-distributions/</guid><pubDate>Wed, 24 Aug 2011 12:07:01 GMT</pubDate><content:encoded>&lt;p&gt;Our DrupalCon London 2011 presentation &lt;em&gt;Building and Maintaining a Distribution in Drupal 7 with Features&lt;/em&gt; can be found as a Slideshare embed below; you can also find the final slides in PDF format on the &lt;a href=&quot;http://london2011.drupal.org/conference/sessions/building-and-maintaining-distribution-drupal-7-features&quot;&gt;session page on the DrupalCon site&lt;/a&gt; or attached to this post.&lt;/p&gt;
&lt;p&gt;For further resources, you can refer to the code-driven development posts in &lt;a href=&quot;https://nuvole.org/blog/code-driven-development&quot;&gt;the Nuvole blog&lt;/a&gt; or check out our &lt;a href=&quot;/trainings&quot;&gt;trainings&lt;/a&gt; (next: London in October). And for those who couldn’t get a printed copy of our code-driven development cheatsheet, a PDF version is &lt;a href=&quot;https://nuvole.org/blog/2011/mar/25/code-driven-development-cheatsheet&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;iframe src=&quot;https://www.slideshare.net/slideshow/embed_code/key/xwtBxigxsYDCnL?startSlide=1&quot; width=&quot;597&quot; height=&quot;486&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border: var(--border-1) solid #CCC; border-width:1px; margin-bottom:5px;max-width: 100%;&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;nuvole-drupalcon-london-2011.pdf (13.33 MB) &lt;a href=&quot;/sites/default/files/code-driven-development-cheatsheet-nuvole.pdf&quot;&gt;Download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Minisites in Open Atrium</title><link>https://www.nuvole.org/blog/minisites-open-atrium/</link><guid isPermaLink="true">https://www.nuvole.org/blog/minisites-open-atrium/</guid><pubDate>Mon, 28 Nov 2011 13:52:29 GMT</pubDate><content:encoded>&lt;p&gt;You can use a single &lt;a href=&quot;http://openatrium.com&quot;&gt;Open Atrium&lt;/a&gt; installation to create minisites, i.e., minimal websites with a simple structure, served at different domains, but sharing a centralized backend. Each minisite can be public or private and can have a specific theme and specific features. An anonymous visitor will only see a small, self-contained site, while site editors will be able to manage content on all minisites from the same backend.&lt;/p&gt;
&lt;p&gt;This is an ideal situation, for example, when an Open Atrium installation is used as a private Intranet but you want to build small auxiliary sites for conferences or events, and possibly take advantage of the fact that editors and registered users are the same as the main Intranet.&lt;/p&gt;
&lt;p&gt;And, best of all, this can be done with a minimal amount of coding and respecting the Open Atrium architecture.&lt;/p&gt;
&lt;h2 id=&quot;creating-and-customizing-a-minisite&quot;&gt;Creating and Customizing a Minisite&lt;/h2&gt;
&lt;p&gt;Our aim is to build a feature that will allow us to create and customize a minisite in just a few minutes. We want to start from the ordinary “Create Group” page:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;minisite-create.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;And this is what we get immediately upon form submission:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;minisite-new-1.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The minisite ships with default content (pages linked from the left side menu) and the editor can just click and add content.&lt;/p&gt;
&lt;p&gt;We can also hook our minisite to the built-in look-and-feel customization that Open Atrium provides for its groups and give the possibility to the editor to choose background and foreground colors:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;minisite-color.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;After a quick content editing, the editor can achieve something like this:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;minisite-custom-1.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Screenshots are taken from &lt;a href=&quot;https://nuvole.org/project/alfa-puentes&quot;&gt;Alfa Puentes&lt;/a&gt;, an international project funded by the EU that will focus on exchanging best practices between Europe and Latin America about higher education and that will include several events and conferences, each with a dedicated minisite.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let’s now see how to build this feature in Open Atrium.&lt;/p&gt;
&lt;h2 id=&quot;creating-a-minisite-group-type-spaces-preset&quot;&gt;Creating a minisite group type (spaces preset)&lt;/h2&gt;
&lt;p&gt;To start, we simply define a new spaces preset type, named “minisite”: this can be done through &lt;code&gt;hook_spaces_presets()&lt;/code&gt; using the existing definitions for public and private spaces from &lt;code&gt;atrium_groups.spaces.inc&lt;/code&gt; as a basis.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_minisite_spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $export &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; stdClass&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;disabled &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; FALSE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;api_version &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;minisite&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;title &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Minisite&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;description &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Minisite space preset.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;space_type &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;og&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new space preset can be exported into a feature and creating a minisite becomes as easy as creating a new group.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;minisite-create.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:1}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;assigning-features-to-a-minisite&quot;&gt;Assigning features to a minisite&lt;/h2&gt;
&lt;p&gt;Just like a standard Open Atrium group has some features available by default, we will want our minisites to use a certain set of features by default. In our case, we built two features for Pages and News and we enable them for our minisites, that will thus have static pages and news available.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  ...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;follows&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; above&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $spaces_presets&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;value &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;variable&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;spaces_features&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_blog&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_book&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_calendar&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_casetracker&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_members&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_pages&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_shoutbox&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;spaces_dashboard&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;atrium_news&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  ...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;continue&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; along&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; lines&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; atrium_groups&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;spaces&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;inc&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;making-minisites-self-contained-menus-and-blocks&quot;&gt;Making minisites self-contained: menus and blocks&lt;/h2&gt;
&lt;p&gt;We want our minisites to be self-contained as much as possible. In order to achieve that, with some standard Drupal hooks, we create a new menu for each minisite, that takes care of updating it as soon as the respective minisite group changes. Here is one of the implemented hooks:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_minisite_nodeapi&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$node, $op, $arg &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isset&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($node&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;spaces_preset_og) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      atrium_minisite_is_minisite_preset&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($node&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;spaces_preset_og)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;in_array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($op, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;insert&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;update&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;delete&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;))) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      $menu &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      $menu[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;title&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $node&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;title;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      $menu[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;menu_name&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_minisite_get_menu_name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($node);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      $menu[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;description&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $node&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;og_description;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      atrium_minisite_menu_api&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($op, $menu);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ($op &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;insert&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        module_invoke_all&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;atrium_minisite_default_content&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, $node, $menu);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can notice, our minisite feature exposes an &lt;code&gt;atrium_minisite_default_content()&lt;/code&gt; hook which allows other modules to provide default content once a new minisite is created (e.g. “About” and “Contact” pages, etc…). Shipping each new minisite with default content allows the editor to start filling up the site straight away without the need to recreate again and again the same content structure.&lt;/p&gt;
&lt;p&gt;To make our minisite menu group-aware, we implement &lt;code&gt;hook_block()&lt;/code&gt; in &lt;code&gt;atrium_minisites.module&lt;/code&gt;. The implementation below relies on a couple of helper functions defined elsewhere.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_minisite_block&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($op &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;list&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, $delta &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; NULL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, $edit &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; NULL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  $blocks &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ($op &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;list&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;primary-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;info&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Minisite: Primary navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;primary-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;cache&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; BLOCK_NO_CACHE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;secondary-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;info&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Minisite: Secondary navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;secondary-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;cache&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; BLOCK_NO_CACHE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;full-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;info&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Minisite: Full navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;full-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;cache&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; BLOCK_NO_CACHE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ($op &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;view&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;atrium_minisite_is_minisite_space&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      $config &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_minisite_get_menu_block_config&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($delta);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ($delta &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;secondary-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $delta &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;full-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $config[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;level&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;        &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $config[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;expanded&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $config[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;depth&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ($delta &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;full-navigation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $config[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;level&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;        &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      $blocks &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; menu_tree_build&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($config);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      unset&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($blocks[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;subject&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $blocks;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This block is then specified as a context reaction, so that site visitors will see a self-contained navigation when browsing the minisite.&lt;/p&gt;
&lt;h2 id=&quot;making-minisites-themeable&quot;&gt;Making minisites themeable&lt;/h2&gt;
&lt;p&gt;Each minisite can be assigned a different theme. To do so from a convenient interface, we implement &lt;code&gt;hook_form_alter()&lt;/code&gt; in &lt;code&gt;atrium_minisite.module&lt;/code&gt; to give the possibility to select a theme for the given minisite. Again, we omit some helper functions for brevity.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Implementation of hook_form_alter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_minisite_form_alter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$form, &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$form_state, $form_id) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isset&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($form[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#node&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $form_id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $form[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#node&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;type &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;_node_form&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $node &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $form[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#node&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;];    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;og_is_group_type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($node&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;type)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      module_load_include&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;inc&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;system&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;system.admin&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);      &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      atrium_minisite_theme_form&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($form);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;atrium_minisite_is_minisite_space&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ($node&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;type &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;page&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $space &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; spaces_get_space&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $conf[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;menu_default_node_menu&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_minisite_get_menu_name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($space&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;group);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $menu_name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_minisite_get_menu_name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($space&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;group);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $menu &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($menu_name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $space&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;group&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;title);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        $form[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;menu&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;parent&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#options&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; menu_parent_options&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($menu, $item);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now choose the best theme for our minisite directly on the creation page:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;minisite-theme.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;serving-minisites-on-different-domains&quot;&gt;Serving minisites on different domains&lt;/h2&gt;
&lt;p&gt;Each Open Atrium group gets its own URL path component; in our case the minisite will be reachable at something like &lt;a href=&quot;http://alfapuentes.org/conference/&quot;&gt;http://alfapuentes.org/conference/&lt;/a&gt;. With a quick &lt;a href=&quot;http://drupal.org/project/purl&quot;&gt;Persistent URL&lt;/a&gt; customization we can actually set the URL rewriting to be per domain, allowing our minisite to have its own domain. This can be achieved by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enabling the “Domain” URL modification type from &lt;code&gt;admin/settings/purl/types&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Assigning to “Group space” the new modifier type on &lt;code&gt;admin/settings/purl&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Adding a custom URL each time we are going to create a minisite group&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By taking advantage of the Persistent URL domain rewriting we can really make the new minisite look completely independent from the underlying Open Atrium platform:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;minisite-url.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The techniques shown here are covered in detail in our &lt;a href=&quot;/trainings&quot;&gt;trainings&lt;/a&gt;; if interested, &lt;a href=&quot;/contact&quot;&gt;contact us&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Look bigger with Features</title><link>https://www.nuvole.org/blog/look-bigger-features/</link><guid isPermaLink="true">https://www.nuvole.org/blog/look-bigger-features/</guid><pubDate>Wed, 18 Jan 2012 20:56:14 GMT</pubDate><content:encoded>&lt;p&gt;Nuvole built some respectably sized projects in 2011: a few large international projects (including &lt;a href=&quot;bolognaexperts.net&quot;&gt;a heavily customized Open Atrium installation&lt;/a&gt; for Higher Education experts and a &lt;a href=&quot;https://nuvole.org/project/alfa-puentes&quot;&gt;portal for collaboration between Europe and Latin America&lt;/a&gt; on educational matters) and a bunch of ordinary websites, besides our other activities such as trainings and general consultancy. And people occasionally ask us how big Nuvole is.&lt;/p&gt;
&lt;p&gt;Well, the truth is that &lt;strong&gt;Nuvole is a two-people company&lt;/strong&gt;, helped by minor contributions by external consultants. Yet, it manages to run and maintain sizeable Drupal projects by using &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt; -the easy part- and by doing it properly - the hard part, and the main focus of &lt;a href=&quot;http://denver2012.drupal.org/node/3333&quot;&gt;our DrupalCon Denver Training&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So the key is not how many people work on a project, but &lt;em&gt;how efficient their workflow is&lt;/em&gt;. Adopting a Features-based workflow provides a solid basis, but you still need to take a lot of design decisions: some will be easier, some will require a bit of thinking, some will prove correct or wrong only months later, when the project needs infrastructural updates.&lt;/p&gt;
&lt;p&gt;Nuvole pioneered a Features-based development, and what in general we call the “Code-Driven Development workflow”, a couple of years ago. Our Features-based projects look much different now: we found out that our old structure was not optimal, or that technology allowed for cleaner workflows (Drupal 7 brought some substantial improvements, for example). We structured our new projects to take the new knowledge into account, found new challenges and gradually solved them.&lt;/p&gt;
&lt;p&gt;Here are a couple of examples of the design decisions you will be happy to have taken properly (we will cover them in detail in future blog posts).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hard and soft configuration&lt;/strong&gt;. A site, especially when built as a distribution, should ship with complete pre-configuration. And this pre-configuration should be in code, we don’t want to carry a database around. But should all of it go into Features? No, because some pre-configuration (the &lt;em&gt;soft&lt;/em&gt; one) is meant to be overridden in the normal site customization, while Features should only contain the &lt;em&gt;hard&lt;/em&gt; one, i.e., configuration that defines the site structure. The soft configuration (like the default site title, or default colors) is best handled by putting the corresponding code in the installation profile rather than in Features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Feature separation&lt;/strong&gt;. When all your site configuration is in Features, you quickly realize that it is not feasible to put everything into one Feature: you will need multiple Features to build a website, and this means you will have to take decisions on how to split your site functionality across Features. A common mistake is to build &lt;em&gt;transversal Features&lt;/em&gt;, like a “permissions” Feature with all site permissions: this works, but it introduces annoying interdependencies and maintenance difficulties in the long run. The problem is best solved by &lt;em&gt;basing Features on content types&lt;/em&gt;, thus making them truly modular: in general, a Feature should contain a content type and everything that is related to it, such as views, contexts, image styles and permissions.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The Nuvole DrupalCon training, &lt;a href=&quot;http://denver2012.drupal.org/node/3333&quot;&gt;Code-driven Development: Use Features Effectively&lt;/a&gt;, will be held on March 19th, the day before DrupalCon Denver. The training is open to both DrupalCon attendees and externals, and must be booked from the &lt;a href=&quot;https://association.drupal.org/denver2012&quot;&gt;DrupalCon registration page&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Hard and Soft configuration in Drupal Distributions</title><link>https://www.nuvole.org/blog/hard-and-soft-configuration-drupal-distributions/</link><guid isPermaLink="true">https://www.nuvole.org/blog/hard-and-soft-configuration-drupal-distributions/</guid><pubDate>Tue, 07 Feb 2012 15:30:44 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;An extract from the new teaching materials we are preparing for our &lt;strong&gt;DrupalCon Denver 2012 pre-conference training&lt;/strong&gt;, &lt;a href=&quot;http://denver2012.drupal.org/node/3333&quot;&gt;Code-Driven Development: Use Features Effectively&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;By now the advantages of a clean Code-Driven Development workflow are clear to the majority of Drupal developers; things like separation between configuration and content, packaging and deployment of new site functionalities or sharing changes in a distributed team don’t look that scary anymore.&lt;/p&gt;
&lt;p&gt;Still not everything in Drupal can be clearly classified into configuration or content, such as Taxonomy terms. Taxonomy terms are mostly used to categorize our content so, naturally, they are often used in site configuration: a &lt;a href=&quot;http://drupal.org/project/context&quot;&gt;context&lt;/a&gt; that reacts to a given taxonomy term is a fairly common scenario. Taxonomy terms can be generated by users (think about free-tagging), which makes them fall into the “Content realm”. Drupal, in fact, considers them as such, assigning to each term a unique numeric identifier, and that’s the problem.&lt;/p&gt;
&lt;h2 id=&quot;each-component-has-its-own-name&quot;&gt;Each component has its own name&lt;/h2&gt;
&lt;p&gt;One of the turning points in making configuration exportable in code was the introduction of a unique string identifier for each component: this way it’s easy to share configuration and to avoid conflicts. So what happens with taxonomy terms? If they appear in our context conditions then we’d better bundle them together: no matter where we are going to deploy our feature, they will always have to have the same values. That’s simply not possible: they have numeric IDs, forget about easy life here.&lt;/p&gt;
&lt;p&gt;Various attempts have been made to export numerically identified components, such as &lt;a href=&quot;http://drupal.org/project/uuid_features&quot;&gt;UUID Features Integration&lt;/a&gt; or the handy &lt;a href=&quot;http://drupal.org/project/defaultcontent&quot;&gt;Default Content&lt;/a&gt; module, but they only solve part of the problem: we want &lt;strong&gt;other&lt;/strong&gt; components to know the unique name of our terms.&lt;/p&gt;
&lt;h2 id=&quot;hard-and-soft-configuration&quot;&gt;Hard and Soft configuration&lt;/h2&gt;
&lt;p&gt;At Nuvole we adopted the following terminology to classify the two kind of settings that we need to deal with everyday:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hard configuration&lt;/strong&gt; includes the settings under the distribution developer’s control (e.g., Views or Contexts); it has a machine name, it is easy to export, it gives no headache. We store it in Features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Soft configuration&lt;/strong&gt; includes the settings that are meant to be overridden by the site administrator (e.g., the default theme, or the initial terms of a vocabulary); it often gets a unique numeric ID from Drupal, it is impossible to export safely, it is painful to handle. We store it in the Installation profile.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This distinction becomes fundamental when the configuration is altered or (if you are dealing with a distribution) when the underlying distribution is upgraded. In the case of &lt;em&gt;Hard configuration&lt;/em&gt;, altering it results in an overridden feature, which is upgrade-unsafe. In the case of &lt;em&gt;Soft configuration&lt;/em&gt;, altering it does not change the Features state, since the corresponding settings are stored in the Installation profile, and changes are upgrade-safe.&lt;/p&gt;
&lt;h2 id=&quot;not-only-taxonomy-terms&quot;&gt;Not only taxonomy terms&lt;/h2&gt;
&lt;p&gt;The distinction between Hard and Soft configuration goes beyond how to conveniently export taxonomy terms: it is more a design decision, especially important when dealing with distributions. We consider everything that the site builder might be entitled to change as Soft configuration. The place where to usually store Soft configuration is your &lt;code&gt;hook_install()&lt;/code&gt;; this guarantees a safe upgrade path to the next version of the distribution. An example could be the default theme: you may ship your distribution with a specific theme but the site owner might want to change it and subsequent updates shouldn’t alter it.&lt;/p&gt;
&lt;p&gt;Here is how our profile &lt;code&gt;hook_install()&lt;/code&gt; might look like in a Drupal 7 distribution:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Implements hook_install()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; example_install&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   $terms &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   $vocabulary &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; taxonomy_vocabulary_machine_name_load&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;category&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   $terms[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Solution&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   $terms[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Client&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   $terms[] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Use case&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   foreach&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ($terms &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $name) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;     $term &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; stdClass&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;     $term&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;vid &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $vocabulary&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;vid;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;     $term&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;     taxonomy_term_save&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($term);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Enable custom theme&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  theme_enable&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;example_theme&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  variable_set&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;theme_default&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;example_theme&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);   &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;reference-for-site-builders&quot;&gt;Reference for site builders&lt;/h2&gt;
&lt;p&gt;If you are customizing a distribution and you need to override some of its default Hard configuration you might want to have a  look at the &lt;a href=&quot;http://drupal.org/project/features_override&quot;&gt;Features Override&lt;/a&gt; module and at these two related articles from &lt;a href=&quot;http://www.phase2technology.com/&quot;&gt;Phase2 Technology&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.agileapproach.com/blog-entry/new-paradigm-overriding-drupal-features&quot;&gt;A new paradigm for overriding Drupal Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.agileapproach.com/blog-entry/features-and-overrides-part-duex&quot;&gt;Features and Overrides - Part Deux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Present and Future of Open Atrium</title><link>https://www.nuvole.org/blog/present-and-future-open-atrium/</link><guid isPermaLink="true">https://www.nuvole.org/blog/present-and-future-open-atrium/</guid><pubDate>Thu, 03 May 2012 13:32:05 GMT</pubDate><content:encoded>&lt;p&gt;Our &lt;a href=&quot;http://books.slashdot.org/story/12/04/30/1235250/book-review-drupal-intranets-with-open-atrium&quot;&gt;review of Tracy Smith’s Open Atrium book&lt;/a&gt; just made it to Slashdot, confirming there is a widespread interest in this popular Drupal distribution. (By the way, the book is not, and is not meant to be, useful to developers, but if you build Open Atrium projects you should definitely recommend it to your clients: it will save a lot of time to you and them).&lt;/p&gt;
&lt;p&gt;The most interesting consideration is that the book, published in January 2011, is still perfectly current now, as of &lt;a href=&quot;http://openatrium.com/node/47&quot;&gt;Open Atrium 1.3&lt;/a&gt; released just a few weeks ago. And this is &lt;em&gt;not&lt;/em&gt; quite good: most distributions have been significantly updated in recent months, while Open Atrium has been quite conservative in features and updates so far.&lt;/p&gt;
&lt;p&gt;In case you haven’t heard, this is going to change. The future of Open Atrium looks bright, and, while there isn’t a roadmap yet, the guys at &lt;a href=&quot;http://www.phase2technology.com/&quot;&gt;Phase2&lt;/a&gt; have recently taken steps to make sure that Open Atrium can again be a shiny example of innovation and bleeding-edge, useful, technology:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://community.openatrium.com/dev&quot;&gt;Open Atrium Development Group&lt;/a&gt; is now open for everybody to join. Open Atrium is now a true community product with a transparent development process.&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://community.openatrium.com/shopresources/feature-directory&quot;&gt;Feature and Theme Directory&lt;/a&gt; is now available to find in a single repository all features and themes created by the Community, such as Nuvole’s &lt;a href=&quot;https://github.com/nuvoleweb/atrium_folders&quot;&gt;Atrium Folders&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An interesting &lt;a href=&quot;http://denver2012.drupal.org/bof/open-atrium-roadmap&quot;&gt;BoF session&lt;/a&gt; was held at DrupalCon Denver, where we shared our experience with &lt;a href=&quot;https://nuvole.org/project/alfa-puentes&quot;&gt;advanced Open Atrium customizations&lt;/a&gt; and we discovered that people are pushing this distribution to many different use cases: there are hundreds of organizations and companies working with Open Atrium now, and each of them can provide valuable feedback in shaping the future version.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The discussion about Open Atrium on Drupal 7 is &lt;a href=&quot;https://community.openatrium.com/dev/node/3950&quot;&gt;now open&lt;/a&gt;, don’t miss it!&lt;/p&gt;</content:encoded></item><item><title>Drupal Global Training Day</title><link>https://www.nuvole.org/blog/drupal-global-training-day/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-global-training-day/</guid><pubDate>Tue, 05 Jun 2012 15:10:15 GMT</pubDate><content:encoded>&lt;p&gt;Nuvole is proud to join the &lt;a href=&quot;http://drupal.org/learn-drupal&quot;&gt;Drupal Global Training Day&lt;/a&gt; on June 22nd.&lt;/p&gt;
&lt;p&gt;We will give a free generic introduction to Drupal. So this won’t be our usual, highly technical, training about streamlining Drupal Development that you may have seen at Drupalcon Chicago 2011, DrupalCon Denver 2012 and that is &lt;a href=&quot;http://munich2012.drupal.org/program/training/code-driven-development-features-and-beyond&quot;&gt;scheduled for DrupalCon Munich 2012&lt;/a&gt; too. This time we will focus on Drupal as a platform, its community, what can be easily done with the available modules and what can be reached with a bit (or a lot!) of customization.&lt;/p&gt;
&lt;p&gt;The target public is mainly people who would like to assess Drupal for use in &lt;strong&gt;international not-for-profit organizations&lt;/strong&gt;: Nuvole has a long &lt;a href=&quot;https://nuvole.org/portfolio&quot;&gt;history&lt;/a&gt; of successful projects with this kind of organizations, and the examples we will show are all from this sector.&lt;/p&gt;
&lt;p&gt;Topics will include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A generic introduction to Drupal and its community&lt;/li&gt;
&lt;li&gt;Why open-source is the best solution: low entry cost, availability of thousands of free modules, focus on customization&lt;/li&gt;
&lt;li&gt;Drupal for distributed NGOs: Public websites with private working groups (Intranet) and file repositories&lt;/li&gt;
&lt;li&gt;Drupal for event sites: minisites for conferences and events, easy to customize and replicate&lt;/li&gt;
&lt;li&gt;Drupal for campaigns: high-impact sites for campaigns with social networks integration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The event is free to attend, and attendees are also welcome to stay for a buffet lunch. Registration is required. Please see &lt;a href=&quot;http://drupal-ngos.eventbrite.com/&quot;&gt;the registration page&lt;/a&gt; for all details.&lt;/p&gt;</content:encoded></item><item><title>Automating Drupal Development: Makefiles, Features and beyond</title><link>https://www.nuvole.org/blog/automating-drupal-development-makefiles-features-and-beyond/</link><guid isPermaLink="true">https://www.nuvole.org/blog/automating-drupal-development-makefiles-features-and-beyond/</guid><pubDate>Mon, 25 Jun 2012 13:16:27 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Features&lt;/a&gt; are a well-known and very useful tool to streamline Drupal development and write reusable components. However, if you get serious about automating as much as possible in your Drupal development, you need to add other tools to your daily practices, and master them properly: forget the tedious, error-prone, point-and-click and embrace Code-Driven Development.&lt;/p&gt;
&lt;p&gt;Automating Drupal Development, as presented by Nuvole at the &lt;a href=&quot;http://barcelona2012.drupaldays.org/sessions/automating-drupal-development-features-makefiles-and-beyond&quot;&gt;Drupal Developer Days Barcelona&lt;/a&gt; last week, consists of the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Automating code retrieval.&lt;/strong&gt; Use &lt;a href=&quot;http://drupal.org/project/drush&quot;&gt;Drush&lt;/a&gt; Make to keep track of modules, patches and Features, and rely on &lt;a href=&quot;http://drupal.org/project/buildkit&quot;&gt;Buildkit&lt;/a&gt; as a basic distribution with preselected modules to make your development faster. Use &lt;em&gt;Drush Bake&lt;/em&gt; to create makefiles based on templates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automating installation.&lt;/strong&gt; Customize the &lt;a href=&quot;http://drupal.org/node/159730&quot;&gt;installation profile&lt;/a&gt; to perform all tasks you need during installation, especially pre-configuring your site with those “soft configuration” settings that the site administrator will be able to modify without overriding your Features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automating site configuration.&lt;/strong&gt; Use Features properly, to make them truly modular, and install a &lt;a href=&quot;http://drupal.org/project/fserver&quot;&gt;Features server&lt;/a&gt;, where you will host and track your Features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automating tests.&lt;/strong&gt; Use continuous integration to automatically ensure that your website will continue to work as expected when it is upgraded: test your codebase, installation and site configuration on every code change.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;These concepts will be covered in more detail in the Nuvole &lt;a href=&quot;http://munich2012.drupal.org/program/training/code-driven-development-features-and-beyond&quot;&gt;DrupalCon Munich Training&lt;/a&gt; (if you don’t see the page, you may need to register to the DrupalCon Munich website).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The full presentation from Barcelona is below, as a &lt;a href=&quot;http://www.slideshare.net/nuvoleweb/automating-drupal-development-makefiles-features-and-beyond&quot;&gt;Slideshare embed&lt;/a&gt; or a PDF file (attached).&lt;/p&gt;
&lt;iframe src=&quot;https://www.slideshare.net/slideshow/embed_code/key/gr1Zry0iMymSJW?startSlide=1&quot; width=&quot;597&quot; height=&quot;486&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border: var(--border-1) solid #CCC; border-width:1px; margin-bottom:5px;max-width: 100%;&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;drupal_dev_days_bcn.pdf (13.94 MB) &lt;a href=&quot;/sites/default/files/drupal_dev_days_bcn.pdf&quot;&gt;Download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>A Code-Driven Development Cheatsheet</title><link>https://www.nuvole.org/blog/code-driven-development-cheatsheet/</link><guid isPermaLink="true">https://www.nuvole.org/blog/code-driven-development-cheatsheet/</guid><pubDate>Fri, 25 Mar 2011 16:31:54 GMT</pubDate><content:encoded>&lt;p&gt;Our &lt;a href=&quot;http://chicago2011.drupal.org/training/code-driven-development-use-features-effectively&quot;&gt;Code-Driven Development with Features&lt;/a&gt; training in Chicago was a very interesting experience for us: people with different levels of Drupal expertise were able to appreciate the &lt;a href=&quot;https://nuvole.org/blog/2011/jan/13/five-reasons-code-driven-development-and-features-are-good-you&quot;&gt;benefits of code-driven development&lt;/a&gt; and the most experienced attendees managed to experiment with the development workflow on their own laptops, by building and modifying sandbox websites.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;5507028473_f9ca241a3a_b.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;As a gift to those who couldn’t attend the training because it was sold out, and to all lovers of Drush, Features and code-driven development in general, we are sharing a Code-Driven Development Cheatsheet that was part of the training materials. Please find it attached to this post, in PDF format.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;cheatsheet.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;We still have some printed copies available to give away; if you are attending the &lt;a href=&quot;http://www.drupalgovdays.com/&quot;&gt;Drupal Government Days&lt;/a&gt; in two weeks in Brussels and are interested in one just let us know.&lt;/p&gt;
&lt;p&gt;Thanks to the DrupalCon trainees for their nice and constructive feedback, to DrupalCon organizers for assistance and logistics… and for those who couldn’t make it to Chicago, there will be further opportunities in Europe in the next months!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Image credits: from &lt;a href=&quot;http://www.flickr.com/photos/drupalassoc/5507028473/in/set-72157626097060777/&quot;&gt;DrupalCon Chicago&lt;/a&gt; on Flickr.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;code-driven-development-cheatsheet-nuvole.pdf (1.45 MB) &lt;a href=&quot;/sites/default/files/code-driven-development-cheatsheet-nuvole.pdf&quot;&gt;Download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>From DrupalCon Munich: CMI, Drupal automation, Open Atrium on D7</title><link>https://www.nuvole.org/blog/drupalcon-munich-cmi-drupal-automation-open-atrium-d7/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupalcon-munich-cmi-drupal-automation-open-atrium-d7/</guid><pubDate>Fri, 24 Aug 2012 12:37:56 GMT</pubDate><content:encoded>&lt;p&gt;The DrupalCon Munich 2012 is just over. There were quite a few inspiring sessions, but here are a few highlights of the moments and topics where the Nuvole team was mostly involved.&lt;/p&gt;
&lt;h2 id=&quot;code-driven-development-another-training-and-what-about-drupal-8&quot;&gt;Code-Driven Development: another training… and what about Drupal 8?&lt;/h2&gt;
&lt;p&gt;We held the &lt;a href=&quot;http://munich2012.drupal.org/program/training/code-driven-development-features-and-beyond&quot;&gt;pre-conference Nuvole training&lt;/a&gt; about Code-Driven Development (Features, Makefiles, a set of techniques for effective Drupal programming) at DrupalCon Europe for the first time, after two successful editions at DrupalCon Chicago 2011 and DrupalCon Denver 2012. Nice to see that there are more and more training attendees who are already familiar with the tools but still want to see the big picture and find out how to really streamline their Drupal development.&lt;/p&gt;
&lt;p&gt;And what’s coming with Drupal 8? We followed with great attention &lt;a href=&quot;http://munich2012.drupal.org/content/status-configuration-management-initiative&quot;&gt;Greg Dunlap’s Munich session&lt;/a&gt; to understand the impact of the &lt;a href=&quot;http://groups.drupal.org/build-systems-change-management/cmi&quot;&gt;Configuration Management Initiative&lt;/a&gt; on the Code-Driven workflow we advocate. There will surely be disruptive changes, but in the end Drupal will be improving a lot as far as configuration management is concerned. And Features won’t die: part of its functionality (the import/export itself) will be taken over by Drupal core, but Features will be able to refocus as a tool to package configuration. And it’s time for Nuvole too to stop lurking on the CMI group and start showing up more frequently on issue queues!&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;2012-08-2016.17.34.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;bof-automating-drupal-development&quot;&gt;BoF: Automating Drupal Development&lt;/h2&gt;
&lt;p&gt;The BoF session about &lt;a href=&quot;http://munich2012.drupal.org/content/automating-drupal-development-makefiles-features-and-beyond&quot;&gt;Automating Drupal Development&lt;/a&gt; was packed, as many developers shared their secrets to effective Drupal Development.&lt;/p&gt;
&lt;p&gt;The basic idea was how to get rid of all repetitive tasks in Drupal Development and automate as much as possible: building the code base (Drush Make), creating the installation profile from templates, storing and packaging configuration with modular Features and a Features server, rely on an automated testing framework activated at every code change. Tools we discussed are mostly the same as in &lt;a href=&quot;https://nuvole.org/blog/2012/jun/25/automating-drupal-development-makefiles-features-and-beyond&quot;&gt;our Barcelona DrupalDays presentation&lt;/a&gt;, with the addition of &lt;a href=&quot;http://capistranorb.com/&quot;&gt;Capistrano&lt;/a&gt; and &lt;a href=&quot;http://www.peritor.com/en/products/webistrano/&quot;&gt;Webistrano&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;2012-08-2210.42.25.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;bof-the-future-of-open-atrium-getting-it-done&quot;&gt;BoF: The future of Open Atrium: getting it done&lt;/h2&gt;
&lt;p&gt;The BoF about &lt;a href=&quot;http://munich2012.drupal.org/content/future-open-atrium-getting-it-done&quot;&gt;the future of Open Atrium&lt;/a&gt; was to bring together the core &lt;a href=&quot;http://openatrium.com&quot;&gt;Open Atrium&lt;/a&gt; team, working for &lt;a href=&quot;http://www.phase2technology.com/&quot;&gt;Phase2 Technology&lt;/a&gt;, and companies and consultants that, like Nuvole, provide Open Atrium services: if we wish to move from talk to action, multiple parties will need to help. Here’s an outline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update core and modules to their D7 version, update the Blog (to become “Discussion”) and Book features, introduce the concept of “Organizations” to be able to apply certain criteria (e.g., inclusion in a group) to multiple users.&lt;/li&gt;
&lt;li&gt;Adopt a new, mobile-friendly, responsive, theme.&lt;/li&gt;
&lt;li&gt;Migrate the other Atrium features.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Development is expected to start soon with the creation of a Drupal 7 branch for all involved features. The basic Open Atrium structure will be ported to Drupal 7 as soon as possible, to give all community members the opportunity to help. A development version will be regularly made available for people to follow progress.&lt;/p&gt;
&lt;p&gt;Lower priority items, which could appear after the Open Atrium 2.0 release (based on Drupal 7), like in version 2.1, include a full content migration path from OpenAtrium 1.x and adding new functionality. But the discussion is open and if you want to help shaping the future of Open Atrium just &lt;a href=&quot;https://community.openatrium.com/dev&quot;&gt;get involved&lt;/a&gt;!&lt;/p&gt;</content:encoded></item><item><title>Apps for Open Atrium: Atrium Folders</title><link>https://www.nuvole.org/blog/apps-open-atrium-atrium-folders/</link><guid isPermaLink="true">https://www.nuvole.org/blog/apps-open-atrium-atrium-folders/</guid><pubDate>Wed, 25 May 2011 13:35:47 GMT</pubDate><content:encoded>&lt;p&gt;Open Atrium’s default set of features covers most of the common needs of an average organization, except one: a file repository with the familiar look and feel of a folders tree. By deploying customized versions of Open Atrium for different kinds of organizations we noticed that the built-in &lt;a href=&quot;https://community.openatrium.com/documentation-en/node/13&quot;&gt;Notebook feature&lt;/a&gt; is still cumbersome for intranet users to store and categorize their documents (meeting reports, minutes, etc…): they are used to store their files in directories and subdirectories and not as books and child pages.&lt;/p&gt;
&lt;p&gt;On the other hand, the needed technology for a more familiar documents repository is there: Drupal’s core Book module can create hierarchical relations among nodes and files can be easily attached to nodes, so it’s enough to interpret book pages as folders and their attachments as files to get the same paradigm users are familiar with.&lt;/p&gt;
&lt;h2 id=&quot;enter-atrium-folders-a-simple-file-repository-application&quot;&gt;Enter Atrium Folders: A simple file repository application&lt;/h2&gt;
&lt;p&gt;The idea behind Atrium Folders is to have a very simple file repository application with no external dependencies that users can just download, enable and start playing with. To get started, download the code directly from &lt;a href=&quot;https://github.com/nuvoleweb/atrium_folders&quot;&gt;Nuvole’s GitHub&lt;/a&gt;, place it under &lt;code&gt;sites/all/modules/features&lt;/code&gt; in your Open Atrium installation and enable the new feature. Atrium Folders is shipped as an ordinary &lt;a href=&quot;http://drupal.org/project/features&quot;&gt;Drupal feature&lt;/a&gt; so, after enabling it as a module, we need to &lt;a href=&quot;https://community.openatrium.com/documentation-en/node/1965&quot;&gt;enable it&lt;/a&gt; in the Open Atrium group.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-settings.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;We can now visit the new &lt;strong&gt;Folders&lt;/strong&gt; section and start adding the first folder.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-add-folder.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;As already mentioned, folders are nodes: we just need to specify its name, press &lt;em&gt;Save&lt;/em&gt; and we are good to go. Now, say goodbye to the ordinary Drupal node interface; from now on we will be managing our repository from an intuitive “filesystem browser”-like interface:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-overview.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;managing-your-repository&quot;&gt;Managing your repository&lt;/h2&gt;
&lt;p&gt;Use the top menu to manage the folder currently being displayed; it contains the following four items.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Basic information&lt;/strong&gt;: change name and description of the current folder:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-generalinfo.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Subfolders&lt;/strong&gt;: easily add a bunch of subfolders by entering their names, one per line:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-subfolders.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attach files to this folder&lt;/strong&gt;: just the reinterpretation of the good old Drupal file upload form, it gives the possibility to upload as many files as we need to the current folder:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-upload.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Notifications&lt;/strong&gt;: specify who will get a notification of the current changes (again, we don’t reinvent the wheel but we integrate with the default Open Atrium notifications system):&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-notifications.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Upload multiple files&lt;/strong&gt;: quickly upload a ZIP archive containing several files and folders: Atrium Folders will unzip it for you and place its content in the current folder (PHP must have &lt;a href=&quot;http://www.php.net/manual/en/book.zip.php&quot;&gt;ZIP support&lt;/a&gt; enabled in order for this functionality to be available)&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-zip.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;rearranging-folders&quot;&gt;Rearranging folders&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Reorder&lt;/strong&gt; tab allows to rearrange the structure of our repository by simple drag-and-drop. This handy interface, borrowed from Drupal’s core Book module, is also very helpful when we have to rename several folders at once.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-reorder.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;file-toolbox&quot;&gt;File toolbox&lt;/h2&gt;
&lt;p&gt;Each file has a toolbox that will allow to &lt;strong&gt;rename&lt;/strong&gt;, &lt;strong&gt;move&lt;/strong&gt; or &lt;strong&gt;delete&lt;/strong&gt; the respective file. The toolbox uses &lt;a href=&quot;http://jquery.com/&quot;&gt;jQuery&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Ajax_(programming)&quot;&gt;AJAX calls&lt;/a&gt; extensively, to guarantee a smooth user interaction: files will be modified and changes will take immediate effect, with no need to reload the page.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-toolbox-rename.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;creating-your-own-toolbox&quot;&gt;Creating your own toolbox&lt;/h2&gt;
&lt;p&gt;Atrium Folders exposes  &lt;code&gt;hook_folder_toolbox()&lt;/code&gt; to enable third party modules to expose their own toolbox. As an example, here is the default &lt;code&gt;atrium_folders_folder_toolbox()&lt;/code&gt; implementation:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Implementation of hook_folder_toolbox()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; atrium_folders_folder_toolbox&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;op&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;move&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;type&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;file&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;title&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Move&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;description&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Move the file to another location.&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;form callback&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;atrium_folders_move_file&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;ajax callback&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;atrium_folders_move_file_ajax_callback&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;drag-and-drop-upload&quot;&gt;Drag and Drop Upload&lt;/h2&gt;
&lt;p&gt;Atrium Folders ships with a built-in integration for the &lt;a href=&quot;http://drupal.org/project/dragndrop_uploads&quot;&gt;Drag’n’Drop Uploads&lt;/a&gt; module which gives the possibility to simply drag and drop files on the upload form to attach them to a node. If Drag’n’Drop Uploads module is enabled a &lt;em&gt;Drop zone&lt;/em&gt; will be visible in the &lt;strong&gt;Attach files to this folder&lt;/strong&gt; panel.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;folders-dropzone.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;drush-integration-import-an-existing-structure&quot;&gt;Drush integration: import an existing structure&lt;/h2&gt;
&lt;p&gt;When an organization wants to switch to Open Atrium, in most cases they need to migrate a shared folder to the new intranet. Atrium Folders includes the &lt;code&gt;folder-import&lt;/code&gt; &lt;a href=&quot;http://drupal.org/project/drush&quot;&gt;Drush&lt;/a&gt; command that allows to import a directory located in our local filesystem into a certain folder of a specific Open Atrium group. We just need to specify the full path of the directory we want to import and the Drupal path of the group we want it to be imported:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; folder-import&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /tmp/meeting&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; secretariat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Created&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 13&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; folders&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; and&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 168&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; files.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type &lt;code&gt;$ drush help folder-import&lt;/code&gt; for more.&lt;/p&gt;</content:encoded></item><item><title>A new role at the Apache Software Foundation</title><link>https://www.nuvole.org/blog/new-role-apache-software-foundation/</link><guid isPermaLink="true">https://www.nuvole.org/blog/new-role-apache-software-foundation/</guid><pubDate>Thu, 18 Oct 2012 17:39:51 GMT</pubDate><content:encoded>&lt;p&gt;Effective today, I (Andrea) have &lt;a href=&quot;https://blogs.apache.org/foundation/entry/the_apache_software_foundation_announces35&quot;&gt;become an officer&lt;/a&gt; of the &lt;a href=&quot;http://apache.org/&quot;&gt;Apache Software Foundation&lt;/a&gt;, in my new role as Vice President for the &lt;a href=&quot;http://www.openoffice.org&quot;&gt;Apache OpenOffice&lt;/a&gt; (commonly known as “OpenOffice”) project.&lt;/p&gt;
&lt;p&gt;This doesn’t change much in my daily activity as director of Nuvole. I have been involved in the OpenOffice project for several years, starting much earlier than Drupal became a mainstream project. It has been and it will continue to be a volunteer activity, even though it will definitely happen that I attend more Apache events in the near future.&lt;/p&gt;
&lt;p&gt;That said, this might be an occasion for the Drupal community to take a closer look at the Apache Software Foundation and its projects.&lt;/p&gt;
&lt;p&gt;Even though there are still many people who identify Apache with its ubiquitous &lt;a href=&quot;http://httpd.apache.org/&quot;&gt;Apache HTTP server&lt;/a&gt;, the Apache Software Foundation is much larger than that: it is a huge, not-for-profit organization developing around 150 free and open source projects for the public good.&lt;/p&gt;
&lt;p&gt;There are interesting opportunities for cooperation between Drupal and Apache projects: &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Apache Solr&lt;/a&gt;, for example, has already become a standard topic at Drupal events; &lt;a href=&quot;http://poi.apache.org/&quot;&gt;Apache POI&lt;/a&gt;, &lt;a href=&quot;http://tika.apache.org/&quot;&gt;Apache Tika&lt;/a&gt; and &lt;a href=&quot;http://pdfbox.apache.org/&quot;&gt;Apache PDFBox&lt;/a&gt; can extract text and metadata from Office or PDF documents and make them search-friendly, with &lt;a href=&quot;http://drupal.org/project/text_extract&quot;&gt;initial&lt;/a&gt; &lt;a href=&quot;http://drupal.org/project/apachesolr_attachments&quot;&gt;work&lt;/a&gt; towards Drupal integration; &lt;a href=&quot;http://trafficserver.apache.org/&quot;&gt;Apache Traffic Server&lt;/a&gt; is a caching server with Drupal support. The incubating &lt;a href=&quot;http://incubator.apache.org/openmeetings/&quot;&gt;Apache OpenMeetings&lt;/a&gt; project has a module for Drupal integration too.&lt;/p&gt;
&lt;p&gt;The Apache OpenOffice project itself maintains two old but functional Drupal sites for hosting its &lt;a href=&quot;http://extensions.openoffice.org/&quot;&gt;Extensions&lt;/a&gt; and &lt;a href=&quot;http://templates.openoffice.org/&quot;&gt;Templates&lt;/a&gt;, that were recently relocated to &lt;a href=&quot;http://sf.net&quot;&gt;SourceForge&lt;/a&gt; with technical advice from Nuvole; and sooner or later it will have to consider a deeper integration with Web technologies…&lt;/p&gt;
&lt;p&gt;We at Nuvole are looking forward to exploring opportunities for cross-project collaboration, and the &lt;a href=&quot;http://www.apachecon.eu/&quot;&gt;ApacheCon 2012&lt;/a&gt; in Germany next month might be a nice occasion for brainstorming. If you have ideas to share, please do so in the comments below.&lt;/p&gt;</content:encoded></item><item><title>Configuration Management and Features: a look at Drupal 8</title><link>https://www.nuvole.org/blog/configuration-management-and-features-look-drupal-8/</link><guid isPermaLink="true">https://www.nuvole.org/blog/configuration-management-and-features-look-drupal-8/</guid><pubDate>Tue, 03 Sep 2013 13:32:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;An update to this post is now available at &lt;a href=&quot;https://nuvole.org/blog/2014/jul/15/packaging-and-reusing-configuration-drupal-8&quot;&gt;https://nuvole.org/blog/2014/jul/15/packaging-and-reusing-configuration-drupal-8&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We have always told attendees of our &lt;a href=&quot;https://nuvole.org/trainings&quot;&gt;Drupal 7 “Code-driven development” trainings&lt;/a&gt; that embracing the idea that configuration must be stored in files and not in the database would make their Drupal development future-ready: the specific processes would necessarily change but the concept would stay and be reinforced in Drupal 8, thanks to the nice work being done in the &lt;a href=&quot;http://heyrocker.com/cmi-feature-freeze&quot;&gt;Configuration Management Initiative&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And this was right. The progress done in CMI is very good, to the point that in our coming DrupalCon training in Prague, &lt;a href=&quot;https://prague2013.drupal.org/node/1828&quot;&gt;Code-Driven Development: an Effective Drupal Workflow, from D7 to D8&lt;/a&gt; we will explore in detail how things work in Drupal 7, but for each topic we’ll also describe what is going to improve/change in Drupal 8.&lt;/p&gt;
&lt;h2 id=&quot;configuration-management-in-a-nutshell&quot;&gt;Configuration Management in a nutshell&lt;/h2&gt;
&lt;p&gt;From a site builder’s point of view, Configuration Management in Drupal 8 is visible as two items in &lt;code&gt;admin/config/development&lt;/code&gt;: “Configuration Export” and “Configuration Import”.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Export&lt;/strong&gt; functionality has no further options, and it provides you with a &lt;code&gt;config.tar.gz&lt;/code&gt; file containing hundreds (180+ on a clean installation, many more on a feature-rich test site) of files in YAML format, human- and machine-readable. All the site configuration is there: node types, their fields, permissions, Views, theme settings, variables…&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Import&lt;/strong&gt; functionality, on the converse, will import a &lt;code&gt;config.tar.gz&lt;/code&gt; file, show you any configuration changes between the currently active configuration and the one you are trying to import and allow you to replace the current configuration with the one in the provided files.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;config-import.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;config-import&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;For many developers, this is a dream come true: with D8, Drupal Core will support a &lt;strong&gt;file-based configuration&lt;/strong&gt;, allowing developers to track changes, put configuration under version control, update a production site cleanly… basically, all the advantages that we have described as &lt;a href=&quot;https://nuvole.org/blog/2011/jan/13/five-reasons-code-driven-development-and-features-are-good-you&quot;&gt;selling points for the Features module&lt;/a&gt; in the last several years.&lt;/p&gt;
&lt;h2 id=&quot;the-future-of-features&quot;&gt;The Future of Features&lt;/h2&gt;
&lt;p&gt;So, if the major selling points of the &lt;a href=&quot;https://drupal.org/project/features&quot;&gt;Features&lt;/a&gt; module are now in core, does Features have a future in Drupal 8?&lt;/p&gt;
&lt;p&gt;It still does, but it needs to be repurposed. So far, it had a &lt;a href=&quot;https://drupal.org/node/1615518#comment-6384738&quot;&gt;dual functionality&lt;/a&gt;, serving as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A machinery to export/import configuration from database to code. This will no longer be necessary in Drupal 8. Configuration &lt;a href=&quot;https://drupal.org/node/1762632&quot;&gt;lives in files&lt;/a&gt; and the database is used only for caching. For the same reason, an important problem in Drupal 7 development (“How do I make configuration exportable when there is no native support for Features?”) is now solved for good.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A convenient way to package and reuse configuration across multiple sites. This is extremely useful to developers and not completely covered by Configuration Management in Drupal 8, even though the basic components are there.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;poormans-features-emulating-features-in-drupal-8&quot;&gt;Poorman’s Features: emulating Features in Drupal 8&lt;/h2&gt;
&lt;p&gt;Let’s see how it is possible to package configuration and emulate, manually, what Features and Drupal 7 allow today.&lt;/p&gt;
&lt;p&gt;As an example use case, we will package a “Blog Feature”.&lt;/p&gt;
&lt;h3 id=&quot;1-create-and-export-configuration&quot;&gt;1: Create and export configuration&lt;/h3&gt;
&lt;p&gt;Create the Blog content type, configure its fields (use &lt;a href=&quot;https://drupal.org/project/kit&quot;&gt;proper naming&lt;/a&gt; for fields: &lt;code&gt;field_blog_image&lt;/code&gt; and so on) and a classic view with a block and page display. Then go to &lt;code&gt;admin/config/development&lt;/code&gt; and export your configuration into the &lt;code&gt;config.tar.gz&lt;/code&gt; file.&lt;/p&gt;
&lt;h3 id=&quot;2-package-relevant-configuration-into-a-module&quot;&gt;2: Package relevant configuration into a module&lt;/h3&gt;
&lt;p&gt;Create a simple &lt;code&gt;feature_blog&lt;/code&gt; module containing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feature_blog.info.yml&lt;/code&gt;: a basic &lt;code&gt;.info&lt;/code&gt; file in the Drupal 8 format.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Blog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;module&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;A Blog content type and related configuration.&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Features&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;8.x-0.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;core&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;8.x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feature_blog.module&lt;/code&gt;: an empty module file, needed for correct Drupal operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Drupal needs this empty file.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;config&lt;/code&gt; subdirectory: copy to this folder all relevant files from &lt;code&gt;config.tar.gz&lt;/code&gt;; filenames are quite helpful here, as it will be enough to copy all files that have “blog” in their name.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.display.comment.comment_node_blog.default.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.display.node.blog.default.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.display.node.blog.teaser.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.form_display.comment.comment_node_blog.default.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.form_display.node.blog.default.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; field.field.field_blog_image.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; field.instance.comment.comment_node_blog.comment_body.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; field.instance.node.blog.body.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; field.instance.node.blog.field_blog_image.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;   |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; node.type.blog.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   `&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; views.view.blog.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; feature_blog.info.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; feature_blog.module&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-make-the-exported-files-portable-remove-uuids&quot;&gt;3: Make the exported files portable (remove UUIDs)&lt;/h3&gt;
&lt;p&gt;The feature is ready, but it will only work if imported in the same Drupal 8 site. We need to make it portable to other sites.&lt;/p&gt;
&lt;p&gt;The main problem here is that Drupal assigns UUIDs (Universally Unique Identifiers) to each component. When you add, say, an “image” field to a content type, you create an instance of the “image” field, and to describe this relationship Drupal will rely on the UUID of the “image” field. Since UUIDs are site-specific, trying to import this configuration on another site would result in an error message like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Drupal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;field&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;FieldException&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Attempt&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; instance&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; of&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;unknown&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; field&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 84e904df&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2f14&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;46e8&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;9700&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;e00c5ca3f7d3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Drupal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;field&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Entity&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;FieldInstance&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;__construct&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;line&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 252&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; .../&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;core&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;modules&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;field&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;lib&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Drupal&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;field&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Entity&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;FieldInstance&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fortunately, in this case configuration import is designed to accept &lt;a href=&quot;https://api.drupal.org/api/drupal/core!modules!field!lib!Drupal!field!Entity!FieldInstance.php/function/FieldInstance%3A%3A__construct/8&quot;&gt;either names or UUIDs&lt;/a&gt; for describing the field this is an instance of. So it is enough to edit all files containing &lt;code&gt;field.instance&lt;/code&gt; in their name and manually replace UUIDs with machine names (or just add machine names), something like the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;-field_uuid&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;84e904df-2f14-46e8-9700-e00c5ca3f7d3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;+field_name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;field_blog_image&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that this relies on the specific handling of the &lt;a href=&quot;https://api.drupal.org/api/drupal/core!modules!field!lib!Drupal!field!Entity!FieldInstance.php/class/FieldInstance/8&quot;&gt;FieldInstance&lt;/a&gt; class and may not be a general solution.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Edit: also note (see comments) that it is important to include a hardcoded UUID if you want to avoid that your  configuration items are seen by Drupal as different items between your development and production sites, causing configuration import/export between the two to fail.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;4-import-the-feature-on-another-site&quot;&gt;4: Import the feature on another site&lt;/h3&gt;
&lt;p&gt;Our module/feature is now ready to be imported. Just copy it to the &lt;code&gt;/modules&lt;/code&gt; folder on another site, or a subfolder of it (this is now the &lt;a href=&quot;http://drupalcode.org/project/drupal.git/blob/HEAD:/modules/README.txt&quot;&gt;recommended location&lt;/a&gt; to add modules) and enable it.&lt;/p&gt;
&lt;p&gt;Your content type and configuration will be immediately available.&lt;/p&gt;
&lt;h2 id=&quot;possible-improvements&quot;&gt;Possible improvements&lt;/h2&gt;
&lt;p&gt;The Configuration Management in Drupal 8 will bring to Drupal Core a clean, unified, professional way to deal with configuration. There will still be the need for a module like Features for packaging and reusing configuration.&lt;/p&gt;
&lt;p&gt;Some implementation details of Configuration Management do not behave well in this respect: for example, UUIDs are problematic and user permissions are stored in a packaging-unfriendly way (one file per each user role, with roles identified by UUID).&lt;/p&gt;
&lt;p&gt;But, overall, the future looks bright and code-driven! And, as we have seen, it is already entirely possible to manually create basic “Features” (i.e., modules with configuration) that will work in Drupal 8.&lt;/p&gt;</content:encoded></item><item><title>Configuration Management: Drupal 7 to Drupal 8</title><link>https://www.nuvole.org/blog/configuration-management-drupal-7-drupal-8/</link><guid isPermaLink="true">https://www.nuvole.org/blog/configuration-management-drupal-7-drupal-8/</guid><pubDate>Fri, 06 Jun 2014 20:23:29 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This is a preview of Nuvole’s training at DrupalCon Amsterdam: “&lt;a href=&quot;https://amsterdam2014.drupal.org/node/2808&quot;&gt;An Effective Development Workflow in Drupal 8&lt;/a&gt;”.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Nuvole gave two talks about the current status of Configuration Management in Drupal 8 at European Drupal events in the last few weeks: &lt;a href=&quot;http://milano2014.drupaldays.it/schedule#conferenza_gestione-della-configurazione-drupal-8&quot;&gt;Drupal Days Milan 2014&lt;/a&gt; and &lt;a href=&quot;http://2014.drupalalpeadria.org/session/configuration-management-drupal-8&quot;&gt;Drupal Camp Alpe Adria 2014&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Developers attending the events were mostly interested in how the future Drupal 8 Configuration Management capabilities will compare to Drupal 7, with and without the Features module (or, in general, with and without what we call the &lt;a href=&quot;https://nuvole.org/blog/code-driven-development&quot;&gt;code-driven workflow&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Here is a comparison based on the current status of Drupal 8. Please note that CMI, the Configuration Management Initiative, is still under active development, and for example a couple issues mentioned in the slides have already been resolved, shortly after we gave our presentations. The very basic concepts, though, remain unchanged from our &lt;a href=&quot;https://nuvole.org/blog/2013/sep/03/configuration-management-and-features-look-drupal-8&quot;&gt;September 2013 post&lt;/a&gt;, so you might want to review that one if you have never heard about CMI before.&lt;/p&gt;
&lt;h3 id=&quot;configuration-management-key-differences-between-drupal-7-and-drupal-8&quot;&gt;Configuration Management: Key differences between Drupal 7 and Drupal 8&lt;/h3&gt;
&lt;h4 id=&quot;configuration-is-well-defined&quot;&gt;Configuration is well-defined&lt;/h4&gt;
&lt;p&gt;In Drupal 7 it isn’t always clear-cut whether something belongs in the “configuration” or “content” realm. Are vocabularies configuration? Are taxonomy terms configuration? And what about a taxonomy term whose ID is used as a contextual filter in a View? Several approaches to this are possible, like &lt;a href=&quot;https://nuvole.org/blog/2012/feb/07/hard-and-soft-configuration-drupal-distributions&quot;&gt;soft configuration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In Drupal 8, configuration will be less subjective: Drupal has an “Export Configuration” function that exports &lt;strong&gt;all&lt;/strong&gt; the site configuration; if something is not there, it is not considered to be configuration. Concepts like soft configuration still apply, but at least it’s clear what is configuration and is not.&lt;/p&gt;
&lt;h4 id=&quot;configuration-is-stored-in-files&quot;&gt;Configuration is stored in files&lt;/h4&gt;
&lt;p&gt;Drupal 7, generally, stores configuration in the database, together with content. Only a little subset of configuration is read from PHP files.
In Drupal 8 configuration will live in text files. Then, for several reasons (performance, safety, security), those files &lt;a href=&quot;https://drupal.org/node/2161591&quot;&gt;will actually be stored in the database&lt;/a&gt; by default (one of the most visible recent changes), but will still be accessed and managed as files.&lt;/p&gt;
&lt;h4 id=&quot;unified-format-and-approach-to-configuration&quot;&gt;Unified format and approach to configuration&lt;/h4&gt;
&lt;p&gt;A particularly annoying issue with Drupal 7 is that modules are free to set their own standards for storing configuration, so it is common to find sites where configuration is scattered among variables, database tables, objects exported via CTools, Features, and other places. Modules like Features at times need some “black magic” to guess where and how relevant configuration is stored.&lt;/p&gt;
&lt;p&gt;The unified approach in Drupal 8, where all configuration is in the form of text files in the YAML format, is a great step forward. It removes the guesswork needed by Features in Drupal 7 to understand how to find and export components.&lt;/p&gt;
&lt;h4 id=&quot;staging-configuration&quot;&gt;Staging configuration&lt;/h4&gt;
&lt;p&gt;In Drupal 7 you can have “staged configuration” through Features and the Features revert operation. Drupal 8 will introduce, out of the box, an “Import Configuration” facility that you can use to import either the whole site configuration or a single configuration file. The approaches are mostly equivalent, but on one side Drupal 7 + Features offers better packaging functionality, on the other side Drupal 8 will allow to import a &lt;strong&gt;complete&lt;/strong&gt; site configuration (all variables, permissions…), something that is highly unpractical to do with Drupal 7 and Features.&lt;/p&gt;
&lt;h4 id=&quot;interface-to-developers&quot;&gt;Interface to developers&lt;/h4&gt;
&lt;p&gt;From a developer’s point of view, writing PHP code to access and/or set a component’s configuration in Drupal 7 is tricky, since you don’t have a global interface, let alone an “entry point” that allows you to explore all the site configuration: you are forced to use a mixed bag of tricks ranging from &lt;code&gt;variable_get()&lt;/code&gt; and &lt;code&gt;variable_set()&lt;/code&gt; to database queries, to Ctools hooks for making configuration exportable and so on.&lt;/p&gt;
&lt;p&gt;Drupal 8 will bring a very welcome improvement by providing a dedicated type for configuration entities and the unified interface &lt;code&gt;Drupal::config($name)&lt;/code&gt; for getting and setting all configuration values: as a typical example &lt;code&gt;Drupal::config(&apos;system.site&apos;)-&gt;get(&apos;name&apos;)&lt;/code&gt; will return the site name, and the same pattern can be used for everything.&lt;/p&gt;
&lt;h3 id=&quot;the-shootout-drupal-7-vs-drupal-7--features-vs-drupal-8&quot;&gt;The shootout: Drupal 7 vs Drupal 7 + Features vs Drupal 8&lt;/h3&gt;
&lt;p&gt;A quick feature comparison table between Drupal 7 core, Drupal 7 + Features and the current state of Drupal 8. As you see, a developer that is now using Drupal 7 without features will see spectacular advantages in Drupal 8, while experienced users of Drupal 7 and Features will still miss something.&lt;/p&gt;



























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Functionality&lt;/th&gt;&lt;th&gt;D7 Core&lt;/th&gt;&lt;th&gt;D7 Core + Features&lt;/th&gt;&lt;th&gt;D8 Core (current)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Export full site config (no content)&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Export selected config items&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Track config changes (full site)&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Track config changes (selected items)&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Stage configuration&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Package configuration&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Reuse configuration in other projects&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Collaborate on the same project&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h4 id=&quot;what-is-still-missing&quot;&gt;What is still missing?&lt;/h4&gt;
&lt;p&gt;It’s important to understand the use case for CMI. The stated aim for CMI is not to replace Features. CMI aims at making it much simpler to transport configuration changes from development to production, and this aim was reached.&lt;/p&gt;
&lt;p&gt;All other benefits of Features are out of scope for CMI. These include: packaging configuration, reusing configuration in other projects (and not simply moving it between the development and production version of the same site) and enabling real-time team collaboration (developer A and developer B build two separate features for the same site, like the News and Blog sections, simultaneously). So there is definitely room for a &lt;a href=&quot;https://drupal.org/project/features&quot;&gt;Drupal 8 version of Features&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;is-cmi-features-done-right&quot;&gt;Is CMI “Features Done Right”?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt; It is a nice way to replace and improve one use case for Features: making configuration exportable into text files.&lt;/p&gt;
&lt;h4 id=&quot;is-cmi-features-done-wrong&quot;&gt;Is CMI “Features Done Wrong”?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt; It is a huge step forward to developers and it paves the way for additional modules that could offer the same functionality of Drupal 7 + Features in a much cleaner and more reliable way.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;See full versions of our Milan and Alpe Adria presentations as a Slideshare embeds or PDF files, below.&lt;/em&gt;&lt;/p&gt;
  &lt;div&gt; &lt;strong&gt; &lt;a href=&quot;https://www.slideshare.net/nuvoleweb/drupal-8cmipreviewen&quot; title=&quot; A preview (DrupalDays Milano 2014)&quot; target=&quot;_blank&quot;&gt;Configuration Management in Drupal 8: A preview (DrupalDays Milano 2014)&lt;/a&gt; &lt;/strong&gt; &lt;br&gt;from &lt;strong&gt;&lt;a href=&quot;http://www.slideshare.net/nuvoleweb&quot; target=&quot;_blank&quot;&gt;Nuvole&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;
  &lt;div&gt; &lt;strong&gt; &lt;a href=&quot;https://www.slideshare.net/nuvoleweb/cmi-d7tod8dcaa140517&quot; title=&quot; A preview (DrupalCamp Alpe Adria 2014)&quot; target=&quot;_blank&quot;&gt;Configuration Management in Drupal 8: A preview (DrupalCamp Alpe Adria 2014)&lt;/a&gt; &lt;/strong&gt; &lt;br&gt;from &lt;strong&gt;&lt;a href=&quot;http://www.slideshare.net/nuvoleweb&quot; target=&quot;_blank&quot;&gt;Nuvole&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;
&lt;p&gt;Milano-2014-drupal-8-cmi-preview-en.pdf (430.48 KB) &lt;a href=&quot;/sites/default/files/Milano-2014-drupal-8-cmi-preview-en.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AlpeAdria-2014-cmi-d7-to-d8—dcaa-140517.pdf (774.4 KB) &lt;a href=&quot;/sites/default/files/AlpeAdria-2014-cmi-d7-to-d8--dcaa-140517.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Packaging and reusing configuration in Drupal 8</title><link>https://www.nuvole.org/blog/packaging-and-reusing-configuration-drupal-8/</link><guid isPermaLink="true">https://www.nuvole.org/blog/packaging-and-reusing-configuration-drupal-8/</guid><pubDate>Tue, 15 Jul 2014 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This is a preview of Nuvole’s training at DrupalCon Amsterdam: &lt;a href=&quot;https://amsterdam2014.drupal.org/node/2808&quot;&gt;An Effective Development Workflow in Drupal 8&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Configuration Management in Drupal 8 elegantly solves staging configuration between different environments addressing an issue that is still haunting even the most experienced Drupal 7 developer. In earlier posts we covered &lt;a href=&quot;https://nuvole.org/blog/2014/jun/06/configuration-management-drupal-7-drupal-8&quot;&gt;the new Configuration Management in Drupal 8&lt;/a&gt;, seeing how it compares to Drupal 7 and Features, and even investigated &lt;a href=&quot;https://nuvole.org/blog/2013/sep/03/configuration-management-and-features-look-drupal-8&quot;&gt;how to manually simulate Features&lt;/a&gt; in Drupal 8 last year. Recent developments and contrib modules can take us several steps closer.&lt;/p&gt;
&lt;h3 id=&quot;packaging-configuration&quot;&gt;Packaging configuration&lt;/h3&gt;
&lt;p&gt;Developers familiar with &lt;a href=&quot;https://nuvole.org/blog/code-driven-development&quot;&gt;code-driven development practices&lt;/a&gt; need an equivalent in Drupal 8 + &lt;a href=&quot;https://github.com/drush-ops/drush&quot;&gt;Drush 7&lt;/a&gt; to what the Features module does in Drupal 7 with its &lt;code&gt;features-update&lt;/code&gt; and &lt;code&gt;features-revert&lt;/code&gt; Drush commands.&lt;/p&gt;
&lt;p&gt;While Drupal 8 configuration staging capabilities are far more advanced than what Features could possibly provide, what the new Configuration Management system really lacks is the ability to package configuration.&lt;/p&gt;
&lt;h3 id=&quot;enter-the-configuration-development-module&quot;&gt;Enter the Configuration development module&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.drupal.org/project/config_devel&quot;&gt;Configuration development&lt;/a&gt; module, currently maintained by &lt;a href=&quot;https://www.drupal.org/u/chx&quot;&gt;chx&lt;/a&gt;, serves two main purposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It automates the import of specified configuration files into the active storage.&lt;/li&gt;
&lt;li&gt;It automates the export of specified configuration objects into files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The module offers a simple, global UI interface where a Drupal developer can set which configuration is automatically exported and imported any time they hit the “Save” button on a configuration setting page.&lt;/p&gt;
&lt;p&gt;In order to achieve a more modular configuration packaging it would be enough to set a specific module’s &lt;code&gt;config/install&lt;/code&gt; directory as the actual export destination.&lt;/p&gt;
&lt;p&gt;Nuvole &lt;a href=&quot;https://www.drupal.org/node/2300717&quot;&gt;contributed a patch&lt;/a&gt; (&lt;em&gt;EDIT: now integrated&lt;/em&gt;) to make that possible: instead of firing an auto-export every time a “Save” button is clicked the developer can, instead, specify in the module’s info file which configuration needs to be written back to that module’s install directory and run a simple Drush command to do that.&lt;/p&gt;
&lt;h3 id=&quot;reusable-features-in-drupal-8&quot;&gt;Reusable “features” in Drupal 8&lt;/h3&gt;
&lt;p&gt;One of the main advantages of having a standardized way of dealing with configuration means that modules can now stage configuration at installation time. In a way that’s something very close to what Features allowed us to do in Drupal 7.&lt;/p&gt;
&lt;p&gt;Say we have our news section up and running on the site we are currently working on and we would like to package it into a custom module, together with some other custom code, and ship it over a new project. The patched Config development module will help us to do just that! Here it is how:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Download, patch and enable Configuration development module&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We need to download and enable the Configuration development module and apply the  patch (&lt;em&gt;EDIT: already integrated as of 8.x-1.0-alpha7&lt;/em&gt;) attached to &lt;a href=&quot;https://www.drupal.org/node/2300717&quot;&gt;this Drupal.org issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After rebuilding the cache, we will have the &lt;code&gt;config-writeback&lt;/code&gt; Drush command available. Let’s have a closer look at what it is meant to do:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; help&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config-writeback&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Write&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; back&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; configuration&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; a&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; module&apos;s config/install directory. State which configuration settings you want to export in the module&apos;s&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; info&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; file&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; by&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; listing&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; them&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; under&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;config_devel&apos;,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; shown&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; below:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;config_devel:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.view_display.node.article.default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.view_display.node.article.teaser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; field.instance.node.article.body&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Examples:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config-writeback&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; MODULE_NAME&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        Write&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; back&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; configuration&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; specified&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; module,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; based&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; on&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; .info&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; file.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Arguments:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                                    Module&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; machine&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; name.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Aliases:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cwb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Find what configuration needs to be packaged&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We now look for all configuration related to our site’s news section. In Drupal 8 most of the site configuration is namespaced with related components so, if we keep on using consistent naming conventions, we can easily list all news-related configuration by simply running:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config-list&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; news&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;entity.form_display.node.news.default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;entity.view_display.node.news.default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;entity.view_display.node.news.teaser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;field.instance.node.news.body&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;image.style.news_medium&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;menu.entity.node.news&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;node.type.news&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Package configuration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To package all the settings above we will create a module called &lt;code&gt;custom_news&lt;/code&gt; and, in its info file, we will specify all the settings we want to export, listing them under the &lt;code&gt;config_devel:&lt;/code&gt; directive, as follows:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;$ cat modules/custom_news/custom_news.info.yml&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Custom News&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;module&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Custom news module.&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Custom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;core&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;8.x&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;config_devel&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;entity.form_display.node.news.default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;entity.view_display.node.news.default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;entity.view_display.node.news.teaser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;field.instance.node.news.body&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;image.style.news_medium&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;menu.entity.node.news&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;node.type.news&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After enabling the module we will run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config-writeback&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; custom_news&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we will have all our settings exported into the module’s install directory:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; tree&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -L&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; modules/custom_news/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;modules/custom_news/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   └──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       ├──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.view_display.node.news.default.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       ├──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; entity.view_display.node.news.teaser.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       ├──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; field.instance.node.news.body.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       ├──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; image.style.news_medium.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       ├──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; menu.entity.node.news.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       └──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; node.type.news.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; custom_news.info.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Drush command above takes care of clearing all sensitive UUID values making sure that the module will stage the exported configuration cleanly, once enabled on a new Drupal 8 site.&lt;/p&gt;
&lt;p&gt;To get the news section on another site we will just copy the module to the new site’s &lt;code&gt;./modules/&lt;/code&gt; directory and enable it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; en&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; custom_news&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; following&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; extensions&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; will&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; be&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; enabled:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; custom_news&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Do&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; you&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; really&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; want&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; continue?&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (y/n): y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;custom_news&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; was&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; enabled&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; successfully.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;final-evaluation-drupal-7-versus-drupal-8&quot;&gt;Final evaluation: Drupal 7 versus Drupal 8&lt;/h3&gt;
&lt;p&gt;One of the main differences between working in Drupal 7 and in Drupal 8 is represented by the new Configuration Management system.&lt;/p&gt;
&lt;p&gt;While Features was proposing a one-stop solution for both configuration staging and packaging, Drupal 8 CM does a better job in keeping them separate, allowing developers in taking a greater control over these two different and, at the same time, complementary aspect of a solid Drupal development workflow.&lt;/p&gt;
&lt;p&gt;By using the method described above we can upgrade our comparison table between Drupal 7 and Drupal 8 introduced in &lt;a href=&quot;https://nuvole.org/blog/2014/jun/06/configuration-management-drupal-7-drupal-8&quot;&gt;one of our previous posts&lt;/a&gt; as follows:&lt;/p&gt;




































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Functionality&lt;/th&gt;&lt;th&gt;D7 Core&lt;/th&gt;&lt;th&gt;D7 Core + Features&lt;/th&gt;&lt;th&gt;D8 Core (current)&lt;/th&gt;&lt;th&gt;D8 Core (current) + Config Devel&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Export full site config (no content)&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Export selected config items&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Track config changes (full site)&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Track config changes (selected items)&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Stage configuration&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Package configuration&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Reuse configuration in other projects&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Collaborate on the same project&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;NO&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The last “NO” deserves a brief explanation: Configuration Management allows two developers to work simultaneously on different parts of the same project if they are very careful: but “merging” the work would have to be done by version control (GIT or similar), that doesn’t know about YAML or Drupal.&lt;/p&gt;
&lt;h3 id=&quot;some-open-issues&quot;&gt;Some open issues&lt;/h3&gt;
&lt;p&gt;Contributed modules seem to be the best way to enhance the core Configuration Management system, much like what happened with Drupal 7 and Features. There are still several issues that should be considered for an optimal workflow, to match and improve what we already have in Drupal 7:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Piping&lt;/strong&gt;: the ability to relate configuration components based on both hard and logic dependencies, for example: I export a content type and, automatically, I get also its fields. If piping might have been too rigid, at times, it would be still useful to have in some configurable form.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced configuration diff&lt;/strong&gt;: it might be useful to have the possibility to review what configuration is going to be installed before enabling a module, like it is now when importing staged configuration to the active storage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Granularity&lt;/strong&gt;: it is still impossible to export part of a configuration file, so we still depend on the core conventions for grouping configuration into files, and we can’t export a single permission for example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ownership&lt;/strong&gt;: we can’t know if another module (or “feature”) is tracking a component we wish to track; this could be useful in the perspective of maintaining several “modular” features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Updates&lt;/strong&gt;: we can reuse configuration by simply enabling a module, but this holds only for the initial installation; after a module is enabled, we don’t have a clean way to import changes (say, to “upgrade” to a newer version of the feature) outside the standard workflow foreseen in Configuration Management.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Git workflow for managing Drupal 8 configuration</title><link>https://www.nuvole.org/blog/git-workflow-managing-drupal-8-configuration/</link><guid isPermaLink="true">https://www.nuvole.org/blog/git-workflow-managing-drupal-8-configuration/</guid><pubDate>Wed, 20 Aug 2014 17:30:26 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This is a preview of Nuvole’s training at DrupalCon Amsterdam: &lt;a href=&quot;https://amsterdam2014.drupal.org/node/2808&quot;&gt;An Effective Development Workflow in Drupal 8&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One of the new key features of Drupal 8 is the possibility to &lt;a href=&quot;https://nuvole.org/blog/2014/jun/06/configuration-management-drupal-7-drupal-8&quot;&gt;deal with configuration in code&lt;/a&gt;. Since configuration is now in text files, we want to put it under version control in Git to enjoy the many advantages this brings: comparing configuration states, keeping a history of configuration changes and even moving configuration between sites.&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;We will assume that you have a development version of Drupal 8, git and drush available on your system.
You can set up your Drupal git repository in several ways. One of them is outlined in &lt;a href=&quot;https://www.drupal.org/node/803746&quot;&gt;Building a Drupal site with Git&lt;/a&gt; on drupal.org. The document is written for Drupal 7, but can easily be adapted for Drupal 8.&lt;br&gt;
Another, probably simpler method is to simply download a Drupal 8 (alpha) release and initialise a new repository with it.&lt;/p&gt;
&lt;p&gt;In either case you should copy &lt;code&gt;example.gitignore&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt; and adapt it to your needs to prevent &lt;code&gt;settings.php&lt;/code&gt; and the files directory from being versioned.&lt;/p&gt;
&lt;p&gt;The next step is to make sure that a configuration directory is versionable. By default Drupal 8 will place the staging directory under &lt;code&gt;sites/default/files&lt;/code&gt; and it is considered a good practice to not version that location, but an alternative location can easily be specified in &lt;code&gt;settings.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$config_directories[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;staging&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;config/staging&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is also possible and even advisable to specify a directory outside of the web root of course. In that case you would put the parent directory of your web root where drupal is under version control and use &lt;code&gt;../config/staging&lt;/code&gt;. We will later see that it is also possible to add more directories and keys to the &lt;code&gt;$config_directories&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;Because the configuration management of Drupal 8 only works between different instances of the &lt;strong&gt;same&lt;/strong&gt; site, the different instances of the site need to be cloned. Cloning a Drupal 8 site is done the same way as cloning a Drupal 7 site. Just dump the database of the site to clone and import it in the other environment.&lt;/p&gt;
&lt;h2 id=&quot;development&quot;&gt;Development&lt;/h2&gt;
&lt;p&gt;After cloning your site you can go ahead and start configuring your site.&lt;br&gt;
Once the part of the configuration you were working on is done the whole configuration of the site needs to be exported.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;local$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config-export&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; staging&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; current&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; contents&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; your&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; export&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; directory&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (config/staging) will be deleted. (&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;y/n&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;): y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Configuration&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; successfully&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; exported&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config/staging.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you need to merge the work of other developers. In some cases it may be enough to simply use &lt;code&gt;git pull&lt;/code&gt;, otherwise the configuration has to be merged after it has been committed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Add all configuration to git and commit it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use git pull (or git fetch and git merge) and resolve any conflicts if necessary.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git can merge changes in text files quite well, but git does not know about Drupal and its yaml format for configuration. It is, therefore, important to verify that the merged configuration makes sense and is valid. In most cases it will probably not be an issue and just work, but it is always better to be vigilant and be on the safe side. So, after merging, you should always run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;local$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config-import&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; staging&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the import went smooth you can push the configuration to the remote repository. Otherwise the configuration needs to be fixed first.&lt;/p&gt;
&lt;h2 id=&quot;deployment&quot;&gt;Deployment&lt;/h2&gt;
&lt;p&gt;The simplest case is when the configuration on the production site has not been changed. There is an interesting &lt;a href=&quot;https://www.drupal.org/project/config_readonly&quot;&gt;Configuration Read-only mode&lt;/a&gt; module that can enforce this.&lt;/p&gt;
&lt;p&gt;If the configuration did not change deploying the new configuration is simply:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;remote$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pull&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;remote$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config-import&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; staging&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the configuration changes on the production site, it is best to frequently export the live configuration into a dedicated directory.&lt;br&gt;
Add a new config directory in &lt;code&gt;settings.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$config_directories[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;to_dev&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;config/to_dev&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;remote$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; drush&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config-export&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to_dev&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add, commit and push it to the production branch so that the developers can deal with it and integrate the changes into the configuration which will be deployed next. Exporting the configuration into a dedicated directory rather than the staging directory avoids the danger that merge conflicts happen on the production site.
The deployment to the production site should be kept hassle free, so it should always be safe to pull from git and import the configuration without the risk of a conflict.&lt;/p&gt;
&lt;h2 id=&quot;important-notes&quot;&gt;Important notes&lt;/h2&gt;
&lt;p&gt;It is important to &lt;em&gt;first&lt;/em&gt; &lt;strong&gt;export&lt;/strong&gt; the configuration changes and &lt;em&gt;then&lt;/em&gt; &lt;strong&gt;pull&lt;/strong&gt; changes from collaborators because the exporting action wipes the directory and re-populates it with the active configuration. Since everything is in git, you can recover from such a mistake without much difficulty but why make your life complicated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Import&lt;/strong&gt; the configuration &lt;em&gt;before&lt;/em&gt; &lt;strong&gt;pushing&lt;/strong&gt; it to the remote repository. Broken configuration breaks the site, be a nice co-worker.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Git doesn’t solve everything!&lt;/em&gt; Imagine Alice and Bob start with the same site, it has one content type and among others an “attachment” field. Alice deletes the attachment field, exports the configuration and pushes it to git. In the meantime, Bob creates a new content type and adds the attachment field to it. Bob exports his configuration, merges Alice’s configuration changes without a problem (the changes are separate files) and imports the merged configuration. The attentive reader sees where this leads. The commit of Alice deletes the field storage for the attachment field, but Bob added a field instance which depends on the field storage.
The exported configuration now contains a field instance that can’t be imported.&lt;br&gt;
At the time of writing, drush will signal a successful import but doesn’t actually import it while the UI is more helpful and complains that the attachment field instance was not imported due to the missing field storage.&lt;/p&gt;</content:encoded></item><item><title>Drupal 7 to Drupal 8: The Cheat Sheet</title><link>https://www.nuvole.org/blog/drupal-7-drupal-8-cheat-sheet/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-7-drupal-8-cheat-sheet/</guid><pubDate>Wed, 01 Oct 2014 09:31:28 GMT</pubDate><content:encoded>&lt;p&gt;Our new &lt;a href=&quot;https://amsterdam2014.drupal.org/node/2808&quot;&gt;An Effective Development Workflow in Drupal 8&lt;/a&gt; training just made its debut at DrupalCon Amsterdam. An important part of the training is dedicated to shorten the trial-and-error period developers necessarily need to go through when converting their practices from Drupal 7 to Drupal 8: we want that proficient Drupal 7 (and Features) developers with zero, or very limited, Drupal 8 knowledge, gain a rather comprehensive knowledge of Drupal 8 (focused on configuration management) that allows them to be immediately productive when Drupal 8 is released.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;2014-09-29 12.51.50_0.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;As a gift to those who couldn’t attend the training because it was sold out, and to all lovers of Drush, Features and code-driven development in general, we are sharing a “Drupal 7 to Drupal 8” cheatsheet that was part of the training materials. Please find it attached to this post, in PDF format.&lt;/p&gt;
&lt;a href=&quot;/sites/default/files/Drupal-7-to-Drupal-8-Cheatsheet.pdf&quot; target=&quot;_blank&quot; title=&quot;Drupal 7 to Drupal 8 Cheat Sheet&quot;&gt;
&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;/sites/default/files/Drupal-7-to-Drupal-8-Cheatsheet.pdf&quot; target=&quot;_blank&quot; title=&quot;Drupal 7 to Drupal 8 Cheat Sheet&quot;&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;cheatsheet-blog-screenshot.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We still have some printed copies available to give away; find us at DrupalCon and feel free to ask us for one!&lt;/p&gt;
&lt;p&gt;Drupal-7-to-Drupal-8-Cheatsheet.pdf (999.82 KB) &lt;a href=&quot;/sites/default/files/Drupal-7-to-Drupal-8-Cheatsheet.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Atrium Folders for Open Atrium 2</title><link>https://www.nuvole.org/blog/atrium-folders-open-atrium-2/</link><guid isPermaLink="true">https://www.nuvole.org/blog/atrium-folders-open-atrium-2/</guid><pubDate>Mon, 08 Dec 2014 17:19:53 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;oa_folders_example.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;We received many requests to make an updated version of our Atrium Folders feature available for the latest version of &lt;a href=&quot;http://openatrium.com&quot;&gt;Open Atrium&lt;/a&gt;, the excellent Drupal-based solution for Intranets developed by Phase2.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://oecd.org&quot;&gt;OECD&lt;/a&gt; sponsored the development of the new version as an open source project, in order to add a file management functionality to the &lt;a href=&quot;https://innovationpolicyplatform.org/&quot;&gt;Innovation Policy Platform&lt;/a&gt; site that it manages together with the &lt;a href=&quot;http://www.worldbank.org/&quot;&gt;World Bank&lt;/a&gt;. Atrium Folders for Open Atrium 2 is thus now available to everybody.&lt;/p&gt;
&lt;h2 id=&quot;the-usual-features-a-new-way&quot;&gt;The usual features, a new way&lt;/h2&gt;
&lt;p&gt;Open Atrium changed completely and so did Atrium Folders. There are many differences under the hood, with a complete code rewrite, but the familiar user experience is still there.&lt;/p&gt;
&lt;h3 id=&quot;uploading-and-downloading-files&quot;&gt;Uploading and downloading files&lt;/h3&gt;
&lt;p&gt;Creating folders and adding files to the folders is as easy as creating any other content in Open Atrium 2: it is enough to create “Files sections”. When you are viewing a folder, specific buttons allow to create subfolders,  upload files and directly download any file.&lt;/p&gt;
&lt;h3 id=&quot;access-management&quot;&gt;Access management&lt;/h3&gt;
&lt;p&gt;The access management works like for other nodes in Open Atrium. Access to the folder can be restricted for both viewing and editing separately, and it can be determined at a folder level.&lt;/p&gt;
&lt;h3 id=&quot;notifications&quot;&gt;Notifications&lt;/h3&gt;
&lt;p&gt;The notification system of Open Atrium can also be used for folders. Users can be informed when new files are added, with the same interface used by other Open Atrium features.&lt;/p&gt;
&lt;h2 id=&quot;and-much-more&quot;&gt;And much more&lt;/h2&gt;
&lt;h3 id=&quot;media-module-support&quot;&gt;Media module support&lt;/h3&gt;
&lt;p&gt;The files are attached with the &lt;a href=&quot;https://www.drupal.org/project/media&quot;&gt;Media&lt;/a&gt; widget and it is thus possible to manage not only files, but everything Media supports, like for example YouTube videos or files attached to other content.&lt;/p&gt;
&lt;h3 id=&quot;multiple-uploads-with-drag-and-drop-support&quot;&gt;Multiple uploads, with drag and drop support&lt;/h3&gt;
&lt;p&gt;The multi-upload feature of Open Atrium 2 can also be used with Folders to upload several files at the same time. Drag and drop uploads are supported too.&lt;/p&gt;
&lt;h3 id=&quot;download-folder-as-zip-file&quot;&gt;Download folder as ZIP file&lt;/h3&gt;
&lt;p&gt;The download button for files exists also for folders and it allows to download a folder with its subfolders and all included files as a ZIP archive. This functionality is available as a submodule, bundled with Atrium Folders.&lt;/p&gt;
&lt;h3 id=&quot;file-and-folder-revisions&quot;&gt;File and folder revisions&lt;/h3&gt;
&lt;p&gt;Atrium Folders supports history and revisions both for folders and files. You can view previous versions of a file and optionally restore an older version. This functionality is available as a submodule, bundled with Atrium Folders.&lt;/p&gt;
&lt;h2 id=&quot;download-installation-and-support&quot;&gt;Download, installation and support&lt;/h2&gt;
&lt;p&gt;The Open Atrium Folders feature can be downloaded and installed like any other module.&lt;/p&gt;
&lt;p&gt;It is available on drupal.org at &lt;a href=&quot;https://www.drupal.org/project/oa_folders/&quot;&gt;https://www.drupal.org/project/oa_folders/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Please report any issues in the module’s issue queue at drupal.org&lt;/p&gt;</content:encoded></item><item><title>A pluggable field-handling system for the Behat Drupal Extension</title><link>https://www.nuvole.org/blog/pluggable-field-handling-system-behat-drupal-extension/</link><guid isPermaLink="true">https://www.nuvole.org/blog/pluggable-field-handling-system-behat-drupal-extension/</guid><pubDate>Tue, 24 Mar 2015 16:15:00 GMT</pubDate><content:encoded>&lt;p&gt;On of the main goal of &lt;strong&gt;BDD (Behaviour Driven Development)&lt;/strong&gt; is to be able to describe a system’s behavior using a single notation, in order to be directly accessible by product owners and developers and testable using automatic conversion tools.&lt;/p&gt;
&lt;p&gt;In the PHP world, &lt;strong&gt;&lt;a href=&quot;http://docs.behat.org/en/latest&quot;&gt;Behat&lt;/a&gt;&lt;/strong&gt; is the tool of choice. Behat allows to write test scenarios using &lt;a href=&quot;http://docs.behat.org/en/latest/guides/1.gherkin.html#steps&quot;&gt;Gherkin step definitions&lt;/a&gt; and it generates the corresponding PHP code to actually run and test the defined scenarios.&lt;/p&gt;
&lt;p&gt;Thanks to the excellent &lt;strong&gt;&lt;a href=&quot;https://www.drupal.org/project/drupalextension&quot;&gt;Behat Drupal Extension&lt;/a&gt;&lt;/strong&gt; Drupal developers have been able to enjoy the benefits of Behavioral Driven Development for quite some time.&lt;/p&gt;
&lt;p&gt;Essentially the project provides an integration between Drupal and Behat allowing the usage of Drupal-specific Gherkin step definitions. For example, writing a scenario that tests node authorship would look like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;gherkin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Scenario&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Create nodes with specific authorship&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  Given &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;users:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | name     | mail            | status |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | Joe User | joe@example.com | 1      |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  And &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;article&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | title          | author   | body             |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | Article by Joe | Joe User | PLACEHOLDER BODY |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  When &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I am logged in as a user with the &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;administrator&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; role&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  And &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I am on the homepage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  And &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I follow &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Article by Joe&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  Then &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I should see the link &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Joe User&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;dealing-with-complex-content-types&quot;&gt;Dealing with complex content types&lt;/h3&gt;
&lt;p&gt;The Gherkin scenario above is pretty straightforward and it gets the job done for simple cases. In a real-life situation, though, it’s very common to have content types with a high number of fields, often of different types and, possibly, referencing other entities.&lt;/p&gt;
&lt;p&gt;The following scenario might be a much more common situation for a Drupal developer:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;gherkin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Scenario&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Reference site pages from within a &quot;Post&quot; node&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  Given &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;page&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    | title      |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    | Page one   |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    | Page two   |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    | Page three |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  When &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I am viewing a &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    | title                | Post title         |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    | body                 | PLACEHOLDER BODY   |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    | field_post_reference | Page one, Page two |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  Then &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I should see &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Page one&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  And &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I should see &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Page two&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While it is always possible to implement project specific step-definition, as show on &lt;a href=&quot;https://gist.github.com/jhedstrom/5708233&quot;&gt;this Gist dealing with field collections and entity references&lt;/a&gt;, having to do that for every specific content type might be an unnecessary burden.&lt;/p&gt;
&lt;h3 id=&quot;introducing-field-handling-for-the-behat-drupal-extension&quot;&gt;Introducing field-handling for the Behat Drupal Extension&lt;/h3&gt;
&lt;p&gt;Nuvole recently contributed a field-handling system that would allow the scenario above to be ran out of the box, without having to implement any custom step definition, working both in Drupal 7 and Drupal 8. The idea behind it is to allow a Drupal developer to work with fields when writing Behat test scenarios, regardless of the entity type or of any field-specific implementation.&lt;/p&gt;
&lt;p&gt;The code is currently available on the master branches of both the &lt;a href=&quot;https://github.com/jhedstrom/drupalextension&quot;&gt;Behat Drupal Extension&lt;/a&gt; and the &lt;a href=&quot;https://github.com/jhedstrom/DrupalDriver&quot;&gt;Drupal Driver&lt;/a&gt; projects, if you want to try it out follow the instructions at &lt;a href=&quot;https://behat-drupal-extension.readthedocs.org/en/3.0/localinstall.html&quot;&gt;“Stand-alone installation”&lt;/a&gt; and make sure to grab the right code by specifying the right package versions in your &lt;code&gt;composer.json&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;require&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;drupal/drupal-extension&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;3.0.*@dev&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;drupal/drupal-driver&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;1.1.*@dev&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The field-handling system provides an integration with several highly-used field types, like:&lt;/p&gt;
&lt;h4 id=&quot;date-fields&quot;&gt;Date fields&lt;/h4&gt;
&lt;p&gt;Date field values can be included in a test scenario by using the following notation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single date field value can be expressed as &lt;code&gt;2015-02-08 17:45:00&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Start and end date are separated by a dash &lt;code&gt;-&lt;/code&gt;, for ex. &lt;code&gt;2015-02-08 17:45:00 - 2015-02-08 19:45:00&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Multiple date field values are separated by a comma &lt;code&gt;,&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, the following Gherkin notation will create a node with 3 date fields:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;gherkin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;When &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I am viewing a &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | title       | Post title                                |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_date1 | 2015-02-08 17:45:00                       |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_date2 | 2015-02-08 17:45:00, 2015-02-09 17:45:00  |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_date3 | 2015-02-08 17:45:00 - 2015-02-08 19:45:00 |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;entity-reference-fields&quot;&gt;Entity reference fields&lt;/h4&gt;
&lt;p&gt;Entity reference field values can be expressed by simply specifying the referenced entity’s label field (e.g. the node’s title or the term’s name). Such an approach wants to keep up with BDD’s promise: i.e. describing the system behavior by abstracting, as much as possible, any internal implementation.&lt;/p&gt;
&lt;p&gt;For example, to reference to a content item with title “Page one” we can simply write:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;gherkin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;When &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I am viewing a &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | title           | Post title |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_reference | Page one   |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or, in case of multiple fields, titles will be separated by a comma:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;gherkin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;When &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I am viewing a &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | title           | Post title         |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_reference | Page one, Page two |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;link-fields&quot;&gt;Link fields&lt;/h4&gt;
&lt;p&gt;A Link field in Drupal offers quite a wide range of options, such as an optional link title or internal/external URLs. We can use the following notation to work with links in our test scenarios:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;gherkin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;When &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I am viewing a &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | title       | Post title                                              |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_link1 | https://nuvole.org                                       |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_link2 | Link 1 - https://nuvole.org                              |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_link3 | Link 1 - https://nuvole.org, Link 2 - http://example.com |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see we use always the same pattern: a dash &lt;code&gt;-&lt;/code&gt; to separate parts of the same field value and a comma &lt;code&gt;,&lt;/code&gt; to separate multiple field values.&lt;/p&gt;
&lt;h4 id=&quot;text-fields-with-select-widget&quot;&gt;Text fields with “Select” widget&lt;/h4&gt;
&lt;p&gt;We can also refer to a select list value by simply referring to its label, so to have much more readable test scenarios. For example, given the following allowed values for a select field:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;option1|Single room&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;option2|Twin room&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;option3|Double room&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our test scenario, we can simply write:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;gherkin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;When &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I am viewing a &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; content:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | title      | Post title               |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | field_room | Single room, Double room |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;working-with-other-entity-types&quot;&gt;Working with other entity types&lt;/h3&gt;
&lt;p&gt;Field-handling works with other entity types too, such as users and taxonomy terms. We can easily have a scenario that would create a bunch of users with their relative fields by writing:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;gherkin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Given &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;users:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | name    | mail                | language | field_name | field_surname | field_country  |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | antonio | antonio@example.com | it       | Antonio    | De Marco      | Belgium        |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | andrea  | andrea@example.com  | it       | Andrea     | Pescetti      | Italy          |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  | fabian  | fabian@example.com  | de       | Fabian     | Bircher       | Czech Republic |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;contributing-to-the-project&quot;&gt;Contributing to the project&lt;/h3&gt;
&lt;p&gt;At the moment field-handling is still a work in progress and, while it does support both Drupal 7 and Drupal 8, it covers only a limited set of field types, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple text fields&lt;/li&gt;
&lt;li&gt;Date fields&lt;/li&gt;
&lt;li&gt;Entity reference fields&lt;/li&gt;
&lt;li&gt;Link fields&lt;/li&gt;
&lt;li&gt;List text fields&lt;/li&gt;
&lt;li&gt;Taxonomy term reference fields&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to contribute to the project by providing additional field type handlers you will need to implement this very simple, core-agnostic, interface:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Drupal\Driver\Fields&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Interface FieldHandlerInterface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;@package&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; Drupal\Driver\Fields&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; FieldHandlerInterface&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   * Expand raw field values in a format compatible with entity_save().&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   *&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   * &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;@param&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; $values&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   *    Raw field values array.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   * &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;@return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   *    Expanded field values array.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; expand&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($values);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need some inspiration check the current handlers implementation by inspecting classes namespaced as  &lt;code&gt;\Drupal\Driver\Fields\Drupal7&lt;/code&gt; and &lt;code&gt;\Drupal\Driver\Fields\Drupal8&lt;/code&gt;.&lt;/p&gt;</content:encoded></item><item><title>Drupal 8 Configuration Management with Features</title><link>https://www.nuvole.org/blog/drupal-8-configuration-management-features/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-8-configuration-management-features/</guid><pubDate>Mon, 27 Apr 2015 13:30:09 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This is a preview of Nuvole’s training at DrupalCon Los Angeles: “&lt;a href=&quot;https://events.drupal.org/losangeles2015/training/effective-development-workflow-drupal-8&quot;&gt;An Effective Development Workflow in Drupal 8&lt;/a&gt;”.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Features for Drupal 8 &lt;a href=&quot;https://events.drupal.org/losangeles2015/sessions/features-drupal-8&quot;&gt;will exist&lt;/a&gt; and &lt;a href=&quot;http://www.phase2technology.com/blog/announcing-features-for-drupal-8/&quot;&gt;it is already in alpha state&lt;/a&gt;. This may be surprising to those who wrongly assumed that Configuration Management was going to supersede Features; but, as we &lt;a href=&quot;https://nuvole.org/blog/2014/jun/06/configuration-management-drupal-7-drupal-8&quot;&gt;explained before&lt;/a&gt;, it all comes down to understanding how to use Features the right way in Drupal 8.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are using Features in Drupal 8 for deployment of configuration, you are doing it wrong!™&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;the-role-of-features&quot;&gt;The role of Features&lt;/h2&gt;
&lt;p&gt;While the configuration management was built to keep configuration consistent and enable deploying it between different environments of the same site it was not built for &lt;strong&gt;sharing configuration&lt;/strong&gt; between different sites. This is where Features comes in. Features allows you to easily bundle related configuration and re-use it on another site.&lt;/p&gt;
&lt;p&gt;For re-using configuration and building starter profiles the current alpha version is already great. It works just like the re-usable “feature” we were &lt;a href=&quot;//nuvole.org/blog/2014/jul/15/packaging-and-reusing-configuration-drupal-8&quot;&gt;blogging&lt;/a&gt; about last year, except it comes with a familiar UI and lets you do everything as a site builder. It even comes with a smart system that auto detects configuration that belongs together.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;features-overview.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Features-overview&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;features-export.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Features-export&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;configuration-changes-are-development&quot;&gt;Configuration changes are development&lt;/h2&gt;
&lt;p&gt;Developers and site builders working with configuration in Drupal 8 need to understand that &lt;strong&gt;changing configuration needs to be treated equally with development.&lt;/strong&gt; While this may seem trivial at first, the consequences are a fundamental shift to the approach of building a Drupal 8 site.&lt;/p&gt;
&lt;p&gt;Drupal 8 comes with the configuration management system that was built to deploy configuration between different environments of the &lt;strong&gt;same&lt;/strong&gt; site. It allows the whole configuration of a site to be synchronized both through the UI for “site builders” and through drush and git for “developers”. While the system allows individual configuration objects to be exported and imported, deployment and synchronization is always done with the entire set to ensure it is consistent.&lt;/p&gt;
&lt;p&gt;This means that when you change configuration on a production site you either opt out of using the configuration management to deploy configuration or you need to go through a more elaborate &lt;a href=&quot;https://nuvole.org/blog/2014/aug/20/git-workflow-managing-drupal-8-configuration&quot;&gt;work flow&lt;/a&gt; to synchronize the configuration changes with the development environments similar to what you would need to do if you were to change code directly on the production server.&lt;/p&gt;
&lt;p&gt;For the math-oriented,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Δ config ⊆ development&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What if you want to be free of the temptation to edit configuration in production? Luckily there is a module for that! It is called &lt;a href=&quot;https://www.drupal.org/project/config_readonly&quot;&gt;Configuration read-only mode&lt;/a&gt; and allows to lock the forms where you change the configuration, thus enforcing its role as a “developers only” tool.&lt;/p&gt;
&lt;p&gt;Of course some configuration will differ between environments. Just as the database credentials are kept separate from the rest of the code, specific configuration can be overridden in the instances’ &lt;code&gt;settings.php&lt;/code&gt; or &lt;code&gt;services.yml&lt;/code&gt;. The rest of the code is usually treated as a whole for consistency reasons, the same should be true for configuration.&lt;/p&gt;
&lt;h2 id=&quot;what-about-not-using-configuration-management&quot;&gt;What about not using configuration management?&lt;/h2&gt;
&lt;p&gt;Features and packaging related modules should not be regarded as a solution to deploy only partial configuration. Packaged configuration by definition is never aware of the whole site and the possible inter-dependencies of the site’s particular configuration. Deploying only partial configuration circumvents the safeguards Drupal put in place to make the deployment more robust. Of course nobody is forced to use the new tools for configuration management and you can easily opt out of using it by just ignoring that option. It is also still possible to develop a Drupal 8 site by sharing a database dump just as with Drupal 5. But remember that a Drupal 7 approach will yield the Drupal 7 headaches. We would recommend re-evaluating the deployment strategies when starting to use Drupal 8.&lt;/p&gt;
&lt;h2 id=&quot;managing-distributions-in-drupal-8&quot;&gt;Managing distributions in Drupal 8&lt;/h2&gt;
&lt;p&gt;The more complicated scenario which is yet to be tackled is a feature for a richer distribution which will update over time. For example a newer version of a feature could come with an updated view or additional or updated fields for a content type.&lt;/p&gt;
&lt;p&gt;First steps in that direction have already been taken by &lt;a href=&quot;https://www.drupal.org/project/config_update&quot;&gt;Configuration Update Manager&lt;/a&gt; (a dependency of features) and &lt;a href=&quot;https://www.drupal.org/sandbox/nedjo/2424835&quot;&gt;Configuration Synchronizer&lt;/a&gt; (a sandbox module). Configuration Update Manager compares the configuration in use on the site with the configuration which was provided by a module or installation profile. Configuration Synchronizer goes a step further and keeps a snapshot of the configuration when it was installed in order to determine whether the configuration has been customized by an administrator or whether it can safely be updated to the one provided in the new version of the module/feature.&lt;/p&gt;
&lt;p&gt;All the modules mentioned above are used in a &lt;strong&gt;development environment&lt;/strong&gt; of a particular site. Or in other words in an environment where configuration changes are expected and part of the process of updating a site.&lt;/p&gt;
&lt;p&gt;For more, see our presentation from the &lt;a href=&quot;http://montpellier2015.drupaldays.org/sessions/configuration-management-drupal-8&quot;&gt;Drupal Developer Days&lt;/a&gt; (embedded below); thanks to all the people there for the fruitful discussions we had on this topic during the event.&lt;/p&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/qm7ydanZPEpS2K&quot; width=&quot;595&quot; height=&quot;485&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen&gt; &lt;/iframe&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.slideshare.net/nuvoleweb/2015-04devdaysmontpellier&quot;&gt;Drupal 8 Configuration Management with Features&lt;/a&gt;&lt;/strong&gt; from &lt;strong&gt;&lt;a href=&quot;https://www.slideshare.net/nuvoleweb&quot;&gt;Nuvole&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2015-04-devdays-montpellier.pdf (711.18 KB) &lt;a href=&quot;/sites/default/files/drupalcon2010.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Drupal 8 Configuration Management: beware of pitfalls</title><link>https://www.nuvole.org/blog/drupal-8-configuration-management-beware-pitfalls/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-8-configuration-management-beware-pitfalls/</guid><pubDate>Mon, 10 Aug 2015 14:35:10 GMT</pubDate><content:encoded>&lt;p&gt;Drupalaton in Hungary last weekend gave the opportunity for a 90 minutes workshop about Configuration Management (CMI) and how it changes a Drupal developers work flow with the coming Drupal 8.&lt;/p&gt;
&lt;p&gt;CMI allows you to have the whole site configuration under control and that is a great improvement. But it also comes with its pitfalls. Developers have to be more vigilant when sharing configuration with each other: mindless exporting configuration may override the configuration shared by others, mindless importing may override one’s own work and forgetting to import merged configuration leads to ignoring the work of your co-developers at a later stage.&lt;/p&gt;
&lt;p&gt;CMI, while it is much more awesome than anything we have in Drupal 7, is also much more unforgiving. So it would be all too easy to not use it properly and what it was designed for and then wonder why you can’t have nice things.&lt;/p&gt;
&lt;p&gt;Fortunately, a methodical git workflow can help recover from mistakes (except importing configuration before exporting it first). So don’t forget the order of the steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;config export&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;git commit&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;git merge&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;config import&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And do it often to keep the merges and failures simple and easy to resolve.&lt;/p&gt;
&lt;p&gt;Please find the full Drupalaton 2015 workshop slides attached below.&lt;/p&gt;
&lt;p&gt;CMI-drupalaton2015.pdf (745.87 KB) &lt;a href=&quot;/sites/default/files/CMI-drupalaton2015.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Optimal deployment workflow for Composer-based Drupal 8 projects</title><link>https://www.nuvole.org/blog/optimal-deployment-workflow-composer-based-drupal-8-projects/</link><guid isPermaLink="true">https://www.nuvole.org/blog/optimal-deployment-workflow-composer-based-drupal-8-projects/</guid><pubDate>Fri, 19 Aug 2016 11:20:33 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This post is an excerpt from the topics covered by our &lt;a href=&quot;https://events.drupal.org/dublin2016/training/d8-development&quot;&gt;DrupalCon Dublin training: Drupal 8 Development - Workflows and Tools&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;During the recent Nuvole presentations at &lt;a href=&quot;http://milan2016.drupaldays.org/node/139&quot;&gt;Drupal Dev Days Milan 2016&lt;/a&gt; and &lt;a href=&quot;http://2016.drupalaton.hu/schedule#speaker-211&quot;&gt;Drupalaton Hungary 2016&lt;/a&gt; we received a number of questions on how to properly setup a Drupal 8 project with Composer. An interesting case where we discovered that existing practices are completely different from each other is: “What is the best way to deploy a Composer-based Drupal 8 project?”.&lt;/p&gt;
&lt;p&gt;We’ll quickly discuss some options and describe what works best for us.&lt;/p&gt;
&lt;h3 id=&quot;what-to-commit&quot;&gt;What to commit&lt;/h3&gt;
&lt;p&gt;You should commit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;composer.json&lt;/code&gt; file: this is obvious when using Composer.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;composer.lock&lt;/code&gt; file: this is important since it will allow you to rebuild the entire codebase at the same status it was at a given point in the past.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fully built site is commonly left out of the repository. But this also means that you need to find a way for rebuilding and deploying the codebase safely.&lt;/p&gt;
&lt;h3 id=&quot;dont-run-composer-on-the-production-server&quot;&gt;Don’t run Composer on the production server&lt;/h3&gt;
&lt;p&gt;You would clearly never run &lt;code&gt;composer update&lt;/code&gt; on the production server, as you want to be sure that you will be deploying the same code you have been developing upon. For a while, we considered it to be enough to have Composer installed on the server and run &lt;code&gt;composer install&lt;/code&gt; to get predictable results from the (committed) &lt;code&gt;composer.lock&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Then we discovered that this approach has a few shortcomings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The process is not robust. A transient network error or timeout might result in a failed build, thus introducing uncertainty factors in the deploy scripts. Easy to handle, but still not desirable as part of a delicate step such as deployment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The process will inevitably take long. If you run &lt;code&gt;composer install&lt;/code&gt; in the webroot directly, your codebase will be unstable for a few minutes. This is orders of magnitude longer than a standard update process (i.e., running &lt;code&gt;drush updb&lt;/code&gt; and &lt;code&gt;drush cim&lt;/code&gt;) and it may affect your site availability. This can be circumvented by building in a separate directory and then symlinking or moving directories.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Even &lt;code&gt;composer install&lt;/code&gt; can be unpredictable, especially on servers with restrictions or running different versions of Composer or PHP; in rare circumstances, a build may succeed but yield a different codebase. This can be mitigated by enforcing (e.g., through Docker or virtualization) a dev/staging environment that matches the production environment, but you are still losing control on a relatively lengthy process.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You have no way of properly testing the newly built codebase after building it and before making it live.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Composer simply does not belong in a production server. It is a tool with a different scope, unrelated to the main tasks of a production server.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;where-to-build-the-codebase-ci-to-the-rescue&quot;&gt;Where to build the codebase? CI to the rescue&lt;/h3&gt;
&lt;p&gt;After ruling out the production server, where should the codebase be built then?&lt;/p&gt;
&lt;p&gt;Building it locally (i.e., using a developer’s environment) can’t work: besides the differences between the development and the production (&lt;code&gt;--no-dev&lt;/code&gt;) setup, there is the risk of missing possible small patches applied to the local codebase. And a totally clean build is always necessary anyway.&lt;/p&gt;
&lt;p&gt;We ended up using Continuous Integration for this task. Besides the standard CI job, which operates after any push operation to the branches under active development, performs a clean installation and runs automated tests, another CI job builds the full codebase based on the &lt;code&gt;master&lt;/code&gt; branch and the &lt;code&gt;composer.lock&lt;/code&gt; file. This allows sharing it between developers, a fast deployment to production through a tarball or rsync, and opportunities for actually testing the upgrade (with a process like: automatically import the production database, run database updates, import the new configuration, run a subset of automated tests to ensure that basic site functionality has no regressions) for maximum safety.&lt;/p&gt;
&lt;p&gt;Slides from our recent presentations, mostly focused on Configuration Management but covering part of this discussion too, are below.&lt;/p&gt;
&lt;p&gt;Configuration Management in Drupal 8 - DDD2016.pdf (1.1 MB) &lt;a href=&quot;/sites/default/files/Configuration%20Management%20in%20Drupal%208%20-%20DDD2016.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Maintain local development configuration with Configuration Split</title><link>https://www.nuvole.org/blog/maintain-local-development-configuration-configuration-split/</link><guid isPermaLink="true">https://www.nuvole.org/blog/maintain-local-development-configuration-configuration-split/</guid><pubDate>Tue, 23 Aug 2016 12:21:17 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This post is an excerpt from the topics covered by our &lt;a href=&quot;https://events.drupal.org/dublin2016/training/d8-development&quot;&gt;DrupalCon Dublin training: Drupal 8 Development - Workflows and Tools&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For the last two years we have been giving trainings and presentations at various Drupal events about configuration management and its new workflows in Drupal 8. One of the recurring questions has been:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do I keep some configuration from being deployed?&lt;/strong&gt; For example the devel module and its configuration?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Until now we have answered to use &lt;code&gt;drush&lt;/code&gt; with the &lt;code&gt;--skip-modules&lt;/code&gt; flag and then to gitignore the configuration for devel. But then you can’t share the development configuration and the process is error prone when there are more modules and other configuration items that depend on the configuration you gitignore.&lt;/p&gt;
&lt;p&gt;For some simpler cases where configuration needs to be different between environments (for example the error verbosity) the configuration override system allows to override the configuration in &lt;code&gt;settings.php&lt;/code&gt;. This is a solution for some cases, however, you can not override which modules are enabled or override configuration that doesn’t exist.&lt;/p&gt;
&lt;h3 id=&quot;enter-configuration-split&quot;&gt;Enter: Configuration split!&lt;/h3&gt;
&lt;p&gt;Our new module &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Configuration split&lt;/a&gt; splits the configuration when exporting it with a special &lt;a href=&quot;https://www.drupal.org/project/console&quot;&gt;Drupal Console&lt;/a&gt; command. It can be configured to split out enabled modules and given blacklisted configuration and it will then separate all the configuration that is dependent on the listed modules or configuration. The module’s settings also allow to set a folder to which the separated configuration will be exported to.&lt;/p&gt;
&lt;p&gt;That way the configuration set which you use to deploy configuration between different environments is a subset of your development configuration and the trusty configuration system of Drupal 8 can be used unharmed.
Of course when importing with the special command the split configuration is merged back, allowing you to keep your development configuration in place.&lt;/p&gt;
&lt;p&gt;“To split” is a synonym for “to break” and as such “Configuration split” has a dangerous ring to it. This is on purpose because the exported subset is &lt;em&gt;on purpose&lt;/em&gt; not what you have on your development site and not what you have locally tested. So you need to compensate that with a workflow that imports and verifies the configuration you are going to deploy. This is better than to import and export individual configuration because Drupal needs the whole set of configuration to do its checks.&lt;/p&gt;
&lt;h4 id=&quot;how-do-i-use-it&quot;&gt;How do I use it?&lt;/h4&gt;
&lt;p&gt;Download and enable the &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Configuration split&lt;/a&gt; module like any other module. Then configure it under &lt;code&gt;admin/config/development/configuration/config_split&lt;/code&gt;. Set the split folder to a different folder than your normal config sync folder. If you want a prettier interface, consider using &lt;a href=&quot;https://www.drupal.org/project/chosen&quot;&gt;Chosen&lt;/a&gt;. Then use the Drupal Console command &lt;code&gt;config_split:export&lt;/code&gt; and &lt;code&gt;config_split:import&lt;/code&gt; to export and import respectively.&lt;/p&gt;
&lt;p&gt;Let’s look at the devel example from above. We typically version the configuration outside of the webroot in &lt;code&gt;../config/sync&lt;/code&gt; as seen by Drupal. (In the project root when starting a project with &lt;code&gt;drupal-composer/drupal-project&lt;/code&gt;.) So the folder we specify for config_split would be &lt;code&gt;../config/dev&lt;/code&gt;. The module to filter would be Devel and the rest can be left empty. The &lt;code&gt;devel.settings&lt;/code&gt; will be detected automatically. Note though that the &lt;code&gt;system.menu.devel&lt;/code&gt; does not depend on the devel module and can not be detected automatically, but it is easy to add it to the blacklisted config and it could theoretically be deployed without breaking anything.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;config_split_settings_form.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Configuration Split settings form with chosen&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The resulting &lt;code&gt;config_split.settings.yml&lt;/code&gt; could look something like this when exporting it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;folder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;../config/dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  devel&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;blacklist&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;system.menu.devel&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally the following command will export the configuration to the default sync directory without the devel module enabled and export the devel configuration into the dev directory.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;web$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ../vendor/bin/drupal&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config_split:export&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now if you would import the configuration through the UI or with &lt;code&gt;drush cim&lt;/code&gt; the devel module would be un-installed, and you can do that to see the site without its development configuration. However, If you want the development configuration to stay or become active and the devel module installed use the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;web$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ../vendor/bin/drupal&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; config_split:import&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;how-does-it-work-internally&quot;&gt;How does it work internally?&lt;/h4&gt;
&lt;p&gt;We implemented a &lt;code&gt;StorageWrapper&lt;/code&gt; that allows filters to interact with the configuration after it has been read and before it is written to the wrapped &lt;code&gt;FileStorage&lt;/code&gt; during the import and export operation. The &lt;code&gt;SplitFilter&lt;/code&gt; has a secondary storage and decides where to read from or write to. This is a very similar concept to what drush does with its &lt;code&gt;--skip-modules&lt;/code&gt; flag since we will want to easily integrate this with drush in the future.&lt;/p&gt;
&lt;h4 id=&quot;what-comes-next&quot;&gt;What comes next?&lt;/h4&gt;
&lt;p&gt;The module is still in an early development stage and some more additions in the scope of splitting configuration could be added. The subset without the split configuration could be verified after exporting it and we could warn the user if it couldn’t be imported. Or for example we currently use only config_split.settings but if the need arises we could support multiple split configurations. Or we could add a “gray list” to ignore some configuration that exists rather than removing it when splitting, essentially making it a configuration override outside of the scope of Drupal’s runtime. This could be useful when maintaining several sites that are almost the same but all have their little “special snowflake” configuration which in turn could be synchronized with the normal workflow.&lt;/p&gt;
&lt;p&gt;It is important to understand that the configuration system of Drupal has limitations that are there for a good reason. Most of them are to ensure data integrity and robustness and reliability of the synchronisation and so on. In other words measures to protect you from accidentally breaking your site. Using tools like &lt;code&gt;config_split&lt;/code&gt; or &lt;code&gt;drush --skip-modules&lt;/code&gt; you circumvent some of these security and integrity checks so use them with caution.&lt;/p&gt;
&lt;p&gt;Since the module is not required for the regular functioning of a Drupal 8 site (it can even be used to blacklist itself) it can already be used in the development process of your current projects already. Your feedback is welcome, see you in the issue queue.&lt;/p&gt;</content:encoded></item><item><title>Pimp your Behat Drupal Extension and rule the world</title><link>https://www.nuvole.org/blog/pimp-your-behat-drupal-extension/</link><guid isPermaLink="true">https://www.nuvole.org/blog/pimp-your-behat-drupal-extension/</guid><pubDate>Mon, 19 Sep 2016 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This post is an excerpt from the topics covered by our &lt;a href=&quot;https://events.drupal.org/dublin2016/training/d8-development&quot;&gt;DrupalCon Dublin training: Drupal 8 Development - Workflows and Tools&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;At Nuvole we consider writing good tests as a fundamental part of development and, when it comes to testing a complex site, there is nothing better than extensive behavioral tests using Behat. The benefits of such a choice are quite obvious:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tests are very easy to write.&lt;/li&gt;
&lt;li&gt;Behat scenarios serve as a solid communication mean between business and developers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a site grows in complexity, however, the default step definitions provided by the excellent &lt;a href=&quot;https://www.drupal.org/project/drupalextension&quot;&gt;Behat Drupal Extension&lt;/a&gt; might not be specific enough and you will quickly find yourself adding custom step to your &lt;code&gt;FeatureContext&lt;/code&gt; or creating custom Behat contexts, as advocated by all &lt;a href=&quot;https://behat-drupal-extension.readthedocs.io/en/3.1/contexts.html&quot;&gt;official&lt;/a&gt; &lt;a href=&quot;http://docs.behat.org/en/v3.0/user_guide/initialize.html&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is all fine except that your boilerplate test code might soon start to grow into a non-reusable, non-tested bunch of code.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href=&quot;https://github.com/nuvoleweb/drupal-behat&quot;&gt;Nuvole’s Behat Drupal Extension&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;nuvoles-behat-drupal-extension&quot;&gt;Nuvole’s Behat Drupal Extension&lt;/h3&gt;
&lt;p&gt;Nuvole’s Behat Drupal Extension is built on the shoulders of the popular &lt;a href=&quot;https://www.drupal.org/project/drupalextension&quot;&gt;Behat Drupal Extension&lt;/a&gt; and it focuses on step re-usability and testability by allowing developers to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Organize their code in &lt;strong&gt;services&lt;/strong&gt; by providing a &lt;code&gt;YAML&lt;/code&gt; service description file, pretty much like we all are used to do nowadays with Drupal 8.&lt;/li&gt;
&lt;li&gt;Override default Drupal Behat Extension services with their own.&lt;/li&gt;
&lt;li&gt;Benefit of many ready-to-use contexts that are provided by the extension out of the box.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;installation-and-setup&quot;&gt;Installation and setup&lt;/h3&gt;
&lt;p&gt;Install Nuvole’s Behat Drupal Extension with &lt;a href=&quot;https://getcomposer.org/&quot;&gt;Composer&lt;/a&gt; by running:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; composer&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nuvoleweb/drupal-behat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setup the extension by following the &lt;a href=&quot;https://github.com/jhedstrom/drupalextension#quick-start&quot;&gt;Quick start&lt;/a&gt; section available on the original Behat Drupal Extension page, just use &lt;code&gt;NuvoleWeb\Drupal\DrupalExtension&lt;/code&gt; instead of the native &lt;code&gt;Drupal\DrupalExtension&lt;/code&gt; in your &lt;code&gt;behat.yml&lt;/code&gt; as shown below:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  suites&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    default&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      contexts&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Drupal\DrupalExtension\Context\DrupalContext&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;NuvoleWeb\Drupal\DrupalExtension\Context\DrupalContext&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  extensions&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    Behat\MinkExtension&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      goutte&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;~&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Use &quot;NuvoleWeb\Drupal\DrupalExtension&quot; instead of &quot;Drupal\DrupalExtension&quot;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    NuvoleWeb\Drupal\DrupalExtension&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      api_driver&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;drupal&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;tests/my_services.yml&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      text&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        node_submit_label&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Save and publish&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;service-container-aware-contexts&quot;&gt;“Service container”-aware Contexts&lt;/h3&gt;
&lt;p&gt;All contexts extending &lt;code&gt;\NuvoleWeb\Drupal\DrupalExtension\Context\RawDrupalContext&lt;/code&gt; and &lt;code&gt;\NuvoleWeb\Drupal\DrupalExtension\Context\RawMinkContext&lt;/code&gt; are provided with direct access to the current Behat service container. Developers can also define their own services by adding a &lt;code&gt;YAML&lt;/code&gt; description file to their project and setting the &lt;code&gt;services:&lt;/code&gt; parameter to point to its current location (as shown above).&lt;/p&gt;
&lt;p&gt;The service description file can describe both custom services and override already defined services. For example, given a &lt;code&gt;tests/my_services.yml&lt;/code&gt; containing:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  your.own.namespace.hello_world&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Your\Own\Namespace\HelloWorldService&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then all contexts extending &lt;code&gt;\NW\D\DE\C\RawDrupalContext&lt;/code&gt; or &lt;code&gt;\NW\D\DE\C\RawMinkContext&lt;/code&gt; will be able to access that service by just calling:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; TestContext&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; RawDrupalContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   * Assert service.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   *&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   * @Then I say hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; assertHelloWorld&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getContainer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;your.own.namespace.hello_world&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sayHello&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;your.own.namespace.hello_world&lt;/code&gt; service class itself can be easily tested using PHPUnit. Also, since Behat uses &lt;a href=&quot;http://symfony.com/doc/current/service_container.html&quot;&gt;Symfony’s Service Container&lt;/a&gt; you can list services your service depends on as arguments so to remove any hardcoded dependency, following &lt;a href=&quot;http://www.phptherightway.com/#dependency_injection&quot;&gt;Dependency Injection best practices&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;override-existing-services&quot;&gt;Override existing services&lt;/h3&gt;
&lt;p&gt;Say that, while working on your Drupal 7 project, you have defined a step that publishes a node given its content type and title and you want to use the same exact step on your Drupal 8 project, something like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Given I publish the node of type &quot;page&quot; and title &quot;My page title&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem here is that the actual API calls to load and save a node differs between Drupal 7 and Drupal 8.&lt;/p&gt;
&lt;p&gt;The solution is to override the default Drupal core services specifying your own classes in your &lt;code&gt;tests/my_services.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;parameters&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Overrides Nuvole&apos;s Drupal Extension Drupal 7 core class.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  drupal.driver.cores.7.class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Your\Own\Namespace\Driver\Cores\Drupal7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Overrides Nuvole&apos;s Drupal Extension Drupal 8 core class.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  drupal.driver.cores.8.class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Your\Own\Namespace\Driver\Cores\Drupal8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  your.own.namespace.hello_world&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Your\Own\Namespace\HelloWorldService&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll then delegate the core-specific business logic to the new core classes allowing your custom step to be transparently run on both Drupal 7 and Drupal 8. Such a step would look like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; TestContext&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; RawDrupalContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   * @Given I publish the node of type :type and title :title&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; iPublishTheNodeOfTypeAndTitle&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($type, $title) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getCore&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;publishNode&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($type, $title);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;ready-to-use-contexts&quot;&gt;Ready to use contexts&lt;/h3&gt;
&lt;p&gt;The extension also provides some utility contexts that you can use right away in your tests. Below a quick overview of what’s currently available:&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;NuvoleWeb\Drupal\DrupalExtension\Context\DrupalContext&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Standard Drupal context. You want to use this one next to (and not instead of) &lt;code&gt;Drupal\DrupalExtension\Context\DrupalContext&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;NuvoleWeb\Drupal\DrupalExtension\Context\ContentContext&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Perform operations on Content.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;NuvoleWeb\Drupal\DrupalExtension\Context\CKEditorContext&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Allows to interact with CKEditor components on your page.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;yaml NuvoleWeb\Drupal\DrupalExtension\Context\ResponsiveContext: devices: mobile_portrait: 360x640 mobile_landscape: 640x360 tablet_portrait: 768x1024 tablet_landscape: 1024x768 laptop: 1280x800 desktop: 2560x1440 &lt;/code&gt;&lt;/td&gt;&lt;td&gt;Resize the browser according to the specified devices, useful for testing responsive behaviors.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;NuvoleWeb\Drupal\DrupalExtension\Context\PositionContext&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Check position of elements on the page.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;NuvoleWeb\Drupal\DrupalExtension\Context\ChosenFieldContext&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Interact with Chosen elements on the page.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;We will share more steps in the future enriching the current contexts as well as providing new ones so keep an eye on the &lt;a href=&quot;https://github.com/nuvoleweb/drupal-behat&quot;&gt;project repository&lt;/a&gt;!&lt;/p&gt;
&lt;h4 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h4&gt;
&lt;p&gt;At the moment only Drupal 8 is supported but we will add Drupal 7 support ASAP (yes, it’s as easy as providing missing Drupal 7 driver core methods and adding tests).&lt;/p&gt;</content:encoded></item><item><title>Configuration Management: Theory and Practice</title><link>https://www.nuvole.org/blog/configuration-management-theory-and-practice/</link><guid isPermaLink="true">https://www.nuvole.org/blog/configuration-management-theory-and-practice/</guid><pubDate>Tue, 27 Sep 2016 16:23:53 GMT</pubDate><content:encoded>&lt;p&gt;Developers often want to use Configuration Management outside its intended use case. New workflows and practices have to be established.&lt;/p&gt;
&lt;p&gt;We’ve just presented a gallery of problems and solutions at DrupalCon Dublin 2016.&lt;/p&gt;
&lt;p&gt;The room was packed, and many people were denied admission for security reasons. For them, and for those who didn’t manage to come to this DrupalCon, we are making the full presentation available here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;Configuration-Management-Theory-and-Practice.pdf&quot;&gt;PDF slides&lt;/a&gt; for download&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=57t_CS2wbHI&quot;&gt;Full video of the presentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe width=&quot;640&quot; height=&quot;360&quot; src=&quot;https://www.youtube.com/embed/57t_CS2wbHI&quot; frameborder=&quot;0&quot; allowfullscreen&gt;
Configuration-Management-Theory-and-Practice.pdf (2.41 MB) download&lt;/iframe&gt;&lt;p&gt;&lt;/p&gt;</content:encoded></item><item><title>Configuration Split: first beta release at Drupal IronCamp</title><link>https://www.nuvole.org/blog/configuration-split-first-beta-release-drupal-ironcamp/</link><guid isPermaLink="true">https://www.nuvole.org/blog/configuration-split-first-beta-release-drupal-ironcamp/</guid><pubDate>Mon, 28 Nov 2016 11:30:44 GMT</pubDate><content:encoded>&lt;p&gt;One of the nice things that happened during &lt;a href=&quot;http://www.drupalironcamp.com/&quot;&gt;Drupal Ironcamp&lt;/a&gt; in Prague last week was the first beta release of the &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Configuration Split&lt;/a&gt; that I started developing &lt;a href=&quot;https://nuvole.org/blog/2016/aug/23/maintain-local-development-configuration-configuration-split&quot;&gt;a few months ago&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As explained in the &lt;a href=&quot;https://nuvole.org/blog/2016/aug/23/maintain-local-development-configuration-configuration-split&quot;&gt;previous blog post about Configuration Split&lt;/a&gt;, the typical use case of Configuration Split is to be able to maintain a “development” configuration and a “production” configuration, where some of the modules or settings you use in development are not copied to production upon configuration export/import.&lt;/p&gt;
&lt;h2 id=&quot;a-typical-use-case&quot;&gt;A typical use case&lt;/h2&gt;
&lt;p&gt;We assume that you have a production and development version of the same site and that they are aligned.&lt;/p&gt;
&lt;h3 id=&quot;step-1-enable-anything-you-need-in-development-and-work-normally&quot;&gt;Step 1: Enable anything you need in development and work normally&lt;/h3&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;enabled_ui_modules.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Enabled UI modules&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the development environment we enable Devel and UI modules enabled and we can create new content types, add fields and do all things we like to do while developing a Drupal site. Some of this configuration work is meant to go to the production site and some (e.g., the fact that Devel is enabled and related configuration) is not. We thus need to &lt;strong&gt;split&lt;/strong&gt; our configuration.&lt;/p&gt;
&lt;h3 id=&quot;step-2-create-a-split-definition&quot;&gt;Step 2: Create a split definition&lt;/h3&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;split_settings.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Basic Split settings&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Choose a name for your “local” configuration and select all the configuration you don’t want to have on the live site (even though, if you wish, you can still share it with colleagues).
This can include modules you would not want to be enabled or configuration that is for development only.
The path here is a folder next to the normal config sync directory outside of the web root.
In the example above, we are splitting out to a configuration set named “Development” the Devel, Fields UI, Views UI modules and the &lt;code&gt;system.menu.devel&lt;/code&gt; configuration.&lt;/p&gt;
&lt;h3 id=&quot;step-3-export-your-configuration-with-config_split&quot;&gt;Step 3: Export your configuration with &lt;code&gt;config_split&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;$ drush config-split-export&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In the most basic use case we just replace &lt;code&gt;config-export&lt;/code&gt; with &lt;code&gt;config-split-export&lt;/code&gt; in our workflow. This exports the elements we selected above to &lt;code&gt;../config/dev&lt;/code&gt; and the rest (the configuration we want to deplay) to our “standard” configuration folder, in this case &lt;code&gt;../config/sync&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;step-4-import-the-clean-configuration-in-production&quot;&gt;Step 4: Import the “clean” configuration in production&lt;/h3&gt;
&lt;p&gt;On all environments we use the configuration import of &lt;code&gt;config_split&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ drush config-split-import&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So in our usual workflow we replace &lt;code&gt;config-import&lt;/code&gt; with &lt;code&gt;config-split-import&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In order not to have different workflows for development and production we simply deactivate the split on production by overriding the configuration in &lt;code&gt;settings.php&lt;/code&gt; on production: &lt;code&gt;$config[&apos;config_split.config_split.development&apos;][&apos;status&apos;] = FALSE;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will result in the split definition to be deactivated.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;deactivated_split.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Deactivated Split settings&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;And consequently the import will have the configuration blacklisted in the split removed and development modules deactivated.&lt;/p&gt;
&lt;h2 id=&quot;whats-new-in-the-beta-version&quot;&gt;What’s new in the beta version&lt;/h2&gt;
&lt;p&gt;The following features are new or changed in the beta release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The drush command works and has some text to warn users to which directories configuration will be written to.&lt;/li&gt;
&lt;li&gt;The drupal console command is disabled, but there is a &lt;a href=&quot;https://www.drupal.org/node/2830660&quot;&gt;patch/branch&lt;/a&gt; you can use or help to make it functional again.&lt;/li&gt;
&lt;li&gt;Split entities now have a status and the default command only uses the active splits. Of course you can override that in &lt;code&gt;settings.php&lt;/code&gt; as with almost all configuration.&lt;/li&gt;
&lt;li&gt;You can switch out the default &lt;code&gt;config.storage.sync&lt;/code&gt; service in your &lt;code&gt;services.yml&lt;/code&gt; file so that the default configuration sync UI uses the split import. Details are in the &lt;code&gt;README.txt&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;more-information-and-acknowledgments&quot;&gt;More information and acknowledgments&lt;/h2&gt;
&lt;p&gt;On Friday I gave a presentation about my favorite topic in Drupal: &lt;a href=&quot;http://www.drupalironcamp.com/content/configuration-management-theory-and-practice&quot;&gt;Configuration Management&lt;/a&gt;, attached here are the &lt;a href=&quot;ConfigurationManagement_ironcamp2016.pdf&quot;&gt;slides&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wish to thank IronCamp participants and especially &lt;a href=&quot;https://www.drupal.org/u/swentel&quot;&gt;Swentel&lt;/a&gt; and &lt;a href=&quot;https://www.drupal.org/u/mr.baileys&quot;&gt;mr.baileys&lt;/a&gt; who worked on patches, and I’d like to thank IronCamp organizers for a very well organized event where I met a lot of old and new friends.  Swentel also took care of documenting more advanced use cases of Configuration Split on &lt;a href=&quot;http://realize.be/configuration-split-deploy-subsets-configuration-drupal-8&quot;&gt;his blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For any further information see &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;the module page&lt;/a&gt; and its &lt;a href=&quot;https://www.drupal.org/project/issues/config_split?categories=All&quot;&gt;issue queue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ConfigurationManagement_ironcamp2016.pdf (1.28 MB) &lt;a href=&quot;/sites/default/files/ConfigurationManagement_ironcamp2016.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Introducing the UI Patterns module: use atomic UI components everywhere in Drupal 8</title><link>https://www.nuvole.org/blog/ui-patterns-module-re-use-ui-components-everywhere-in-drupal-8/</link><guid isPermaLink="true">https://www.nuvole.org/blog/ui-patterns-module-re-use-ui-components-everywhere-in-drupal-8/</guid><pubDate>Mon, 23 Jan 2017 16:09:20 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; We have setup a Gitter chat room if you have any specific questions about the module’s functionality:
&lt;a href=&quot;https://gitter.im/nuvoleweb/ui_patterns?utm_source=badge&amp;#x26;utm_medium=badge&amp;#x26;utm_campaign=pr-badge&quot;&gt;&lt;img src=&quot;https://badges.gitter.im/nuvoleweb/ui_patterns.svg&quot; alt=&quot;Gitter&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Establishing an &lt;a href=&quot;http://bradfrost.com/blog/post/atomic-web-design/&quot;&gt;atomic design&lt;/a&gt; system in your project is one of the most effective way to build a consistent and maintainable user interface.&lt;/p&gt;
&lt;p&gt;Over the past years projects like &lt;a href=&quot;http://patternlab.io/&quot;&gt;PatternLab&lt;/a&gt; or the &lt;a href=&quot;https://www.drupal.org/project/components&quot;&gt;Component Libraries module&lt;/a&gt; aimed at lowering the cost of maintaining (PatternLab) and re-using (Component Libraries) UI patterns in your projects, be it a generic PHP application or a brand new Drupal 8 site.&lt;/p&gt;
&lt;p&gt;But, as we all know, when it comes to presenting content the Drupal 8 landscape is quite diverse: you can layout your pages using &lt;a href=&quot;https://www.drupal.org/project/panels&quot;&gt;Panels&lt;/a&gt; or Views, style your entities using &lt;a href=&quot;https://www.drupal.org/project/ds&quot;&gt;Display Suite&lt;/a&gt; view modes, group your fields with &lt;a href=&quot;https://www.drupal.org/project/field_group&quot;&gt;Field group&lt;/a&gt;, etc.&lt;/p&gt;
&lt;p&gt;Such a diversification can surely present some challenges when it comes at reusing a well-designed and consistent UI library. In other words: how can I transparently use the same UI pattern in my views, layouts, field formatters, etc.?&lt;/p&gt;
&lt;h2 id=&quot;enter-the-ui-patterns-module&quot;&gt;Enter the UI Patterns module&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.drupal.org/project/ui_patterns&quot;&gt;UI Patterns module&lt;/a&gt; allows you to &lt;a href=&quot;http://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definition.html&quot;&gt;define and expose&lt;/a&gt; self-contained UI patterns as Drupal 8 plugins and use them seamlessly as drop-in templates for panels, field groups, views, Display Suite view modes and field templates.&lt;/p&gt;
&lt;p&gt;The module also generates a pattern library page that can be used as documentation for content editors or as a showcase for business and clients (the following example is styled using the &lt;a href=&quot;https://www.drupal.org/project/bootstrap&quot;&gt;Bootstrap theme&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;pattern-library.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;UI Patterns&lt;/strong&gt; module also &lt;a href=&quot;http://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definition.html#override-patterns-behavior&quot;&gt;easily integrates&lt;/a&gt; with with tools like &lt;strong&gt;PatternLab&lt;/strong&gt; or modules like &lt;strong&gt;Component Libraries&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;define-your-patterns&quot;&gt;Define your patterns&lt;/h2&gt;
&lt;p&gt;Patterns can be defined using YAML in files named &lt;code&gt;MY_MODULE.ui_patterns.yml&lt;/code&gt; or &lt;code&gt;MY_THEME.ui_patterns.yml&lt;/code&gt; using the following format:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;blockquote&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  label&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Blockquote&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Display a quote with attribution information.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  fields&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    quote&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      label&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Quote&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Quote text.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      preview&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Life is like riding a bicycle. To keep your balance, you must keep moving.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    attribution&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      label&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Attribution&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Quote attribution.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      preview&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Albert Einstein&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After defining the pattern you have to provide a Twig template to render it which, in our case, could look like that:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;twig&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;blockquote&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{{ quote }}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;footer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{{ attribution }}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;footer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;blockquote&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you are done you can visit the pattern library page and check your new &lt;strong&gt;Blockquote&lt;/strong&gt; pattern in action:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;blockquote-preview.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;We have much more options available to make sure pattern definition can fit your use case (i.e. template overrides, etc.), make sure you &lt;a href=&quot;http://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definition.html&quot;&gt;check the documentation&lt;/a&gt; for the full list.&lt;/p&gt;
&lt;h2 id=&quot;use-your-patterns-everywhere&quot;&gt;Use your patterns everywhere&lt;/h2&gt;
&lt;p&gt;After exposing your patterns you are ready to use them anywhere thanks to the sub-modules bundled within &lt;strong&gt;UI Patterns&lt;/strong&gt;, namely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UI Patterns Field Group&lt;/strong&gt;: allows to use patterns to format field groups provided by the &lt;a href=&quot;https://www.drupal.org/project/field_group&quot;&gt;Field group&lt;/a&gt; module. &lt;a href=&quot;http://ui-patterns.readthedocs.io/en/8.x-1.x/content/field-group.html&quot;&gt;Read the documentation.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI Patterns Layouts&lt;/strong&gt;: allows to use patterns as layouts provided by the &lt;a href=&quot;https://www.drupal.org/project/layout_plugin&quot;&gt;Layout plugin&lt;/a&gt; module. This allows patterns to be used on &lt;a href=&quot;https://www.drupal.org/project/ds&quot;&gt;Display Suite&lt;/a&gt; view modes or on &lt;a href=&quot;https://www.drupal.org/project/panels&quot;&gt;panels&lt;/a&gt; out of the box. &lt;a href=&quot;http://ui-patterns.readthedocs.io/en/8.x-1.x/content/layout-plugin.html&quot;&gt;Read the documentation.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI Patterns Display Suite&lt;/strong&gt;: allows to use patterns to format &lt;a href=&quot;https://www.drupal.org/project/ds&quot;&gt;Display Suite&lt;/a&gt; field templates. &lt;a href=&quot;http://ui-patterns.readthedocs.io/en/8.x-1.x/content/field-templates.html&quot;&gt;Read the documentation.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI Patterns Views&lt;/strong&gt;: allows to use patterns as Views row templates. &lt;a href=&quot;http://ui-patterns.readthedocs.io/en/8.x-1.x/content/views.html&quot;&gt;Read the documentation.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;example-style-links-as-call-to-action-buttons&quot;&gt;Example: style links as call-to-action buttons&lt;/h2&gt;
&lt;p&gt;One of the most ordinary situation is styling a group of links as call-to-action buttons. This can be easily achieved using &lt;strong&gt;UI Patters&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Say we have defined the following &lt;strong&gt;Button&lt;/strong&gt; pattern:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Button&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt; description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;A simple button.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt; fields&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;   title&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;     type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;     label&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Label&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;     description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;The button label&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;     preview&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Submit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;   url&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;     type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;     label&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;URL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;     description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;The button URL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;     preview&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;http://example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the entity display setting page we access the link field setting by clicking on the gear icon:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;field-template-1.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then, after selecting the &lt;strong&gt;Pattern&lt;/strong&gt; field template and the &lt;strong&gt;Button&lt;/strong&gt; pattern, we map the link field columns to the pattern’s fields defined above:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;field-template-2.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Each value of our multi-valued link field will be then formatted using the &lt;strong&gt;Button&lt;/strong&gt; pattern,
as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;field-template-3.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;UI Patterns&lt;/strong&gt; module aims at integrating your pattern library with the most used Drupal 8 rendering systems. It also makes easy to use third-party tools such as &lt;a href=&quot;http://patternlab.io/&quot;&gt;PatternLab&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The project is currently under active maintenance, please file issues and/or support requests using &lt;a href=&quot;https://github.com/nuvoleweb/ui_patterns&quot;&gt;this GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; Special thanks to &lt;a href=&quot;http://www.aleksip.net/&quot;&gt;aleksip&lt;/a&gt; for getting the integration with &lt;strong&gt;PatternLab&lt;/strong&gt; and the &lt;strong&gt;Component Libraries&lt;/strong&gt; to work!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT UPDATE:&lt;/strong&gt; If you are using version &lt;code&gt;8.x-1.0-beta2&lt;/code&gt; make sure you read &lt;a href=&quot;https://github.com/nuvoleweb/ui_patterns/issues/36&quot;&gt;this change note&lt;/a&gt; for a safe upgrade to newer versions.&lt;/p&gt;</content:encoded></item><item><title>Introducing Config Filter</title><link>https://www.nuvole.org/blog/introducing-config-filter/</link><guid isPermaLink="true">https://www.nuvole.org/blog/introducing-config-filter/</guid><pubDate>Mon, 27 Feb 2017 14:12:28 GMT</pubDate><content:encoded>&lt;p&gt;Our solution for advanced configuration management workflows has just become more powerful!
The core of what makes Configuration Split work so nicely with drush and the Drupal UI has been split off in a new module: &lt;a href=&quot;https://www.drupal.org/project/config_filter&quot;&gt;Config Filter&lt;/a&gt;!
Config Filter exposes a &lt;code&gt;ConfigFilter&lt;/code&gt; plugin and swaps the sync storage applying all the active ConfigFilters to it. This means it is no longer necessary to manually swap the service as we recommended to do in the past and it works also when installing a site, as the swapping happens only when the module is installed. As of drush 8.1.10 this works with the default config import and export commands.&lt;/p&gt;
&lt;h3 id=&quot;update-config-split&quot;&gt;Update Config Split&lt;/h3&gt;
&lt;p&gt;For current users of the &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Config Split&lt;/a&gt; module it means that they should remove the custom service swapping as part of the update and then apply the database updates which will install Config Filter (you already downloaded it with composer update, right?).&lt;/p&gt;
&lt;h3 id=&quot;new-command-line-options-and-new-workflows&quot;&gt;New command-line options and new workflows&lt;/h3&gt;
&lt;p&gt;With the re-factoring we also changed the options of the drush and console commands. We added a new option for specifying a split and then export to and import from only that split directory; this supersedes the previous options to specify separate export destinations and import sources - they are not needed for the simple work flow we advocate for, and the behaviour can easily be achieved by modifying &lt;code&gt;settings.php&lt;/code&gt; as needed.&lt;/p&gt;
&lt;p&gt;With the addition of the single split export, a new workflow becomes possible: export only the “production” split, before importing the whole configuration which will have it merged back in.&lt;/p&gt;
&lt;h3 id=&quot;improved-graylists&quot;&gt;Improved Graylists&lt;/h3&gt;
&lt;p&gt;In addition to the new architecture the graylist feature has been improved so that dependencies can be graylisted as well and optionally only items that differ from the sync storage are split off. More details and more documentation will come in future.&lt;/p&gt;
&lt;h3 id=&quot;roadmap&quot;&gt;Roadmap&lt;/h3&gt;
&lt;p&gt;The ultimate goal is to improve the export API in Drupal 8 core so that more advanced workflows are possible. Config Filter is our proposal for a solution in contrib, but we feel that this functionality should belong to Core.&lt;/p&gt;
&lt;p&gt;On the way to Core, here are some more immediate steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clean up after the big refactoring, adding automated tests for some special cases.&lt;/li&gt;
&lt;li&gt;Improve the documentation.&lt;/li&gt;
&lt;li&gt;Integrate with &lt;a href=&quot;https://www.drupal.org/project/config_readonly&quot;&gt;Config Readonly&lt;/a&gt;  to allow locking the site except for the configuration defined in splits.&lt;/li&gt;
&lt;li&gt;For those that want to do configuration management without seatbelts, integrate &lt;a href=&quot;https://www.drupal.org/project/config_ignore&quot;&gt;Config Ignore&lt;/a&gt; with Config Filter so that it can work smoothly next to Config Split.&lt;/li&gt;
&lt;li&gt;…and eventually propose Config Filter for inclusion in Core!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;more-information-and-acknowledgments&quot;&gt;More information and acknowledgments&lt;/h3&gt;
&lt;p&gt;The re-factoring started during the Drupal Mountain Camp in Davos and I would like to thank the organizing committee for the great event.
On Friday I gave a presentation about my favorite topic in Drupal: &lt;a href=&quot;https://drupalmountaincamp.ch/sessions/configuration-management-without-problems&quot;&gt;Configuration Management&lt;/a&gt;, attached here are the &lt;a href=&quot;ConfigurationManagement_no-problems_davos17.pdf&quot;&gt;slides&lt;/a&gt; based on the Nuvole presentation at DrupalCon Dublin 2016.&lt;/p&gt;
&lt;p&gt;For any further information see the module pages for &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Config Split&lt;/a&gt; and &lt;a href=&quot;https://www.drupal.org/project/config_filter&quot;&gt;Config Filter&lt;/a&gt; and their &lt;a href=&quot;https://www.drupal.org/project/issues/config_filter?categories=All&quot;&gt;issue&lt;/a&gt; &lt;a href=&quot;https://www.drupal.org/project/issues/config_split?categories=All&quot;&gt;queues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ConfigurationManagement_no-problems_davos17.pdf (1.15 MB) &lt;a href=&quot;/sites/default/files/ConfigurationManagement_no-problems_davos17.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Our presentations at Drupal Dev Days Seville</title><link>https://www.nuvole.org/blog/our-presentations-drupal-dev-days-seville/</link><guid isPermaLink="true">https://www.nuvole.org/blog/our-presentations-drupal-dev-days-seville/</guid><pubDate>Thu, 30 Mar 2017 15:25:55 GMT</pubDate><content:encoded>&lt;p&gt;Last week we attended and sponsored the Drupal Developer Days in Seville where we also had two well attended sessions. &lt;a href=&quot;https://seville2017.drupaldays.org/sessions/introducing-ui-patterns-module-use-atomic-ui-components-everywhere-drupal-8&quot;&gt;Introducing the UI Patterns module: use atomic UI components everywhere in Drupal 8&lt;/a&gt; and &lt;a href=&quot;https://seville2017.drupaldays.org/sessions/advanced-configuration-management-config-split-et-al&quot;&gt;Advanced Configuration Management with Config Split et al.&lt;/a&gt; attached here are the promised slides, as well as a few updates about the modules.&lt;/p&gt;
&lt;h2 id=&quot;whats-new-in-config-split&quot;&gt;What’s new in Config Split&lt;/h2&gt;
&lt;h4 id=&quot;drush&quot;&gt;Drush&lt;/h4&gt;
&lt;p&gt;As we posted about before, drush supports the Config Split workflow since 8.1.10. In the next version drush will drop the support for its &lt;code&gt;--skip-modules&lt;/code&gt; flag and people using it should upgrade to using Config Split.&lt;/p&gt;
&lt;h4 id=&quot;split-storage-in-database&quot;&gt;Split Storage in Database&lt;/h4&gt;
&lt;p&gt;Previous versions of Config Split allowed to set an empty split folder which resulted in the configuration to be lost. To avoid saving the configuration into files under version control one would therefore have to set up a temporary directory and save the split there. But with the last development release a separate database storage is used when not specifying a split folder. This allows configuration to be “stashed” in the database for a deployment. A specific export first is still and will always be necessary by design.&lt;/p&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/xXExXIVtBJ8qc0&quot; width=&quot;595&quot; height=&quot;485&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;div style=&quot;margin-bottom:5px&quot;&gt; &lt;strong&gt; &lt;a href=&quot;//www.slideshare.net/nuvoleweb/advanced-configuration-management-with-config-split-et-al&quot; title=&quot;Advanced Configuration Management with Config Split et al.&quot; target=&quot;_blank&quot;&gt;Advanced Configuration Management with Config Split et al.&lt;/a&gt; &lt;/strong&gt; from &lt;strong&gt;&lt;a target=&quot;_blank&quot; href=&quot;//www.slideshare.net/nuvoleweb&quot;&gt;Nuvole&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;
&lt;p&gt;Click &lt;a href=&quot;/sites/default/files/presentation-config_split.pdf&quot;&gt;here&lt;/a&gt; to download the slides.&lt;/p&gt;
&lt;h2 id=&quot;whats-new-in-ui-patterns&quot;&gt;What’s new in UI Patterns&lt;/h2&gt;
&lt;p&gt;Following a productive BOF meeting at DDD, it was decided to move everything that concerns defining and displaying patterns locally into a separate module.&lt;/p&gt;
&lt;p&gt;This will allow for a better and more solid architecture: the main UI  Patterns module will be solely responsible to provide plugins and other  glue code: it will then be responsibility of modules implementing  component library integrations to expose their components using pattern  derivers.&lt;/p&gt;
&lt;p&gt;Discussion is ongoing at &lt;a href=&quot;https://github.com/nuvoleweb/ui_patterns/issues/86&quot;&gt;https://github.com/nuvoleweb/ui_patterns/issues/86&lt;/a&gt; and more generic future plans are being dicussed at &lt;a href=&quot;https://github.com/nuvoleweb/ui_patterns/issues/76&quot;&gt;https://github.com/nuvoleweb/ui_patterns/issues/76&lt;/a&gt;&lt;/p&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/dN1hedBYK5geTP&quot; width=&quot;595&quot; height=&quot;485&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;div style=&quot;margin-bottom:5px&quot;&gt; &lt;strong&gt; &lt;a href=&quot;//www.slideshare.net/nuvoleweb/introducing-the-ui-patterns-module-use-atomic-ui-components-everywhere-in-drupal-8&quot; title=&quot;Introducing the UI Patterns module: use atomic UI components everywhere in Drupal 8&quot; target=&quot;_blank&quot;&gt;Introducing the UI Patterns module: use atomic UI components everywhere in Drupal 8&lt;/a&gt; &lt;/strong&gt; from &lt;strong&gt;&lt;a target=&quot;_blank&quot; href=&quot;//www.slideshare.net/nuvoleweb&quot;&gt;Nuvole&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;
&lt;p&gt;Click &lt;a href=&quot;/sites/default/files/presentation-ui_patterns.pdf&quot;&gt;here&lt;/a&gt; to download the slides.&lt;/p&gt;</content:encoded></item><item><title>Configuration Management dimensions</title><link>https://www.nuvole.org/blog/configuration-management-dimensions/</link><guid isPermaLink="true">https://www.nuvole.org/blog/configuration-management-dimensions/</guid><pubDate>Tue, 25 Apr 2017 12:07:28 GMT</pubDate><content:encoded>&lt;p&gt;Unfortunately none of us from Nuvole are attending DrupalCon Baltimore and can’t, therefore, attend the “hallway track” and join discussions in person. We have held presentations about advanced configuration management in Drupal 8 at all Drupal events we attended in the last year including the last DrupalCon in Dublin. So I’ll try to cover some concepts here that could help the discussion about how the configuration management can be improved.&lt;/p&gt;
&lt;p&gt;To me there are at least two important dimensions to configuration management in Drupal 8 and different contrib project have sprouted to address:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vertical:&lt;/strong&gt; transfer configuration between different environments of the same site.&lt;br&gt;
&lt;strong&gt;Horizontal:&lt;/strong&gt; transfer configuration between different sites.&lt;/p&gt;
&lt;p&gt;Following are a few contrib solutions and core issues that address the different itches. This is not meant to be an exhaustive or definitive list but highlight the different problem spaces. Many more contrib solutions address some issues in this space to accommodate various different workflows.&lt;/p&gt;
&lt;h2 id=&quot;vertical&quot;&gt;Vertical&lt;/h2&gt;
&lt;p&gt;Drupal 8 core only addresses this use case, however, there are some important cases not covered (yet). The management of configuration between the different environments is best taken care of by importing and exporting the whole sites configuration together.&lt;/p&gt;
&lt;h4 id=&quot;installing-from-existing-configuration&quot;&gt;Installing from existing configuration:&lt;/h4&gt;
&lt;p&gt;Contrib solution: &lt;a href=&quot;https://www.drupal.org/project/config_installer&quot;&gt;Config installer&lt;/a&gt;&lt;br&gt;
Core issues: &lt;a href=&quot;https://www.drupal.org/node/1613424&quot;&gt;Allow a site to be installed from existing configuration&lt;/a&gt;, &lt;a href=&quot;https://www.drupal.org/node/2788777&quot;&gt;Allow a profile to be installed from existing config&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;environment-specific-configuration-ie-devel-only-on-develop&quot;&gt;Environment specific configuration (ie devel only on develop)&lt;/h4&gt;
&lt;p&gt;Contrib solution: &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Config Split&lt;/a&gt;&lt;br&gt;
Core issues: &lt;a href=&quot;https://www.drupal.org/node/2844681&quot;&gt;Allow exported configuration to be environment-specific&lt;/a&gt;, &lt;a href=&quot;https://www.drupal.org/node/2830300&quot;&gt;Allow development modules to opt out of config-export&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;configuration-with-content&quot;&gt;Configuration with Content&lt;/h4&gt;
&lt;p&gt;Contrib solution: &lt;a href=&quot;https://www.drupal.org/project/default_content&quot;&gt;Default Content&lt;/a&gt;, &lt;a href=&quot;https://www.drupal.org/project/deploy&quot;&gt;Deploy&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;horizontal&quot;&gt;Horizontal&lt;/h2&gt;
&lt;p&gt;Due to the fact that the same tools can be used for both dimensions and the fact that in Drupal 7 features was abused for doing the vertical configuration management as well this concept may not be so clear. Ideally configuration between sites is shared from the development environment of one site to the development environment of another, and the vertical tools with drupal core are used for deployment. Moving configuration between different sites is done by moving a subset of configuration between environments.&lt;/p&gt;
&lt;h4 id=&quot;re-using-a-set-of-configuration-in-another-site&quot;&gt;Re-using a set of configuration in another site&lt;/h4&gt;
&lt;p&gt;Contrib Solution: &lt;a href=&quot;https://www.drupal.org/project/features&quot;&gt;Features&lt;/a&gt;&lt;br&gt;
There are many more modules designed to deal with distributions and dealing with the paradigm that sites now own the configuration.&lt;/p&gt;
&lt;h4 id=&quot;inheriting-installation-profiles&quot;&gt;Inheriting installation profiles&lt;/h4&gt;
&lt;p&gt;Core issue: &lt;a href=&quot;https://www.drupal.org/node/1356276&quot;&gt;Allow profiles to provide a base/parent profile&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;multisite-with-large-portion-of-shared-configuration&quot;&gt;Multisite with large portion of shared configuration&lt;/h4&gt;
&lt;p&gt;Contrib Solution: &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Config Split&lt;/a&gt;&lt;br&gt;
While this is not the original problem it tries to solve, &lt;a href=&quot;https://evolvingweb.ca/blog/drupal-8-configuration-management-multi-site&quot;&gt;it is reported to be used for it.&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I may have an obvious bias towards config split since we maintain that module but it is just one of &lt;a href=&quot;https://www.drupal.org/search/site/config?f%5B0%5D=&amp;#x26;f%5B1%5D=&amp;#x26;f%5B2%5D=&amp;#x26;f%5B3%5D=drupal_core%3A7234&amp;#x26;f%5B4%5D=sm_field_project_type%3Afull&amp;#x26;f%5B5%5D=&amp;#x26;f%5B6%5D=ss_meta_type%3Amodule&amp;#x26;solrsort=score+desc&quot;&gt;many config related modules&lt;/a&gt;. I hope there is a fruitful discussion in Baltimore about configuration management in Drupal 8. &lt;a href=&quot;https://events.drupal.org/baltimore2017/bofs/beyond-features-all-things-config&quot;&gt;BoF&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;related-blog-posts&quot;&gt;Related blog posts:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chapterthree.com/blog/installing-drupal-8-from-configuration&quot;&gt;https://www.chapterthree.com/blog/installing-drupal-8-from-configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chromatichq.com/blog/managing-complex-configuration-drupal-8&quot;&gt;https://chromatichq.com/blog/managing-complex-configuration-drupal-8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.liip.ch/archive/2017/04/07/advanced-drupal-8-cmi-workflows.html&quot;&gt;https://blog.liip.ch/archive/2017/04/07/advanced-drupal-8-cmi-workflows.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Stable release for Config Split and Config Filter</title><link>https://www.nuvole.org/blog/stable-release-config-split-and-config-filter/</link><guid isPermaLink="true">https://www.nuvole.org/blog/stable-release-config-split-and-config-filter/</guid><pubDate>Mon, 21 Aug 2017 08:39:04 GMT</pubDate><content:encoded>&lt;p&gt;One year ago we released the first public version of &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Config Split&lt;/a&gt; with the goal to simplify working with Drupal cores configuration management.
The main motivation was to find a solution for having development modules in local environments and avoiding to deploy their configuration. To achieve this in the cleanest way possible we decided to interact with Drupal only during the configuration import and export operations by altering what is read from and written to the \Drupal\Core\Config\StorageInterface.&lt;/p&gt;
&lt;p&gt;We quickly realized that this is a powerful way to interact with how Drupal sees the configuration to import and so we split off the code that does the heavy lifting into its own module and now &lt;a href=&quot;https://www.drupal.org/project/config_ignore&quot;&gt;Config Ignore&lt;/a&gt; and &lt;a href=&quot;https://www.drupal.org/project/config_role_split&quot;&gt;Config Role Split&lt;/a&gt; use the same mechanism to do their thing.&lt;/p&gt;
&lt;p&gt;Config Split now has &lt;a href=&quot;https://www.drupal.org/docs/8/modules/configuration-split&quot;&gt;documentation pages&lt;/a&gt; you are welcome to contribute to and there will be a &lt;a href=&quot;https://events.drupal.org/vienna2017/sessions/advanced-configuration-management-config-split-et-al&quot;&gt;session at DrupalCon&lt;/a&gt; in Vienna in which I will show how it can be used to manage a sites configuration in interesting ways.&lt;/p&gt;
&lt;p&gt;If you were an early adopter (pre beta3) but have not updated to a recent version, you will need to install Config Filter first or &lt;a href=&quot;https://www.drupal.org/node/2863986&quot;&gt;patch core&lt;/a&gt;. The workaround and legacy code has now been removed and the current code is going to be backwards compatible in the future. So if you use the APC class loader, make sure to clear the apc/apcu cache or you might get an &lt;a href=&quot;https://www.drupal.org/node/2877158&quot;&gt;error after updating&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>How to maintain Drush commands for Drush 8 and 9 and Drupal console with the same code base.</title><link>https://www.nuvole.org/blog/how-maintain-drush-commands-drush-8-and-9-and-drupal-console-same-code-base/</link><guid isPermaLink="true">https://www.nuvole.org/blog/how-maintain-drush-commands-drush-8-and-9-and-drupal-console-same-code-base/</guid><pubDate>Fri, 13 Oct 2017 08:48:40 GMT</pubDate><content:encoded>&lt;p&gt;Drupal 8.4 and its upgrade to Symfony 3 has made the compatibility of the global Drush 8 a bit more challenging. Drush 9 works with Drupal 8.4 but it is not stable yet and the format of how third party Drush commands are made has changed significantly.&lt;/p&gt;
&lt;p&gt;While Drush 9 comes with a command that helps porting Drush 8 commands you will still end up maintaining very similar code in two places one with calls to &lt;code&gt;drush_confirm(&apos;...&apos;)&lt;/code&gt; and one with &lt;code&gt;$this-&gt;io()-&gt;confirm(&apos;...&apos;)&lt;/code&gt;. If you decide to also provide your commands for Drupal console you now have three times the burden.&lt;/p&gt;
&lt;p&gt;Because we tried to provide the commands for Config Split for both Drush and Drupal console early on we faced this problem already more than a year ago. And now it has paid off because porting the commands to Drush 9 was very quick.&lt;/p&gt;
&lt;p&gt;The solution is actually really simple and brings the added benefit of being able to test the business logic of the commands in the absence of Drush or Drupal console. It is all about separating the command discovery from the command logic. Drush 8, 9 and Drupal console all have a bit different ways to discover and invoke commands, but the business logic you want to implement is the same so all we have to do is to extract a common “interface” our custom service can implement and then make the command definitions wrap that and keep things DRY.&lt;/p&gt;
&lt;h2 id=&quot;the-cliservice&quot;&gt;The CliService&lt;/h2&gt;
&lt;p&gt;Config Split defines a &lt;code&gt;config_split.cli&lt;/code&gt; service with the class &lt;a href=&quot;https://github.com/nuvoleweb/config_split/blob/8.x-1.x/src/ConfigSplitCliService.php&quot;&gt;ConfigSplitCliService&lt;/a&gt; with all its dependencies injected. It has the methods &lt;a href=&quot;https://github.com/nuvoleweb/config_split/blob/8.x-1.x/src/ConfigSplitCliService.php#L180&quot;&gt;\Drupal\config_split\ConfigSplitCliService::ioExport&lt;/a&gt; and &lt;a href=&quot;https://github.com/nuvoleweb/config_split/blob/8.x-1.x/src/ConfigSplitCliService.php#L214&quot;&gt;\Drupal\config_split\ConfigSplitCliService::ioImport&lt;/a&gt; that implements all the commands logic and delegate the actual importing and exporting to specific methods.&lt;/p&gt;
&lt;p&gt;The method signature for both the export and import method are more or less the same: &lt;code&gt;CliService::ioMethod($arguments, $io, callable $t)&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$arguments&lt;/strong&gt;: The arguments passed to the command.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$io&lt;/strong&gt;: This is an object that interacts with the command line, in Drush 9 and Drupal console this comes from the Symfony console component, for Drush 8 we created a custom wrapper around &lt;code&gt;drush_confirm&lt;/code&gt; and &lt;code&gt;drush_log&lt;/code&gt; called &lt;a href=&quot;https://github.com/nuvoleweb/config_split/blob/8.x-1.x/config_split.drush.inc#L76&quot;&gt;ConfigSplitDrush8Io&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$t&lt;/strong&gt;: This is essentially a &lt;code&gt;t&lt;/code&gt; function akin to how Drupal translates strings. Because Drupal console translates things differently we had to be a bit creative with that by adding a t method to the command.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;commands-wrap-the-service&quot;&gt;Commands wrap the service&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/nuvoleweb/config_split/blob/8.x-1.x/config_split.drush.inc#L46&quot;&gt;Drush 8 command&lt;/a&gt; is essentially:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; drush_config_split_export&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($split &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; NULL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Make the magic happen.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  \Drupal&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;service&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;config_split.cli&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ioExport&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($split, &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ConfigSplitDrush8Io&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dt&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Drush 9 we can use dependency injection and the &lt;a href=&quot;https://github.com/nuvoleweb/config_split/blob/8.x-1.x/src/Commands/ConfigSplitCommands.php#L48&quot;&gt;Drush 9 command&lt;/a&gt; becomes essentially:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ConfigSplitCommands&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; DrushCommands&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; splitExport&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($split &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; NULL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;cliService&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ioExport&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($split, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;io&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dt&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And very similar the &lt;a href=&quot;https://github.com/nuvoleweb/config_split/blob/8.x-1.x/src/Command/ExportCommand.php#L38&quot;&gt;Drupal console command&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ExportCommand&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; SplitCommandBase&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  protected&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; execute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;InputInterface&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $input, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;OutputInterface&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $output) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setupIo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($input, $output);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Make the magic happen.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;cliService&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ioExport&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;($input&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getOption&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;split&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getIo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(), [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;t&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/nuvoleweb/config_split/blob/8.x-1.x/tests/src/Kernel/ConfigSplitCliServiceTest.php#L101&quot;&gt;ConfigSplitCliServiceTest&lt;/a&gt; is a KernelTest which asserts that the export works as expected by exporting to a virtual file system. The test coverage is not 100% (patches welcome) but the most important aspects for the complete and conditional splitting (blacklist/graylist) is thoroughly tested.
There are no limitations on what or how you can test your CliService since it is self contained in your module and does not depend on Drush or the Drupal console. For example one could write a unit test with a mocked $io object that asserts that the messages printed to the cli are correct.&lt;/p&gt;</content:encoded></item><item><title>Config Distro</title><link>https://www.nuvole.org/blog/config-distro/</link><guid isPermaLink="true">https://www.nuvole.org/blog/config-distro/</guid><pubDate>Mon, 09 Apr 2018 16:06:25 GMT</pubDate><content:encoded>&lt;h2 id=&quot;drupal-core-workflow-limitations&quot;&gt;Drupal core workflow limitations&lt;/h2&gt;
&lt;p&gt;It is not a secret that the configuration management in Drupal 8 core was made for only one specific use case: move configuration from one environment to another for the same site. Everything else was left for contrib to find solutions for. &lt;a href=&quot;https://www.drupal.org/project/config_installer&quot;&gt;Config Installer&lt;/a&gt; is necessary to set up a site starting from existing configuration and &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Config Split&lt;/a&gt; is needed to have environment specific configuration that go beyond simple configuration overrides, like development modules enabled only locally. But the whole workflow still assumes that developers - most often in the same team - have control over the environments and deployment of the site.&lt;/p&gt;
&lt;h2 id=&quot;distributions-are-not-covered&quot;&gt;Distributions are not covered&lt;/h2&gt;
&lt;p&gt;Distributions are very different. When maintaining a distribution one doesn’t develop a specific site but rather the template to build different sites with. Deploying configuration as we do it for a single site does not have any meaning because there is no one production site to deploy to. Each site based off a distribution is its own site with its own site uuid and its own possible customisations and possibly its own development and production environments. At the same time as a consumer of a distribution - a site owner of a site based off a distribution - one wants to update the distributions features while keeping the customisations.&lt;/p&gt;
&lt;h2 id=&quot;update-hooks&quot;&gt;Update hooks?&lt;/h2&gt;
&lt;p&gt;Different distributions handle this in different ways. A first approach to solve this issue is treating it the same way you would a schema update and change the configuration on a site with an update hook. The problem with this is that update hooks are meant to fix the database to be able to run with the version of code that is in use on the site. The first thing one needs to do after updating the code base is to run the update hooks to realign the database. Configuration management has different needs and when updating the configuration in an update hooks means that one has to be careful to check that the configuration is in the expected state. And the only recourse is to log and make sure the user is informed what manual actions he needs to do to update the configuration himself. As there is no place to involve the user with a choice while update hooks are run.&lt;/p&gt;
&lt;h1 id=&quot;config-distro&quot;&gt;Config Distro&lt;/h1&gt;
&lt;p&gt;We propose a new kind of workflow, in essence it allows developers of a distribution to say how the “distributions configuration” is supposed to be updated and it allows site owners of a site based on a distribution to treat the distribution maintainers as a developer on their team. At the same time this does not interfere with the configuration management workflow for staging and deploying configuration to the production site.&lt;/p&gt;
&lt;p&gt;The primary function of &lt;a href=&quot;https://www.drupal.org/project/config_distro&quot;&gt;Config Distro&lt;/a&gt; is to allow updating a distributions configuration in a new workflow and give the site owners more control and insight on how the configuration of their site changes. It is a new way to imagine the configuration management system in a way that lets sites own their configuration but allowing distributions to provide updated configuration for existing sites.&lt;/p&gt;
&lt;h2 id=&quot;a-new-dimension&quot;&gt;A new dimension&lt;/h2&gt;
&lt;p&gt;In &lt;a href=&quot;https://nuvole.org/blog/2017/apr/25/configuration-management-dimensions&quot;&gt;a blog post&lt;/a&gt; one year ago I talked about different dimensions of configuration management;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vertical: moving configuration between environments but for the same site&lt;/li&gt;
&lt;li&gt;horizontal: moving configuration between different sites. for example for distribution updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://www.drupal.org/project/config_distro&quot;&gt;Config Distro&lt;/a&gt; is a UI and a drush command for that workflow. It is intended to be used by site builders who base their site on a distribution and want to import configuration updates from the updated distribution.
The UI is essentially a parallel to configuration import screen for the workflow supported by core. But instead of importing the configuration from the files used to move/deploy configuration between environments you import from a special configuration storage which starts out with the sites active configuration and has the distributions updates applied to it. This means you do not have problems with mismatched site uuids or things being removed that you added and were not part of the distribution. And updates can apply (or not) to translations even if the distribution did not have languages.&lt;/p&gt;
&lt;h2 id=&quot;where-do-updates-come-from&quot;&gt;Where do updates come from&lt;/h2&gt;
&lt;p&gt;If you only install Config Distro, the import screen eternally shows that there is nothing to import. This is because the module only provides the framework for this to work, ie the UI and the drush command. The module is not opinionated on how or what should be updated. All the complexity can be addressed by a distribution maintainer with the help of a ConfigFilter plugin (the same way Config Split and Config Ignore work). One such module is &lt;a href=&quot;https://www.drupal.org/project/config_sync&quot;&gt;Config Sync&lt;/a&gt;. All the complexity of finding out what what configuration a module has originally shipped with, what it does now and whether a user has changed the originally installed configuration is left to Config Sync and its dependencies.&lt;/p&gt;
&lt;h2 id=&quot;choice&quot;&gt;Choice&lt;/h2&gt;
&lt;p&gt;Just like Config Ignore allows you to opt out of some of the configuration management workflows, Config Distro has a Config Distro Ignore module that lets you retain certain configuration from being changed when you hit the “import” button. The “Retain configuration” button is available right next to the “View differences” button.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;config_distro_retain1.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Clicking it leads to a form that lets you choose to retain the configuration permanently or only for this specific update. It also allows you to ignore a update for a specific language.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;config_distro_retain2.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;
&lt;p&gt;In our team we set up a site based on a distribution. We added our own login module for our single sign on and added a few webforms. Now there is a new version of the distribution with some new features and I would like upgrade the site to using the new features. I am tasked with updating the site, here is what I do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I update the code of the distribution by specifying the new version in &lt;code&gt;composer.json&lt;/code&gt; and do a &lt;code&gt;composer update&lt;/code&gt; to get the updated code &lt;strong&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;I run the database updates &lt;code&gt;drush updb&lt;/code&gt; to align the database with the code &lt;strong&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;I go to the distro update screen provided by Config Distro&lt;br&gt;
- This screen looks very familiar, it looks the same way as when I import configuration changes my teammates made after I get their code with &lt;code&gt;git pull&lt;/code&gt;.&lt;br&gt;
- I see a new module is going to be installed and a few views and settings from the distribution are updated.&lt;br&gt;
- Our webforms are not going to be removed and our custom modules not uninstalled.&lt;/li&gt;
&lt;li&gt;I click “import all”.&lt;/li&gt;
&lt;li&gt;Now my site has the updated code and configuration, so I export the configuration for deployment: &lt;code&gt;drush cex&lt;/code&gt; and commit &lt;strong&gt;*&lt;/strong&gt;&lt;br&gt;
- &lt;code&gt;git pull&lt;/code&gt; to get the code.&lt;br&gt;
- &lt;code&gt;composer install&lt;/code&gt; to get the external code (vendor, contrib, etc).&lt;br&gt;
- &lt;code&gt;drush updb&lt;/code&gt; to align the database with the new code base.&lt;br&gt;
- &lt;code&gt;drush cim&lt;/code&gt; to import the sync configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;This is the same procedure as for any module update on any site.&lt;br&gt;
Distributions may add a “Auto upgrade” setting and then import the Config Distro in a post_update_hook to bypass the manual step required of site administrators upgrading the distribution.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Config Distro provides a new workflow for updating distributions with a familiar feel. It recognizes that update hooks are not an adequate solution for updating a sites configuration when a site owns the configuration and site administrators may have changed the initially provided configuration. &lt;br&gt;
It allows developers of distributions to alter the sites configuration through ConfigFilter plugins and it gives site administrators a choice of what to import.&lt;br&gt;
Config Distro is just the framework for extending cores configuration management to allow managing configuration changes to get into the site from a third party such as the distribution maintainers. It does not interfere with the traditional workflow and importing the configuration updates from a distribution should be seen as any other configuration change such as adding a new view or changing permissions: You go to an admin page and you click some things in a form, then you export the configuration and deploy it.&lt;/p&gt;
&lt;h2 id=&quot;future---cmi-20&quot;&gt;Future - CMI 2.0&lt;/h2&gt;
&lt;p&gt;While it already works, Config Distro is still in an alpha state. Config Distro is part of a larger effort to improve the configuration management in Drupal. You can find further information and participate in the discussion on &lt;a href=&quot;https://www.drupal.org/project/ideas/issues/2957423&quot;&gt;drupal.org&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>CMI 2.0 issues need your review.</title><link>https://www.nuvole.org/blog/cmi-20-issues-need-your-review/</link><guid isPermaLink="true">https://www.nuvole.org/blog/cmi-20-issues-need-your-review/</guid><pubDate>Wed, 06 Mar 2019 09:08:56 GMT</pubDate><content:encoded>&lt;p&gt;The configuration management initiative 2.0 needs your help.&lt;/p&gt;
&lt;p&gt;I will be presenting updates of CMI 2.0 at the upcoming &lt;a href=&quot;https://events.drupal.org/seattle2019/sessions/configuration-management-initiative-20-updates&quot;&gt;Drupalcon Seattle&lt;/a&gt; together
with Mike Potter.&lt;/p&gt;
&lt;p&gt;Some of the highlights of the &lt;a href=&quot;https://www.drupal.org/project/cmi2&quot;&gt;CMI 2.0&lt;/a&gt; road map for inclusion in Drupal core are an improved version of the concept of &lt;a href=&quot;https://www.drupal.org/project/config_filter&quot;&gt;Config
Filter&lt;/a&gt; (in the form of the &lt;a href=&quot;https://www.drupal.org/project/drupal/issues/2991683&quot;&gt;config storage transformation api&lt;/a&gt;) and a simplified
version of &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Config Split&lt;/a&gt; (in the form of &lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3028179&quot;&gt;config environments&lt;/a&gt;).&lt;br&gt;
Unfortunately those big things will not make it into 8.7, but we could lay the ground work for 8.8.&lt;/p&gt;
&lt;p&gt;But the &lt;a href=&quot;https://groups.drupal.org/node/534845&quot;&gt;deadline for some patches for Drupal 8.7&lt;/a&gt; is this Friday!&lt;/p&gt;
&lt;p&gt;It would be great if you could help us get the following issues to RTBC and committed:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3036193&quot;&gt;#3036193: Add ExportStorageFactory to allow config export in third party tools&lt;/a&gt;&lt;br&gt;
This would allow us to add a service to core that drush, drupal console and other tools and modules can use to export configuration. If this lands in 8.7 we will be able to patch
Drush and Drupal Console between 8.7 and 8.8 and make improvements to configuration management such as adding a &lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3028179&quot;&gt;Config
Environment&lt;/a&gt; module to core without then patching the cli tools again afterwards.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3016429&quot;&gt;#3016429: Add a config storage copy utility trait&lt;/a&gt;&lt;br&gt;
This adds a new utility trait that would make dealing with configuration storages easier. Currently there are a bunch of modules that implement this logic by themselves and not all
of them do it correctly. This lead to bugs in Drush and Drupal Console and even Drupal core has a bug (which is fixed by this issue).&lt;/p&gt;
&lt;p&gt;Thanks already in advance.&lt;/p&gt;
&lt;p&gt;PS: If you are interested in more CMI 2.0 issues to review or work on check the &lt;a href=&quot;https://www.drupal.org/project/issues/search?issue_tags=CMI%202.0%20candidate&quot;&gt;CMI 2.0 candidate&lt;/a&gt; issue tag.&lt;/p&gt;</content:encoded></item><item><title>CMI 2.0 at Drupalcon Seattle 2019</title><link>https://www.nuvole.org/blog/cmi-20-drupalcon-seattle-2019/</link><guid isPermaLink="true">https://www.nuvole.org/blog/cmi-20-drupalcon-seattle-2019/</guid><pubDate>Tue, 09 Apr 2019 17:08:51 GMT</pubDate><content:encoded>&lt;p&gt;Tomorrow Wednesday at 13:45 (Pacific Daylight Time) in room 6C we have the &lt;a href=&quot;https://events.drupal.org/seattle2019/sessions/configuration-management-initiative-20-updates&quot;&gt;CMI 2.0 updates session&lt;/a&gt; at the Drupalcon.&lt;/p&gt;
&lt;p&gt;Together with Mike Potter, we will briefly present the work that has been done so far and lay out the road map for what is to come. The session will be recorded and the slides are attached to this post (subject to change).&lt;/p&gt;
&lt;p&gt;I will not go more into details of the session in this post but I would like to mention a challenge for which we need to find a solution and I would like everyone to invite to join the discussion on drupal.org &lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3046903&quot;&gt;#3046903&lt;/a&gt; or here in the comments.&lt;/p&gt;
&lt;p&gt;The challenge is how to deal with update hooks that change configuration which run only in certain environments and thus make the configuration deployment more difficult.&lt;/p&gt;
&lt;p&gt;Click &lt;a href=&quot;/sites/default/files/CMI%202.0%20DC%20Seattle%202019.pdf&quot;&gt;here&lt;/a&gt; to download the presentation slides.&lt;/p&gt;</content:encoded></item><item><title>CMI 2.0 session at Drupaldevdays in Cluj-Napoca</title><link>https://www.nuvole.org/blog/cmi-20-session-drupaldevdays-cluj-napoca/</link><guid isPermaLink="true">https://www.nuvole.org/blog/cmi-20-session-drupaldevdays-cluj-napoca/</guid><pubDate>Thu, 13 Jun 2019 14:25:47 GMT</pubDate><content:encoded>&lt;p&gt;Today I presented the CMI 2.0 updates at the drupal dev days in Cluj-Napoca.
The session went well and I received good feedback and had interesting conversations about configuration management after it.&lt;/p&gt;
&lt;p&gt;Attached are the slides I presented.&lt;/p&gt;
&lt;p&gt;There are plenty of issues to work on, join us in the sprint room or on the #config drupal slack.
Find more information on the &lt;a href=&quot;https://www.drupal.org/project/cmi2&quot;&gt;CMI 2 project page&lt;/a&gt; and issues tagged with &lt;a href=&quot;https://www.drupal.org/project/issues/search?issue_tags=CMI%202.0%20candidate&quot;&gt;CMI 2.0 candiate&lt;/a&gt;&lt;/p&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/lks6yLBQeyBvXF&quot; width=&quot;595&quot; height=&quot;485&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;div style=&quot;margin-bottom:5px&quot;&gt; &lt;strong&gt; &lt;a href=&quot;//www.slideshare.net/nuvoleweb/cmi-20-session-at-drupal-devdays-in-clujnapoca&quot; title=&quot;CMI 2.0 session at Drupal DevDays in Cluj-Napoca&quot; target=&quot;_blank&quot;&gt;CMI 2.0 session at Drupal DevDays in Cluj-Napoca&lt;/a&gt; &lt;/strong&gt; from &lt;strong&gt;&lt;a href=&quot;https://www.slideshare.net/nuvoleweb&quot; target=&quot;_blank&quot;&gt;Nuvole&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;
&lt;p&gt;Click &lt;a href=&quot;/sites/default/files/CMI%202.0%20Devdays%202019.pdf&quot;&gt;here&lt;/a&gt; to download the presentation slides.&lt;/p&gt;</content:encoded></item><item><title>Highlights from CMI 2.0 in Drupal 8.8.0</title><link>https://www.nuvole.org/blog/highlights-cmi-20-drupal-880/</link><guid isPermaLink="true">https://www.nuvole.org/blog/highlights-cmi-20-drupal-880/</guid><pubDate>Thu, 10 Oct 2019 08:35:59 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Nuvole offers a training at DrupalCon Amsterdam: “&lt;a href=&quot;https://events.drupal.org/amsterdam2019/drupal-8-migration-process&quot;&gt;Drupal 8 Migration as a process&lt;/a&gt;” - &lt;a href=&quot;https://events.drupal.org/amsterdam2019/training&quot;&gt;register until October 27&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are a few significant changes that landed in Drupal 8.8 with respect to configuration management:&lt;/p&gt;
&lt;h2 id=&quot;the-sync-directory-is-defined-in-settings-and-not-config_directories&quot;&gt;&lt;a href=&quot;https://www.drupal.org/node/3018145&quot;&gt;The sync directory is defined in $settings and not $config_directories&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first one concerns everybody using Drupal 8 and especially going forward Drupal 9.
So far the config directory was set up with in settings.php with the variable &lt;code&gt;$config_directories&lt;/code&gt;. This variable was an array for historical reasons when both the active configuration and the sync configuration were stored in files. But since Drupal 8.0.0 that was not the case any more, yet the array remained. Drupal only ever used one key, but developers saw an array and naturally assumed that there should be more than one key.&lt;/p&gt;
&lt;p&gt;In the effort to avoid confusion and to streamline the configuration management we have now corrected this and added the config directory to the settings variable as &lt;code&gt;$settings[&apos;config_sync_directory&apos;]&lt;/code&gt;. If for some reason someone still wants to have a workflow with multiple sync directories then that can still be achieved with a small amount of code since setting.php is a php file and the settings can be set conditionally. In my opinion though one sync directory is sufficient for the vast majority of workflows and this change will help to not confuse developers.&lt;/p&gt;
&lt;h2 id=&quot;event-is-dispatched-before-configuration-import-and-export-to-transform-the-configuration&quot;&gt;&lt;a href=&quot;https://www.drupal.org/node/3066005&quot;&gt;Event is dispatched before configuration import and export to transform the configuration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For me this is the biggest achievement for CMI 2. This allows contrib and custom modules to interact with the configuration synchronization in a very straight forward way. It is Config Filter in core but better, it is what I have been talking about at many Drupal events and finally it is in Drupal core, stable and available to everyone.
There is already a &lt;a href=&quot;https://www.drupal.org/project/config_filter/issues/3050529&quot;&gt;patch&lt;/a&gt; for Config Filter 2.x to be the bridge between modules depending on Config Filter and the new core API. Config Filter 2.x will be api compatible with Config Filter 1.x but instead of decorating the sync storage it will apply the filter plugins to the new core API.
In addition there is &lt;a href=&quot;https://www.drupal.org/project/config_distro/issues/3080778&quot;&gt;an issue&lt;/a&gt; for Config Distro on the way to model its API after the new event driven core API.&lt;/p&gt;
&lt;h2 id=&quot;modules-can-be-excluded-from-the-configuration-synchronization&quot;&gt;&lt;a href=&quot;https://www.drupal.org/node/3079028&quot;&gt;Modules can be excluded from the configuration synchronization&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The latest major achievement which will be celebrated by many will be that it is finally possible to have devel installed on the development environment and not having to worry about it being deployed. This feature is already available in contrib since December 2017 with the contrib module &lt;a href=&quot;https://www.drupal.org/project/config_exclude&quot;&gt;Config Exclude&lt;/a&gt;. It uses Config Filter and is a relatively simple module with just one configuration option. This is why it was a good candidate to add to core as a proof that the new API works. The contrib module and the new core feature (yes you don’t need to install a module it just works) both use the same setting:
&lt;code&gt;$settings[&apos;config_exclude_modules&apos;] = [&apos;devel&apos;, &apos;stage_file_proxy&apos;];&lt;/code&gt;
They work both alongside together so if you want to use this feature you don’t have to wait for 8.8, you can use it on 8.6 and then when you upgrade to 8.8 you can simply uninstall Config Exclude and it just continues to work.&lt;/p&gt;
&lt;p&gt;There has been some confusion about this feature and how it can be dangerous.
The excluded modules, (ie the modules listed in the settings array) are removed from core.extension.yml and all the config depending on them is not exported. When importing the configuration and the module is installed already the configuration from the site is added to the configuration to import just before validating the configuration for the import. That means that devel will not be uninstalled when you import the configuation that doesn’t have devel in it. But it also means that the configuration that you exported is no longer the configuration you had when you devloped your site and if you exclude modules that are central to the site such as required modules such as system or user or modules that change a lot of configuration such as language or field then the configuration can not be imported on the target site any more. Your site will not break, but you can not deploy the new configuration.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://drupal.kuoni-congress.info/2019/program/abstract/308&quot;&gt;CMI 2.0 Session at DrupalCon Amsterdam&lt;/a&gt; will contain more about all of the above and what else is planned to come.&lt;/p&gt;</content:encoded></item><item><title>A middle-format approach for complex and incremental Drupal 8 migrations</title><link>https://www.nuvole.org/blog/middle-format-approach-for-complex-and-incremental-drupal-8-migrations/</link><guid isPermaLink="true">https://www.nuvole.org/blog/middle-format-approach-for-complex-and-incremental-drupal-8-migrations/</guid><pubDate>Sun, 20 Oct 2019 16:29:30 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Nuvole offers a training at DrupalCon Amsterdam: “&lt;a href=&quot;https://events.drupal.org/amsterdam2019/drupal-8-migration-process&quot;&gt;Drupal 8 Migration as a process&lt;/a&gt;” - &lt;a href=&quot;https://events.drupal.org/amsterdam2019/training&quot;&gt;register until October 27&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When having to import data onto a Drupal 8 site there is no other choice than relying on core’s &lt;a href=&quot;https://www.google.com/url?q=https://www.drupal.org/docs/8/api/migrate-api&amp;#x26;sa=D&amp;#x26;ust=1571576341123000&quot;&gt;Migrate API&lt;/a&gt; and its contrib ecosystem. The Migrate API in Drupal 8 implements a rather classic &lt;a href=&quot;http://www.google.com/url?q=http://wikipedia.org/wiki/Extract,_transform,_load&amp;#x26;sa=D&amp;#x26;ust=1571576341124000&quot;&gt;Extract, Transform, Load (ETL) process&lt;/a&gt;, with the following “Drupal-lingo” twists:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The extract phase is called “source” and it uses a source plugin to read data from external systems, be it a Drupal 7 database, a CSV file, a REST web service, etc.&lt;/li&gt;
&lt;li&gt;The transform phase is called “process” and it uses process plugins to process and transform data&lt;/li&gt;
&lt;li&gt;The load phase is called “destination” and it uses destination plugins to import data into specific Drupal 8 entity storages (e.g. nodes, taxonomy terms, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To recap, Drupal core implements the ETL process as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Source plugins extract the data from the source.&lt;/li&gt;
&lt;li&gt;Process plugins transform the data.&lt;/li&gt;
&lt;li&gt;Destination plugins save the data as Drupal 8 entities.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;migration-standard-process.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;limitations-of-standard-drupal-8-etl-process&quot;&gt;Limitations of standard Drupal 8 ETL process&lt;/h2&gt;
&lt;p&gt;In the process described above the three steps are executed at the same moment in time, sequentially, every time we run a migration import. In this scenario we are to lose one of the most valuable aspects of an ETL process: testing and validating data prior to import.&lt;/p&gt;
&lt;p&gt;Also, the process above easily accommodates for multiple data sources to be consolidated into one, coherent, dataset. With Drupal Migrate API this is only possible by using chained source plugin, and it can only run on the destination site. This is quite a limitation in complex enterprise scenarios, where displayed data is often the result of complex and convoluted transformations in the backend.&lt;/p&gt;
&lt;h2 id=&quot;a-middle-format-approach&quot;&gt;A middle-format approach&lt;/h2&gt;
&lt;p&gt;At Nuvole we have adopted a so called “middle-format approach”. The process simply moves the Extract and Transform parts outside Drupal so to ease the production of an easy to import dataset, in a well known middle-format (such as JSON API).&lt;/p&gt;
&lt;p&gt;This approach has proved very successful in complex scenarios and, while completely leveraging the standard Drupal 8 migration process, it also allows to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Aggregate data from different sources (not only Drupal 7 databases)&lt;/li&gt;
&lt;li&gt;Test the data transformation process&lt;/li&gt;
&lt;li&gt;Test imported Drupal 8 data&lt;/li&gt;
&lt;li&gt;Easily automate the points above&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The process looks like the following:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;migration-middle-format-process.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The approach outlined above brings the following benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exported data can be reviewed by the client and iteratively refined&lt;/li&gt;
&lt;li&gt;Since Drupal 8 is not a precondition to export and transform data, data export site building can run in parallel, making the whole migration process much more efficient&lt;/li&gt;
&lt;li&gt;Exported data can be presented to the different stakeholders using a user friendly UI, even before starting any Drupal 8 development&lt;/li&gt;
&lt;li&gt;Since the data uses a well-known middle-format building the import process (as Drupal core’s Migrate plugins) is straightforward and allows to maximize code reusability&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;middle-format-migration-at-work&quot;&gt;Middle-format migration at work&lt;/h2&gt;
&lt;p&gt;At Nuvole we have successfully used the middle-format approach to incrementally transform, review and import data over several years, on complex sites such as the &lt;a href=&quot;https://wfp.org&quot;&gt;World Food Programme main website&lt;/a&gt;. In that scenario we had to consolidate data coming from two Drupal 7 sites (plus a number of external data sources) in 13 different languages, over a two years period.&lt;/p&gt;
&lt;p&gt;We have recently open-sourced a simplified version of the tool we have used to export and transform data, you can find its boilerplate code &lt;a href=&quot;https://github.com/nuvoleweb/drupal-middle-format-migration&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We will run an hands-on session on how to use such a tool to plan and execute data exports in our upcoming &lt;a href=&quot;https://events.drupal.org/amsterdam2019/drupal-8-migration-process&quot;&gt;DrupalCon Amsterdam training&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>DrupalCon CMI 2 Session slides</title><link>https://www.nuvole.org/blog/drupalcon-cmi-2-session-slides/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupalcon-cmi-2-session-slides/</guid><pubDate>Tue, 29 Oct 2019 13:42:38 GMT</pubDate><content:encoded>&lt;p&gt;Yesterday I presented the updates for the Configuration Management Initiative 2 at DrupalCon Amsterdam.&lt;/p&gt;
&lt;p&gt;The main takeaways are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.drupal.org/node/3018145&quot;&gt;The sync directory is defined in $settings and not $config_directories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.drupal.org/node/3066005&quot;&gt;Event is dispatched before configuration import and export to transform the configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.drupal.org/node/3079028&quot;&gt;Modules can be excluded from the configuration synchronization&lt;/a&gt;
You can find more explanation in our &lt;a href=&quot;https://nuvole.org/blog/2019/oct/10/highlights-cmi-20-drupal-880&quot;&gt;previous blog post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There were 180 people in the small room and many who wanted to join the session simply couldn’t fit any more.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;IMG_20191028_171704.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;drupalcon session photo&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is the recording from &lt;a href=&quot;https://www.youtube.com/watch?v=k-I3oOWHWVA&amp;#x26;list=PLpeDXSh4nHjSZET8xL2RyK3_2WeXxyWkY&quot;&gt;youtube&lt;/a&gt;:&lt;/p&gt;
&lt;iframe width=&quot;768&quot; height=&quot;432&quot; src=&quot;https://www.youtube-nocookie.com/embed/k-I3oOWHWVA&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;There is still a lot of work to be done for CMI 2, join us on the &lt;a href=&quot;https://events.drupal.org/amsterdam2019/contribution-events&quot;&gt;contribution day at DrupalCon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Attached are the &lt;a href=&quot;/sites/default/files/DrupalCon%20AMS%20CMI2.pdf&quot;&gt;slides of the session&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Have I seen you at DrupalCon before?</title><link>https://www.nuvole.org/blog/have-i-seen-you-drupalcon/</link><guid isPermaLink="true">https://www.nuvole.org/blog/have-i-seen-you-drupalcon/</guid><pubDate>Thu, 31 Oct 2019 21:56:01 GMT</pubDate><content:encoded>&lt;!-- Scripts and styles needed for graphs and tables below --&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;https://www.gstatic.com/charts/loader.js&quot;&gt;&lt;/script&gt;
&lt;style&gt;
  table, tr, td, th {
    border: 1px solid #999;
  }
  table {
    border-collapse: collapse;
  }
  td, th {
    padding: 5px;
    text-align: left;
  }
  td + td, th + th {
    text-align: right;
  }
  td + td + td, th + th + th {
    text-align: left;
  }
  .disclaimer {
    border: 5px solid rgb(100%, 50%, 50%);
    background: rgb(100%, 80%, 80%);
    padding: 10px;
    line-height: 1.5em;
  }
  .nuvole-member {
    color: #f00;
  }
&lt;/style&gt;
&lt;p&gt;Amsterdam is the first city to host three European DrupalCons: 2005 (first DrupalCon ever), 2014 and 2019 (ongoing). How many community members attended all three of them?&lt;/p&gt;
&lt;p&gt;We analyzed all user profiles published on &lt;a href=&quot;https://www.drupal.org/&quot;&gt;drupal.org&lt;/a&gt; to find out this and much more.&lt;/p&gt;
&lt;div class=&quot;disclaimer&quot;&gt;
Disclaimer: this is not serious research, just a fun experiment. It uses data from drupal.org user profiles that are known to be both unverified and outdated. If you take these data seriously, then Dries has never attended a DrupalCon. Who was that man on stage then? 😅
&lt;/div&gt;
&lt;h3 id=&quot;the-flow-amsterdam-to-amsterdam-to-amsterdam&quot;&gt;The flow: Amsterdam to Amsterdam to Amsterdam&lt;/h3&gt;
&lt;div id=&quot;sankey_multiple&quot; style=&quot;height: 600px;&quot;&gt;&lt;/div&gt;
&lt;script&gt;
  google.charts.load(&quot;current&quot;, { packages: [&quot;sankey&quot;] });
  google.charts.setOnLoadCallback(drawChart);
  function drawChart() {
    var data = new google.visualization.DataTable();
    data.addColumn(&apos;string&apos;, &apos;From&apos;);
    data.addColumn(&apos;string&apos;, &apos;To&apos;);
    data.addColumn(&apos;number&apos;, &apos;Weight&apos;);
    data.addRows([
      [ &apos;Never seen in AMS before&apos;, &apos;1st in 2005&apos;, 75+478+69],
      [ &apos;Never seen in AMS before&apos;, &apos;1st in 2014&apos;, 1475+123],
      [ &apos;Never seen in AMS before&apos;, &apos;1st in 2019&apos;, 177],
      [ &apos;1st in 2005&apos;, &apos;2nd in 2014&apos;, 75 ],
      [ &apos;1st in 2005&apos;, &apos;Never seen in AMS again&apos;, 478 ],
      [ &apos;1st in 2005&apos;, &apos;2nd in 2019&apos;, 69 ],
      [ &apos;1st in 2014&apos;, &apos;Never seen in AMS again&apos;, 1475 ],
      [ &apos;1st in 2014&apos;, &apos;2nd in 2019&apos;, 123 ],
      [ &apos;2nd in 2014&apos;, &apos;Never seen in AMS again&apos;, 69 ],
      [ &apos;2nd in 2014&apos;, &apos;3rd in 2019&apos;, 6 ],
    ]);
    // Set chart options
    var options = {
      sankey: {
        node: {
          nodePadding: 60,
          label: {
              fontName: &apos;Roboto&apos;,
              fontSize: 18,
              color: &apos;#871b47&apos;,
              bold: true,
              italic: false
          }
        }
      },
    };
    var chart = new google.visualization.Sankey(document.getElementById(&apos;sankey_multiple&apos;));
    chart.draw(data, options);
  }
&lt;/script&gt;
&lt;p&gt;You can hover/tap on the chart to see details. Apparently, less than 200 people from the current DrupalCon Amsterdam had never been seen before at a DrupalCon in Amsterdam, and a huge amount of those seen in Amsterdam for the first time in 2014 did not return. But this may well be due to people not updating their profile, so this should be assessed again once everybody has had time to put their attendance on record.&lt;/p&gt;
&lt;p&gt;Out of the (not so few, it seems; but beware of cheaters, see below) pioneers from 2005, about 75% never returned. But some were still attending in 2014, some skipped 2014 but are back this year, and &lt;strong&gt;three heroes attended all DrupalCons Amsterdam&lt;/strong&gt; (watch for the almost invisible segment bottom right in the chart). Kudos to them! Here they are, together with other statistics about DrupalCon Amsterdam.&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Amsterdam DrupalCons attended in total&lt;/th&gt;&lt;th&gt;How many people&lt;/th&gt;&lt;th&gt;Who&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Attended &lt;strong&gt;one DrupalCon&lt;/strong&gt; in Amsterdam&lt;/td&gt;&lt;td&gt;2130 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Attended &lt;strong&gt;two DrupalCons&lt;/strong&gt; in Amsterdam&lt;/td&gt;&lt;td&gt;191 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Attended &lt;strong&gt;all three DrupalCons&lt;/strong&gt; in Amsterdam&lt;/td&gt;&lt;td&gt;3 people&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://drupal.org/user/17943&quot;&gt;Heine&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/188&quot;&gt;bertboerland&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/3064&quot;&gt;drumm&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;extending-to-europe-attendees-through-15-years-of-drupalcon-in-europe&quot;&gt;Extending to Europe: attendees through 15 years of DrupalCon in Europe&lt;/h3&gt;
&lt;p&gt;Amsterdam 2019 is the 15th DrupalCon held in Europe. What about extending the analysis above to cover the entire history of DrupalCon in Europe?&lt;/p&gt;
&lt;p&gt;Here is the entire flow from 2005 to 2019. The representation is a bit overwhelming due to the very high number of combinations, but hovering or tapping will help you see what goes where.&lt;/p&gt;
&lt;div id=&quot;sankey_big&quot; style=&quot;height:700px&quot;&gt;&lt;/div&gt;
&lt;script&gt;
  google.charts.load(&quot;current&quot;, { packages: [&quot;sankey&quot;] });
  google.charts.setOnLoadCallback(drawChart);
  function drawChart() {
    var data = new google.visualization.DataTable();
    data.addColumn(&apos;string&apos;, &apos;From&apos;);
    data.addColumn(&apos;string&apos;, &apos;To&apos;);
    data.addColumn(&apos;number&apos;, &apos;Weight&apos;);
    data.addRows([
      [ &apos;New&apos;, &apos;2005&apos;, 553 ],
      [ &apos;New&apos;, &apos;2006&apos;, 171 ],
      [ &apos;New&apos;, &apos;2007&apos;, 309 ],
      [ &apos;New&apos;, &apos;2008&apos;, 283 ],
      [ &apos;New&apos;, &apos;2009&apos;, 549 ],
      [ &apos;New&apos;, &apos;2010&apos;, 482 ],
      [ &apos;New&apos;, &apos;2011&apos;, 770 ],
      [ &apos;New&apos;, &apos;2012&apos;, 646 ],
      [ &apos;New&apos;, &apos;2013&apos;, 524 ],
      [ &apos;New&apos;, &apos;2014&apos;, 619 ],
      [ &apos;New&apos;, &apos;2015&apos;, 439 ],
      [ &apos;New&apos;, &apos;2016&apos;, 268 ],
      [ &apos;New&apos;, &apos;2017&apos;, 211 ],
      [ &apos;New&apos;, &apos;2018&apos;, 99 ],
      [ &apos;New&apos;, &apos;2019&apos;, 75 ],
      [ &apos;2005&apos;, &apos;Never again&apos;, 91 ],
      [ &apos;2006&apos;, &apos;Never again&apos;, 78 ],
      [ &apos;2007&apos;, &apos;Never again&apos;, 199 ],
      [ &apos;2008&apos;, &apos;Never again&apos;, 184 ],
      [ &apos;2009&apos;, &apos;Never again&apos;, 408 ],
      [ &apos;2010&apos;, &apos;Never again&apos;, 387 ],
      [ &apos;2011&apos;, &apos;Never again&apos;, 501 ],
      [ &apos;2012&apos;, &apos;Never again&apos;, 577 ],
      [ &apos;2013&apos;, &apos;Never again&apos;, 475 ],
      [ &apos;2014&apos;, &apos;Never again&apos;, 720 ],
      [ &apos;2015&apos;, &apos;Never again&apos;, 725 ],
      [ &apos;2016&apos;, &apos;Never again&apos;, 512 ],
      [ &apos;2017&apos;, &apos;Never again&apos;, 516 ],
      [ &apos;2018&apos;, &apos;Never again&apos;, 319 ],
      [ &apos;2005&apos;, &apos;2006&apos;, 424 ],
      [ &apos;2005&apos;, &apos;2007&apos;, 11 ],
      [ &apos;2005&apos;, &apos;2008&apos;, 3 ],
      [ &apos;2005&apos;, &apos;2009&apos;, 9 ],
      [ &apos;2005&apos;, &apos;2010&apos;, 3 ],
      [ &apos;2005&apos;, &apos;2011&apos;, 3 ],
      [ &apos;2005&apos;, &apos;2012&apos;, 1 ],
      [ &apos;2005&apos;, &apos;2013&apos;, 1 ],
      [ &apos;2005&apos;, &apos;2014&apos;, 5 ],
      [ &apos;2005&apos;, &apos;2015&apos;, 1 ],
      [ &apos;2005&apos;, &apos;2018&apos;, 1 ],
      [ &apos;2006&apos;, &apos;2007&apos;, 462 ],
      [ &apos;2006&apos;, &apos;2008&apos;, 14 ],
      [ &apos;2006&apos;, &apos;2009&apos;, 19 ],
      [ &apos;2006&apos;, &apos;2010&apos;, 5 ],
      [ &apos;2006&apos;, &apos;2011&apos;, 5 ],
      [ &apos;2006&apos;, &apos;2012&apos;, 4 ],
      [ &apos;2006&apos;, &apos;2013&apos;, 2 ],
      [ &apos;2006&apos;, &apos;2014&apos;, 4 ],
      [ &apos;2006&apos;, &apos;2015&apos;, 1 ],
      [ &apos;2006&apos;, &apos;2017&apos;, 1 ],
      [ &apos;2007&apos;, &apos;2008&apos;, 463 ],
      [ &apos;2007&apos;, &apos;2009&apos;, 58 ],
      [ &apos;2007&apos;, &apos;2010&apos;, 21 ],
      [ &apos;2007&apos;, &apos;2011&apos;, 12 ],
      [ &apos;2007&apos;, &apos;2012&apos;, 5 ],
      [ &apos;2007&apos;, &apos;2014&apos;, 4 ],
      [ &apos;2007&apos;, &apos;2015&apos;, 16 ],
      [ &apos;2007&apos;, &apos;2016&apos;, 1 ],
      [ &apos;2007&apos;, &apos;2018&apos;, 1 ],
      [ &apos;2007&apos;, &apos;2019&apos;, 2 ],
      [ &apos;2008&apos;, &apos;2009&apos;, 502 ],
      [ &apos;2008&apos;, &apos;2010&apos;, 37 ],
      [ &apos;2008&apos;, &apos;2011&apos;, 13 ],
      [ &apos;2008&apos;, &apos;2012&apos;, 12 ],
      [ &apos;2008&apos;, &apos;2013&apos;, 8 ],
      [ &apos;2008&apos;, &apos;2014&apos;, 2 ],
      [ &apos;2008&apos;, &apos;2015&apos;, 2 ],
      [ &apos;2008&apos;, &apos;2016&apos;, 1 ],
      [ &apos;2008&apos;, &apos;2017&apos;, 2 ],
      [ &apos;2009&apos;, &apos;2010&apos;, 534 ],
      [ &apos;2009&apos;, &apos;2011&apos;, 117 ],
      [ &apos;2009&apos;, &apos;2012&apos;, 40 ],
      [ &apos;2009&apos;, &apos;2013&apos;, 16 ],
      [ &apos;2009&apos;, &apos;2014&apos;, 11 ],
      [ &apos;2009&apos;, &apos;2015&apos;, 8 ],
      [ &apos;2009&apos;, &apos;2016&apos;, 2 ],
      [ &apos;2009&apos;, &apos;2019&apos;, 1 ],
      [ &apos;2010&apos;, &apos;2011&apos;, 526 ],
      [ &apos;2010&apos;, &apos;2012&apos;, 109 ],
      [ &apos;2010&apos;, &apos;2013&apos;, 28 ],
      [ &apos;2010&apos;, &apos;2014&apos;, 19 ],
      [ &apos;2010&apos;, &apos;2015&apos;, 7 ],
      [ &apos;2010&apos;, &apos;2016&apos;, 5 ],
      [ &apos;2010&apos;, &apos;2017&apos;, 1 ],
      [ &apos;2011&apos;, &apos;2012&apos;, 704 ],
      [ &apos;2011&apos;, &apos;2013&apos;, 106 ],
      [ &apos;2011&apos;, &apos;2014&apos;, 79 ],
      [ &apos;2011&apos;, &apos;2015&apos;, 30 ],
      [ &apos;2011&apos;, &apos;2016&apos;, 15 ],
      [ &apos;2011&apos;, &apos;2017&apos;, 4 ],
      [ &apos;2011&apos;, &apos;2018&apos;, 4 ],
      [ &apos;2011&apos;, &apos;2019&apos;, 3 ],
      [ &apos;2012&apos;, &apos;2013&apos;, 717 ],
      [ &apos;2012&apos;, &apos;2014&apos;, 153 ],
      [ &apos;2012&apos;, &apos;2015&apos;, 42 ],
      [ &apos;2012&apos;, &apos;2016&apos;, 15 ],
      [ &apos;2012&apos;, &apos;2017&apos;, 10 ],
      [ &apos;2012&apos;, &apos;2018&apos;, 7 ],
      [ &apos;2013&apos;, &apos;2014&apos;, 777 ],
      [ &apos;2013&apos;, &apos;2015&apos;, 101 ],
      [ &apos;2013&apos;, &apos;2016&apos;, 26 ],
      [ &apos;2013&apos;, &apos;2017&apos;, 16 ],
      [ &apos;2013&apos;, &apos;2018&apos;, 5 ],
      [ &apos;2013&apos;, &apos;2019&apos;, 2 ],
      [ &apos;2014&apos;, &apos;2015&apos;, 768 ],
      [ &apos;2014&apos;, &apos;2016&apos;, 107 ],
      [ &apos;2014&apos;, &apos;2017&apos;, 53 ],
      [ &apos;2014&apos;, &apos;2018&apos;, 10 ],
      [ &apos;2014&apos;, &apos;2019&apos;, 15 ],
      [ &apos;2015&apos;, &apos;2016&apos;, 545 ],
      [ &apos;2015&apos;, &apos;2017&apos;, 113 ],
      [ &apos;2015&apos;, &apos;2018&apos;, 23 ],
      [ &apos;2015&apos;, &apos;2019&apos;, 9 ],
      [ &apos;2016&apos;, &apos;2017&apos;, 423 ],
      [ &apos;2016&apos;, &apos;2018&apos;, 33 ],
      [ &apos;2016&apos;, &apos;2019&apos;, 17 ],
      [ &apos;2017&apos;, &apos;2018&apos;, 261 ],
      [ &apos;2017&apos;, &apos;2019&apos;, 57 ],
      [ &apos;2018&apos;, &apos;2019&apos;, 125 ],
    ]);
    var colors = [&apos;#a6cee3&apos;, &apos;#b2df8a&apos;, &apos;#fb9a99&apos;, &apos;#fdbf6f&apos;,
                    &apos;#cab2d6&apos;, &apos;#ffff99&apos;, &apos;#1f78b4&apos;, &apos;#33a02c&apos;];
    // Set chart options
    var options = {
      sankey: {
        node: {
          width: 2,
          nodePadding: 10,
          label: {
            fontName: &apos;Roboto&apos;,
            fontSize: 10,
            color: &apos;#871b47&apos;,
            bold: false,
            italic: false
          }
        },
        link: {
            colorMode: &apos;gradient&apos;,
            colors: colors
        }
      },
    };
    var chart = new google.visualization.Sankey(document.getElementById(&apos;sankey_big&apos;));
    chart.draw(data, options);
  }
&lt;/script&gt;
&lt;p&gt;You can explore your path through the European DrupalCons and check how many people made your same choice at each step. For example, 58 community members decided, like me, to go from Barcelona 2007 to Paris 2009 (skipping 2008) and 534 community members decided to go from Paris 2009 to Copenhagen 2010.&lt;/p&gt;
&lt;p&gt;Can we assess the community growth associated to each conference?&lt;/p&gt;
&lt;div id=&quot;sankey_cities&quot; style=&quot;height: 600px&quot;&gt;&lt;/div&gt;
&lt;script&gt;
  google.charts.load(&quot;current&quot;, { packages: [&quot;sankey&quot;] });
  google.charts.setOnLoadCallback(drawChart);
  function drawChart() {
    var data = new google.visualization.DataTable();
    data.addColumn(&apos;string&apos;, &apos;From&apos;);
    data.addColumn(&apos;string&apos;, &apos;To&apos;);
    data.addColumn(&apos;number&apos;, &apos;Weight&apos;);
    data.addRows([
      [ &apos;New&apos;, &apos;amsterdam_2005&apos;, 553 ],
      [ &apos;New&apos;, &apos;brussels_2006&apos;, 171 ],
      [ &apos;New&apos;, &apos;barcelona_2007&apos;, 309 ],
      [ &apos;New&apos;, &apos;szeged_hungary_2008&apos;, 283 ],
      [ &apos;New&apos;, &apos;paris_2009&apos;, 549 ],
      [ &apos;New&apos;, &apos;copenhagen_2010&apos;, 482 ],
      [ &apos;New&apos;, &apos;london_2011&apos;, 770 ],
      [ &apos;New&apos;, &apos;munich_2012&apos;, 646 ],
      [ &apos;New&apos;, &apos;prague_2013&apos;, 524 ],
      [ &apos;New&apos;, &apos;amsterdam_2014&apos;, 619 ],
      [ &apos;New&apos;, &apos;barcelona_2015&apos;, 439 ],
      [ &apos;New&apos;, &apos;dublin_2016&apos;, 268 ],
      [ &apos;New&apos;, &apos;vienna_2017&apos;, 211 ],
      [ &apos;New&apos;, &apos;europe_2018&apos;, 99 ],
      [ &apos;New&apos;, &apos;amsterdam_2019&apos;, 75 ],
      [ &apos;amsterdam_2005&apos;, &apos;Never again&apos;, 91 ],
      [ &apos;brussels_2006&apos;, &apos;Never again&apos;, 78 ],
      [ &apos;barcelona_2007&apos;, &apos;Never again&apos;, 199 ],
      [ &apos;szeged_hungary_2008&apos;, &apos;Never again&apos;, 184 ],
      [ &apos;paris_2009&apos;, &apos;Never again&apos;, 408 ],
      [ &apos;copenhagen_2010&apos;, &apos;Never again&apos;, 387 ],
      [ &apos;london_2011&apos;, &apos;Never again&apos;, 501 ],
      [ &apos;munich_2012&apos;, &apos;Never again&apos;, 577 ],
      [ &apos;prague_2013&apos;, &apos;Never again&apos;, 475 ],
      [ &apos;amsterdam_2014&apos;, &apos;Never again&apos;, 720 ],
      [ &apos;barcelona_2015&apos;, &apos;Never again&apos;, 725 ],
      [ &apos;dublin_2016&apos;, &apos;Never again&apos;, 512 ],
      [ &apos;vienna_2017&apos;, &apos;Never again&apos;, 516 ],
      [ &apos;europe_2018&apos;, &apos;Never again&apos;, 319 ],
    ]);
    // Set chart options
    var options = {
      sankey: {
        node: {
          nodePadding: 10,
          label: {
            fontName: &apos;Roboto&apos;,
            fontSize: 16,
            color: &apos;#871b47&apos;,
            bold: true,
            italic: false
          }
        }
      },
    };
    var chart = new google.visualization.Sankey(document.getElementById(&apos;sankey_cities&apos;));
    chart.draw(data, options);
   }
&lt;/script&gt;
&lt;p&gt;Left: how many new people joined at each conference (first DrupalCon presence); right: how many people left after each conference (last DrupalCon presence). So, e.g., after the recent Barcelona, Dublin and Vienna we lost more people than the conference attracted; while after London, Prague, Paris and Munich the members we gained with the event were more than those we lost after the event. Again, it is likely that the Amsterdam 2005 figures are altered by cheaters as discussed in the final paragraph.&lt;/p&gt;
&lt;p&gt;And here’s the overall ranking as far as &lt;strong&gt;European DrupalCons&lt;/strong&gt; are concerned.&lt;/p&gt;





















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;DrupalCons attended in Europe (as of 2019)&lt;/th&gt;&lt;th&gt;How many people&lt;/th&gt;&lt;th&gt;Who&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1 DrupalCon&lt;/td&gt;&lt;td&gt;2903 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2 DrupalCons&lt;/td&gt;&lt;td&gt;1107 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3 DrupalCons&lt;/td&gt;&lt;td&gt;647 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4 DrupalCons&lt;/td&gt;&lt;td&gt;417 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5 DrupalCons&lt;/td&gt;&lt;td&gt;326 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6 DrupalCons&lt;/td&gt;&lt;td&gt;228 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7 DrupalCons&lt;/td&gt;&lt;td&gt;113 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8 DrupalCons&lt;/td&gt;&lt;td&gt;77 people&lt;/td&gt;&lt;td&gt;Many!&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9 DrupalCons&lt;/td&gt;&lt;td&gt;38 people&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://drupal.org/user/195691&quot;&gt;Helpermedia&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/337498&quot;&gt;Ivo.Radulovski&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/409&quot;&gt;Jeremy&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/8777&quot;&gt;Jo Wouters&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/58170&quot;&gt;John Morahan&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/4299&quot;&gt;Jose Reyero&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/122682&quot;&gt;Nick_vh&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/84357&quot;&gt;PieterDC&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/106525&quot;&gt;SqyD&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/462700&quot;&gt;StryKaizer&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/577844&quot;&gt;aburrows&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/248999&quot;&gt;askibinski&lt;/a&gt;, &lt;a class=&quot;nuvole-member&quot; href=&quot;https://drupal.org/user/1344166&quot;&gt;bircher&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/276995&quot;&gt;chipway&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/9446&quot;&gt;chx&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/626306&quot;&gt;cocomore&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/40963&quot;&gt;demeester_roel&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/29191&quot;&gt;douggreen&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/787856&quot;&gt;e.huijs&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/1773&quot;&gt;emmajane&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/1765&quot;&gt;gaele&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/119195&quot;&gt;h2cm&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/787250&quot;&gt;idevit&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/100393&quot;&gt;kgtzs&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/257505&quot;&gt;kostask&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/121684&quot;&gt;lslinnet&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/256793&quot;&gt;meichr&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/35488&quot;&gt;ohthehugemanatee&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/72810&quot;&gt;pdjohnson&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/341265&quot;&gt;pixelite&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/235415&quot;&gt;pmarciano&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/66273&quot;&gt;rachel_norfolk&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/245473&quot;&gt;ratbagash&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/205988&quot;&gt;reglogge&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/116305&quot;&gt;skwashd&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/10848&quot;&gt;steveparks&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/239562&quot;&gt;valthebald&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/75070&quot;&gt;vesapalmu&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10 DrupalCons&lt;/td&gt;&lt;td&gt;24 people&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://drupal.org/user/107229&quot;&gt;BarisW&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/26398&quot;&gt;Crell&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/114486&quot;&gt;MatthewS&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/398454&quot;&gt;MickeA&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/307428&quot;&gt;R.Hendel&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/99777&quot;&gt;Wim Leers&lt;/a&gt;, &lt;a class=&quot;nuvole-member&quot; href=&quot;https://drupal.org/user/186696&quot;&gt;ademarco&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/157725&quot;&gt;alexpott&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/228295&quot;&gt;dasjo&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/82971&quot;&gt;dokumori&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/118398&quot;&gt;gdemet&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/28223&quot;&gt;hagen&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/98730&quot;&gt;ifrik&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/77723&quot;&gt;jfhovinne&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/55005&quot;&gt;kvantomme&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/58679&quot;&gt;mikl&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/124705&quot;&gt;mkalkbrenner&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/59351&quot;&gt;nicholasThompson&lt;/a&gt;, &lt;a class=&quot;nuvole-member&quot; href=&quot;https://drupal.org/user/436244&quot;&gt;pescetti&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/49851&quot;&gt;pwolanin&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/49344&quot;&gt;rszrama&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/36942&quot;&gt;stBorchert&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/107403&quot;&gt;swentel&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/121849&quot;&gt;yngvewb&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;11 DrupalCons&lt;/td&gt;&lt;td&gt;12 people&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://drupal.org/user/47194&quot;&gt;Pol&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/34940&quot;&gt;Wimmmmm&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/65088&quot;&gt;add1sun&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/20975&quot;&gt;agentrickard&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/35997&quot;&gt;alanburke&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/107667&quot;&gt;aschiwi&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/151799&quot;&gt;beltofte&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/232832&quot;&gt;betz&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/56348&quot;&gt;claudiu.cristea&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/262198&quot;&gt;klausi&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/11550&quot;&gt;michaelemeyers&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/65676&quot;&gt;mortendk&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;12 DrupalCons&lt;/td&gt;&lt;td&gt;9 people&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://drupal.org/user/3555&quot;&gt;Robert Castelo&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/10083&quot;&gt;ekes&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/16747&quot;&gt;fago&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/27985&quot;&gt;fgm&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/76919&quot;&gt;horncologne&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/58600&quot;&gt;populist&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/28074&quot;&gt;sanduhrs&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/66894&quot;&gt;stella&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/24967&quot;&gt;webchick&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;13 DrupalCons&lt;/td&gt;&lt;td&gt;5 people&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://drupal.org/user/4166&quot;&gt;Gábor Hojtsy&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/17943&quot;&gt;Heine&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/73854&quot;&gt;Sutharsan&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/71764&quot;&gt;criz&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/23&quot;&gt;moshe weitzman&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;14 DrupalCons&lt;/td&gt;&lt;td&gt;0 people&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;15 DrupalCons&lt;/td&gt;&lt;td&gt;2 people&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://drupal.org/user/188&quot;&gt;bertboerland&lt;/a&gt;, &lt;a href=&quot;https://drupal.org/user/3064&quot;&gt;drumm&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;small&gt;In &lt;strong&gt;red&lt;/strong&gt; Nuvole team members, just for a bit of company pride.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-fine-print&quot;&gt;The fine print&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Only DrupalCons held in Europe are considered. Drupal Europe 2018 is considered to be the European DrupalCon for the year 2018.&lt;/li&gt;
&lt;li&gt;Data were obtained using the little-known but very handy &lt;a href=&quot;https://www.drupal.org/drupalorg/docs/api&quot;&gt;Drupal.org API&lt;/a&gt; as explained in &lt;a href=&quot;https://www.drupaleurope.org/session/analyzing-drupal-community-data-driven-approach&quot;&gt;a session&lt;/a&gt;, more focused on community demographics, we gave at Drupal Europe 2018.&lt;/li&gt;
&lt;li&gt;Like most d.o profile data, attendance to Drupal events is manually entered by each user. There are obvious cases of missing information (Dries being a notable example) and of cheaters (many very recent accounts claim an unlikely number of attended events).&lt;/li&gt;
&lt;li&gt;Data were algorithmically sanitized to remove suspicious entries (e.g., accounts created last month that self-proclaimed presence at all DrupalCons ever held) as much as possible. This was done for the tables but not for the charts. If you are affected, please point out any errors in the comments.&lt;/li&gt;
&lt;li&gt;If you are in Amsterdam now but forgot to tick the checkbox - don’t worry, we had forgotten about it too. Go edit your profile (“Drupal” tab) and we’ll update this post with fresh numbers this weekend.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>CMI 2 at DrupalCon Europe 2020</title><link>https://www.nuvole.org/blog/cmi-2-drupalcon-europe-2020/</link><guid isPermaLink="true">https://www.nuvole.org/blog/cmi-2-drupalcon-europe-2020/</guid><pubDate>Mon, 21 Dec 2020 07:30:42 GMT</pubDate><content:encoded>&lt;p&gt;Last week was the virtual DrupalCon Europe and I had the pleasure to present CMI 2 updates together with Moshe Weitzman. Attached to this post are the slides.&lt;/p&gt;
&lt;p&gt;It was a strange DrupalCon, no travelling and home made food and good coffee are of course a nice change but I am not sure it makes up for not meeting Drupal friends in person. I enjoyed the hallway track and visited the “virtual booth” of sponsors and it was a nice way to randomly meet people like one would at a in-person event.
But presenting a presentation to just the screen without seeing the audience was a new experience.&lt;/p&gt;
&lt;p&gt;I presented the CMI updates under the overarching goal of standardising the way configuration management is approached in Drupal. After the big API addition last year we focused on updating contrib to use this new API.
A particular highlight is the &lt;a href=&quot;https://www.drupal.org/node/3187459&quot;&gt;testing framework&lt;/a&gt; for modules upgrading from config filter to the new core API.&lt;/p&gt;
&lt;p&gt;Another highlight is of course the new &lt;code&gt;drush deploy&lt;/code&gt; command which Moshe presented.
It executes the steps to update a drupal site where new code has been pulled. In particular it executes a database update, config import and running of deploy hooks.&lt;/p&gt;
&lt;p&gt;Deploy hooks are the same as post_update hooks, but they are meant to be run after the configuration is imported. But instead of putting them in your &lt;code&gt;mymodule.post_update.php&lt;/code&gt; you put the code in &lt;code&gt;mymodule.deploy.php&lt;/code&gt;.
They are not meant for contrib modules, but rather for custom code that a site relies on. For example it allows you to add content to fields that you created via config. More information can be found in &lt;a href=&quot;https://www.drush.org/deploycommand/&quot;&gt;the deploy hook documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Lastly I attended a very interesting BOF around distributions. I did not initiate it but I was very happy to observe the exchange of ideas and approaches between maintainers of different distributions. I hope more collaboration will happen next year in this space and hopefully more distributions join forces to find a way core can assist the development of distributions.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/sites/default/files/CMI%202%20at%20DrupalCon%20Europe%202020.pdf&quot;&gt;CMI 2 at DrupalCon Europe 2020.pdf&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Changes coming to Config Split</title><link>https://www.nuvole.org/blog/changes-coming-config-split/</link><guid isPermaLink="true">https://www.nuvole.org/blog/changes-coming-config-split/</guid><pubDate>Wed, 04 Aug 2021 08:11:58 GMT</pubDate><content:encoded>&lt;p&gt;Config Split has worked mostly the same way as it did when the first beta release was tagged in 2016. The 8.x-1.x branch is using Config Filter as the API to hook into the configuration management of Drupal. But since Drupal 8.8 we have a much better API in Drupal core which allows us to do more complex things with simpler code than the Config Filter API did. The core API was designed with the lessons learned from Config Filter in mind after all.&lt;/p&gt;
&lt;p&gt;Config Split 2.0.0-beta2 already uses the core API, but it still does the same thing as Config Split 1.x. I think now would be a great time to change things up a bit and do the things that were too hard to do before.&lt;/p&gt;
&lt;h3 id=&quot;but-first-lets-revisit-what-config-split-does&quot;&gt;But first let’s revisit what Config Split does.&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;export&lt;/strong&gt; these two things happen:&lt;br&gt;
&lt;strong&gt;Complete Split:&lt;/strong&gt;
Modules and config and all the config that depends on it is split off into the split storage and removed from the sync storage when the export is complete.&lt;br&gt;
&lt;strong&gt;Conditional Split:&lt;/strong&gt;
The config is compared against the sync storage and is split to the split storage if it differs, the original config in the sync storage remains.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;import&lt;/strong&gt; the config from the split storage is merged before Drupal imports the config from the sync storage.&lt;/p&gt;
&lt;h3 id=&quot;now-what-do-i-propose-2x-should-do&quot;&gt;Now what do I propose 2.x should do?&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;export&lt;/strong&gt; these very similar but different things happen:&lt;br&gt;
&lt;strong&gt;Complete Split:&lt;/strong&gt;
Modules and config are removed from the sync storage as if it had been uninstalled. The difference of how other config (which no longer depends on the modules) changes is kept as a quasi-config-patch* together with the config of the split modules in the split storage.&lt;br&gt;
&lt;strong&gt;Partial Split:&lt;/strong&gt;
Config is compared against the sync storage but instead of splitting the whole config to the split storage only the difference is kept in the split storage as a quasi-config-patch*.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;import&lt;/strong&gt; the config from the split storage is merged and the quasi-patches applied before Drupal imports the config from the sync storage.&lt;/p&gt;
&lt;h3 id=&quot;what-now&quot;&gt;What now?&lt;/h3&gt;
&lt;p&gt;I think these changes make sense and make Config Split a better module. But the behavior is quite different, so if you have gotten accustomed to the stability of Config Split and depend on some specific way it works then this could be upsetting. And right now there is still a way to have the cake and eat it too: We keep &lt;em&gt;2.x&lt;/em&gt; the way it is now (in beta2) and maintain the behavior and only add “non-breaking” changes and do what I proposed in &lt;em&gt;3.x&lt;/em&gt;. Here is the catch though: I am not interested in supporting two branches, especially if I don’t know that it is useful to anyone.&lt;/p&gt;
&lt;p&gt;Please let me know what you think about this in a comment on &lt;a href=&quot;https://www.drupal.org/project/config_split/issues/3215319&quot;&gt;the issue on drupal.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4 id=&quot;-what-is-a-quasi-patch-or-a-config-patch&quot;&gt;* What is a “quasi-patch” or a config patch?&lt;/h4&gt;
&lt;p&gt;The word quasi-patch was already introduced to the drupal world &lt;a href=&quot;https://www.drupal.org/project/automatic_updates/issues/3055872&quot;&gt;#3055872&lt;/a&gt;. But what I have in mind is a bit different. I think it will be easiest to explain with an example:&lt;br&gt;
Imagine you install the demo Umami profile and then uninstall the Menu UI module. The recipe node type will be updated to have the third party configuration removed. A patch for the exported config file would look like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;diff&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;diff --git a/node.type.recipe.yml b/node.type.recipe.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;index 974c2ce..8d0de1f 100644&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;-&lt;/span&gt;-- a/node.type.recipe.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;+&lt;/span&gt;++ b/node.type.recipe.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0;font-weight:bold&quot;&gt;@@ -1,13 +1,6 @@&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; uuid: 40b74b76-6f31-4999-87bb-9700b85d43a6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; langcode: en&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; status: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;-&lt;/span&gt;dependencies:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;-&lt;/span&gt;  module:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;-&lt;/span&gt;    - menu_ui&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;-&lt;/span&gt;third_party_settings:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;-&lt;/span&gt;  menu_ui:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;-&lt;/span&gt;    available_menus: {  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FDAEB7&quot;&gt;&lt;span style=&quot;user-select: none;&quot;&gt;-&lt;/span&gt;    parent: &apos;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; _core:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;   default_config_hash: &apos;-3P1yuM15pcimXHBBhevjW-4aqojpmZbUgAqlVIeweo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; name: Recipe&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://dictionary.cambridge.org/dictionary/english/quasi&quot;&gt;quasi-&lt;/a&gt;patch or config patch would be a file called &lt;em&gt;config_split.partial.node.type.recipe.yml&lt;/em&gt; with the content:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;added&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;removed&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  dependencies&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    module&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;menu_ui&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  third_party_settings&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    menu_ui&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      available_menus&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      parent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Disclaimer: The details about this are subject to change. There are likely some compromises we need to make.&lt;/p&gt;</content:encoded></item><item><title>Config Split at DrupalCon Europe 2021</title><link>https://www.nuvole.org/blog/config-split-drupalcon-europe-2021/</link><guid isPermaLink="true">https://www.nuvole.org/blog/config-split-drupalcon-europe-2021/</guid><pubDate>Tue, 05 Oct 2021 13:25:50 GMT</pubDate><content:encoded>&lt;p&gt;This morning was the session on Config Split 2.x.
It introduced the new features of Config Split 2.x and why they are relevant for the planned new core module.&lt;/p&gt;
&lt;p&gt;Attached are &lt;a href=&quot;/sites/default/files/Config%20Split%20DrupalCon%20Europe%202021.pdf&quot;&gt;the slides of the presentation&lt;/a&gt;&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube-nocookie.com/embed/qrDBMfxCnJs&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;</content:encoded></item><item><title>Drupal for European Universities</title><link>https://www.nuvole.org/blog/drupal-european-universities/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-european-universities/</guid><pubDate>Fri, 08 Oct 2021 19:23:38 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Thank you for a great DrupalCon! Here are a few key findings and PDF slides from our Thursday talk, &lt;a href=&quot;https://events.drupal.org/europe2021/sessions/drupal-universities-current-state-and-perspectives&quot;&gt;Drupal for European Universities: Data-based perspectives&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;drupal-is-not-leading-and-not-even-the-runner-up&quot;&gt;Drupal is not leading (and not even the runner-up)&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;drupal-universities-share-750px.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Wordpress is the most popular CMS for European Universities (restricted to &lt;a href=&quot;https://ec.europa.eu/eurostat/statistics-explained/index.php?title=Glossary:European_Economic_Area_(EEA)&quot;&gt;EEA Countries&lt;/a&gt;; data retrieved last week from all the the 1830 HEIs from those 30 countries). Surprise, Drupal is not the runner-up either: Typo3 beats it by a few installations.&lt;/p&gt;
&lt;h2 id=&quot;but-many-countries-still-love-drupal&quot;&gt;But many countries still love Drupal&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;european-universities-dominant-cms-by-country-750px.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Wordpress (green in this map) leads in 15 countries, Drupal (blue) in 12, Typo3 in 2 (Germany, Austria), Joomla in Slovakia. This is a good sign for the expansion potential: Typo3’s success is based on a couple countries only. The most Drupalized countries are Estonia (44% of HEIs sites are Drupal sites), Iceland (43%), Finland (34%), Italy (33%), Belgium (33%).&lt;/p&gt;
&lt;h2 id=&quot;european-universities-like-drupal-7&quot;&gt;European Universities like Drupal 7&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;european-universities-dominant-drupal-version-by-country-750px.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Drupal 7 (red in this map) is still the most common Drupal version for University sites in 16 countries. In 9 countries the most common version is Drupal 8 (yellow), only in 2 countries it’s Drupal 9 (green); other EEA countries are omitted due to non-significant data.&lt;/p&gt;
&lt;p&gt;Overall, Drupal 9 sites account for only about 10% of the total, with &lt;strong&gt;Drupal 6 (yes, 6!) still beating Drupal 9&lt;/strong&gt; in four countries. 45% of the Drupal websites used by European Universities are running on a version that will be unsupported in one month.&lt;/p&gt;
&lt;h2 id=&quot;and-the-future&quot;&gt;And the future?&lt;/h2&gt;
&lt;p&gt;Drupal 9 has a lot of solid selling points for Universities, see the slides below for a summary and a case study. But it needs to be marketed well to people who are still on Drupal 7, and still facing problems the Drupal community solved years ago.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It will be interesting to re-run the analysis in one month, perhaps in an extended version including the non-EEA countries that were left out of this initial survey, like the UK and Switzerland. Stay tuned!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/sites/default/files/Drupal-for-Universities-DrupalCon-Europe-2021.pdf&quot;&gt;Drupal-for-Universities-DrupalCon-Europe-2021.pdf&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>PSA: Config Split and Drupal 9.3</title><link>https://www.nuvole.org/blog/psa-config-split-and-drupal-93/</link><guid isPermaLink="true">https://www.nuvole.org/blog/psa-config-split-and-drupal-93/</guid><pubDate>Tue, 14 Dec 2021 09:38:38 GMT</pubDate><content:encoded>&lt;p&gt;Drupal &lt;a href=&quot;https://www.drupal.org/project/drupal/releases/9.3.0&quot;&gt;9.3.0&lt;/a&gt; was just released!
It contains a lot of cool new things we are excited about but for users of &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Config Split&lt;/a&gt; there is one change that will impact on what is split off by Config Split.&lt;/p&gt;
&lt;p&gt;For those unfamiliar with it, Config Split is a Drupal module developed by Nuvole, which splits the configuration when it is exported and merges it back together when it is imported. It is useful in many scenarios but best tailored to having some configuration only in some environments (for example the Devel module only in development environments).&lt;/p&gt;
&lt;p&gt;The important change in Drupal 9.3+ is that &lt;a href=&quot;https://www.drupal.org/node/3193348&quot;&gt;permissions must exist&lt;/a&gt;. This is enforced with configuration dependencies which &lt;a href=&quot;https://www.drupal.org/node/3055548&quot;&gt;roles can now have&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Config Split 1.x will split off all the config which depends on the module you split off. Which means that if a role has a permission from the module you are splitting off, it will then in turn also be split. This is how Config Split 1.x works and it is not considered a bug. However, most likely this is not what you wanted, so what is the solution?&lt;/p&gt;
&lt;p&gt;Use Config Split &lt;strong&gt;2.x!&lt;/strong&gt; The new version is currently still in beta but I aim to tag a new release soon.
In particular Config Split 2.x splits off modules as if they had been uninstalled. Which means that instead of splitting the role completely, it will just remove the permissions and save them in a patch for the role.&lt;/p&gt;
&lt;p&gt;There are still a few open issues though and probably more testing is needed too. One of the issues I would like to merge before 2.0.0 is &lt;a href=&quot;https://www.drupal.org/project/config_split/issues/3238855&quot;&gt;#3238855&lt;/a&gt;. So if you have some moment to give it a try and mark it as RTBC then that would be great!
Also of course don’t hesitate to report new bugs too or contribute with patches/MRs. And if coding or testing is not your strength then we also need still a lot of &lt;a href=&quot;https://www.drupal.org/docs/contributed-modules/configuration-split&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I hope to tag a stable 2.0.0 release for Config Split soon and then Config Split 1.x will become unsupported when Drupal 9.2 becomes unsupported.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.drupal.org/project/config_role_split&quot;&gt;Config Role Split&lt;/a&gt; (one of the obscure modules in the long tail of config_* modules) has an even deeper &lt;a href=&quot;https://www.drupal.org/project/config_role_split/issues/3223569&quot;&gt;existential crisis&lt;/a&gt; and may become unsupported altogether unless someone wants to become a maintainer. Probably the main use case for Config Role Split is now automatically addressed with Config Split 2. since roles have dependencies. But if you rely on some other feature you should reach out and start maintaining it.&lt;/p&gt;</content:encoded></item><item><title>New stable releases for config filter split and ignore</title><link>https://www.nuvole.org/blog/new-stable-releases-config-filter-split-and-ignore/</link><guid isPermaLink="true">https://www.nuvole.org/blog/new-stable-releases-config-filter-split-and-ignore/</guid><pubDate>Tue, 07 Nov 2023 15:12:43 GMT</pubDate><content:encoded>&lt;p&gt;The last two weeks the dust settled after an energetic and productive Drupalcon. Now there is a new stable release for all of the three most popular contrib modules Nuvole maintains: &lt;a href=&quot;https://www.drupal.org/project/config_filter/releases/8.x-1.12&quot;&gt;Config Filter&lt;/a&gt;, &lt;a href=&quot;https://www.drupal.org/project/config_split/releases/2.0.0&quot;&gt;Config Split&lt;/a&gt; and &lt;a href=&quot;https://www.drupal.org/project/config_ignore/releases/8.x-3.0&quot;&gt;Config Ignore&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;config-filter&quot;&gt;Config Filter&lt;/h2&gt;
&lt;p&gt;This is the most stable module, its new releases just switch over to Gitlab CI and fix a small inconsistency in how config storages are expected to behave. The first iteration introduced a bug which the second release fixed. Thank you to the early adopters who spotted the bug.
Config Filter will remain supported but its relevance is probably going to decrease with the release of stable versions of the modules which used to depend on it but no longer do. For example Config Split 2.x and Config Ignore 3.x do not depend on Config FIlter and both now have a stable release. Modules are encouraged to switch to the &lt;a href=&quot;https://www.drupal.org/docs/drupal-apis/configuration-api/configuration-import-export-transformation&quot;&gt;Config Storage Transformation API&lt;/a&gt; added to Drupal 8.8. Both branches remain supported and recommended on drupal.org since most commits can be cherry picked between the branches. But for performance I would recommend the original 1.x branch. Both branches have exactly the same API, just the behaviour is different when importing or exporting.
Other config modules can use either branch for the &lt;a href=&quot;https://www.drupal.org/node/3187459&quot;&gt;test traits&lt;/a&gt; which facilitate writing tests that pass before and after refactoring from Config Filters API to the core API.&lt;/p&gt;
&lt;p&gt;If your site indirectly depends on Config Filter because you use a module which depends on it, you need to explicitly require Config Filter when you upgrade the modules. Drupal can not uninstall a module that is no longer in the codebase. So explicitly require Config Filter, uninstall it and then in a subsequent deployment remove Config Filter.&lt;/p&gt;
&lt;h2 id=&quot;config-split&quot;&gt;Config Split&lt;/h2&gt;
&lt;p&gt;Our oldest Drupal 8 config module has a new stable 2.0.0 release. In it, bugs and edge cases discovered in last year’s release were fixed. It also contains a “new” feature which brings back the functionality of the 1.x branch. This should help sites holding back on upgrading because the functionality changed.
With that out of the way the plan is to deprecate the 1.x branch and end support with the end of Drupal 10 support. There will not be any features added to 1.x.&lt;/p&gt;
&lt;h2 id=&quot;config-ignore&quot;&gt;Config Ignore&lt;/h2&gt;
&lt;p&gt;Previous releases of the 3.x branch have not been feature compatible with 2.x. But the stable 3.0 release has been re-written from previous 3.x beta versions. It is configurable so that most use cases can be catered for with enough creativity. One can configure the configuration to ignore for create, update and delete for both import and export. But one can also just keep it simple and then it will be as all the versions before. In particular, however, it can be configured easily to behave like: the last 2.x release, the last 2.x release with the &lt;a href=&quot;https://www.drupal.org/project/config_ignore/issues/2857247&quot;&gt;popular patch&lt;/a&gt; to allow filtering on export and the previous 3.x release. Because of that the 2.x branch is deprecated as of now and it will be marked as unsupported by the end of the year on Drupal.org. There is a new hook replacing the one which existed in 2.x and which addresses the new capabilities (the old hook is still invoked and will be removed in 4.0.0). With the end of Drupal 9 being supported, PHP 8.1 is now the minimum required version, but 3.0 does not yet take advantage of the new PHP language features. So the plan for 4.0.0 is to switch the string constants to enums and switch to semantic versioning and remove the old hook.&lt;/p&gt;
&lt;h2 id=&quot;how-the-pizza-is-made&quot;&gt;How the pizza is made&lt;/h2&gt;
&lt;p&gt;Our Drupal 8 modules have always been maintained “like a php library”. The development of &lt;a href=&quot;https://www.drupal.org/project/ui_patterns&quot;&gt;UI Patterns&lt;/a&gt; was initially hosted on Github among other things for that reason. Config Split and Config Filter shipped with their docker compose files and scripts to symlink the module into the Drupal site. Later the custom scripts were replaced by &lt;a href=&quot;https://gitlab.com/drupalspoons/composer-plugin&quot;&gt;drupal-spoons&lt;/a&gt;. Since the now generally available drupal-gitlab-ci is inspired by spoons and DDEV is likely becoming the &lt;a href=&quot;https://www.drupal.org/project/ideas/issues/2965681&quot;&gt;recommended development environment&lt;/a&gt; for contributing to Drupal, I switched my local environment for maintaining the contrib modules to DDEV with the &lt;a href=&quot;https://github.com/ddev/ddev-drupal-contrib/&quot;&gt;ddev-drupal-contrib&lt;/a&gt; plugin, the spiritual successor of drupal-spoons. For contributing to Drupal core I can only recommend the &lt;a href=&quot;https://github.com/justafish/ddev-drupal-core-dev/&quot;&gt;ddev-drupal-core-dev&lt;/a&gt; plugin created by justafish during Drupalcon. I helped beta test it and it works like a charm. That said my PhpStorm configuration had to be updated a bit even though I installed the ddev plugin for it. In particular I had to add a second server mapping for Xdebug to work as described in a &lt;a href=&quot;https://github.com/php-perfect/ddev-intellij-plugin/issues/227#issuecomment-1773899551&quot;&gt;comment on the issue&lt;/a&gt;. The release notes for the contrib modules were generated with &lt;a href=&quot;https://drupal-mrn.dev/&quot;&gt;drupal-mrn.dev&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many thanks to everyone who contributed with feedback, code, ideas or even just listened to my ramblings as I discovered untested edge cases.&lt;/p&gt;</content:encoded></item><item><title>More peace of mind when applying recipes or letting AI configure your site</title><link>https://www.nuvole.org/blog/more-peace-mind-when-applying-recipes-or-letting-ai-configure-your-site/</link><guid isPermaLink="true">https://www.nuvole.org/blog/more-peace-mind-when-applying-recipes-or-letting-ai-configure-your-site/</guid><pubDate>Fri, 25 Apr 2025 18:10:23 GMT</pubDate><content:encoded>&lt;p&gt;Last week I attended the Drupal Dev Days in Leuven and many of the sessions and also conversations in the hallway and the contribution room were about Drupal CMS and AI.&lt;/p&gt;
&lt;p&gt;The foundational engine behind Drupal CMS is the recipe system, which allows a bunch of configuration changes to be bundled and applied to your site. This is really cool, but it may do things you are not completely aware of. Unfortunately there is no way to “undo” a recipe. The same problem occurs if one is letting AI change configuration.&lt;/p&gt;
&lt;h2 id=&quot;ddev-snapshots-and-git-commits&quot;&gt;DDEV snapshots and git commits&lt;/h2&gt;
&lt;p&gt;The proper solution of course is to export the configuration and version it in git before starting to play with recipes, as well as periodically in between applying recipes to have more save points to go back to. Since recipes can also add default content and some configuration can not be deleted when there is content for it, it means that sometimes a previous git checkout of the configuration can not be imported. Therefore, the best solution is to create database backups and roll them back when needed.
DDEV makes this very easy to do, and so that would be the first recommendation to address the problem and it will always work.&lt;/p&gt;
&lt;p&gt;However, the ddev snapshots do not really give one a lot of information of what changed. The configuration in git is a lot better, but it requires an extra manual step to create the export and commit it to git. That may not be instinctive to most drupal users, especially not the target audience of Drupal CMS.&lt;/p&gt;
&lt;h2 id=&quot;what-if-drupal-could-help-us-with-that&quot;&gt;What if Drupal could help us with that?&lt;/h2&gt;
&lt;p&gt;It turns out that Drupal core introduced the concept of configuration checkpoints when recipes were added. The idea is that before a recipe is applied a checkpoint is set, then when the recipe application fails the configuration is rolled back to the checkpoint.&lt;/p&gt;
&lt;p&gt;There is an &lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3432089&quot;&gt;issue for adding a command to revert to checkpoints&lt;/a&gt; but no work has even started.&lt;/p&gt;
&lt;p&gt;I had the idea already at Drupalcon Barcelona 2024, but after discussions in Leuven I decided to implement a &lt;a href=&quot;https://www.drupal.org/project/config_checkpoint_ui&quot;&gt;UI for the configuration checkpoints&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-does-config-checkpoint-ui-work&quot;&gt;How does Config Checkpoint UI work?&lt;/h2&gt;
&lt;p&gt;So far the first version is very simple: It exposed some of the basic API to the UI. One can create new checkpoints, delete checkpoints and revert the site configuration to an older checkpoint. The reverting is done essentially the same way as the core command does: importing from the checkpoint storage. This is very similar to what Config Split does and the reason for which I thought it was a good idea to try.&lt;/p&gt;
&lt;p&gt;The UI is pretty straight forward:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;backup.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Config Checkpoint overview&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Reverting to a checkpoint is very similar to the familiar configuration import screen:&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;checkpoints.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Reverting a checkpoint&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;future-and-limitations&quot;&gt;Future and Limitations&lt;/h2&gt;
&lt;p&gt;There are a few bugs in other projects to watch out for:
&lt;a href=&quot;https://www.drupal.org/project/project_browser/issues/3521074&quot;&gt;Project browser does not set a checkpoint&lt;/a&gt;, and core has a &lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3521081&quot;&gt;bug when deleting checkpoints&lt;/a&gt;.
Of course there may be lots of bugs in the new module itself. In particular one of the things I have ran into is that the uninstall validation is a bit broken, so some of the recipes can not be rolled back when they add fields that depend on a module added at the same time. I will have to check where exactly this bug is hiding.
More advanced things could also be explored, for example &lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3409718&quot;&gt;merging checkpoints&lt;/a&gt; or &lt;a href=&quot;https://www.drupal.org/project/drupal/issues/3417064&quot;&gt;deleting checkpoints without deleting everything that came before&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Implementing fast end-to-end tests on Drupal with Docker and JSON-RPC</title><link>https://www.nuvole.org/blog/implementing-fast-end-to-end-tests-using-drupal-docker-and-json-rpc/</link><guid isPermaLink="true">https://www.nuvole.org/blog/implementing-fast-end-to-end-tests-using-drupal-docker-and-json-rpc/</guid><pubDate>Mon, 07 Jul 2025 12:24:16 GMT</pubDate><content:encoded>&lt;p&gt;“We’ll add the tests later”. Most of us have said this at some point in our software development career. But then “later” never arrives.&lt;/p&gt;
&lt;p&gt;That is because implementing tests is hard. Setting up the test runners, writing the test cases, keeping them running; all of it requires extra effort. Even more effort is needed for end-to-end tests (E2E) since they touch every part of an application. However, this effort always pays off and in this post, I will share how we implemented end-to-end tests (E2E) on one of our Drupal projects so that you can save some of the effort needed to get started. We are very happy with how this approach turned out and we plan to use it on future projects too.&lt;/p&gt;
&lt;h2 id=&quot;meet-ilo-live&quot;&gt;Meet ILO Live&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://live.ilo.org&quot;&gt;ILO Live&lt;/a&gt; is the &lt;a href=&quot;https://www.ilo.org&quot;&gt;International Labour Organization (ILO)&lt;/a&gt;‘s online video streaming platform. It hosts both live streams and recordings of events organized all across the world by the ILO.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ilo-live-home-page.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Screenshot of ILO Live&amp;#x27;s home page&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ilo-live-group-detail-page_0.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Screenshot of ILO Live&amp;#x27;s group detail page&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;Under the hood, ILO Live is powered by &lt;a href=&quot;https://nextjs.org&quot;&gt;Next.js&lt;/a&gt; and a headless Drupal CMS. Most pages on the site are &lt;a href=&quot;https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation&quot;&gt;statically served&lt;/a&gt; by Next.js.&lt;/p&gt;
&lt;p&gt;In this post, I will take you through how we implemented E2E tests on ILO Live.&lt;/p&gt;
&lt;h2 id=&quot;the-challenges-of-implementing-e2e-tests&quot;&gt;The challenges of implementing E2E tests&lt;/h2&gt;
&lt;p&gt;A common approach when writing frontend tests is to use mocked API responses. But with ILO Live, we wanted to go a step further and use the actual CMS in the tests. By using the actual CMS in the tests, in addition to verifying whether the site works as intended, we can also verify whether changes to the CMS have not caused any functionality on the site to break. But to do this, we had to solve 2 major problems.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How do we orchestrate the CMS into different testing states from the test runner?&lt;/li&gt;
&lt;li&gt;How do we quickly start the CMS during each test run?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;orchestrating-the-drupal-cms-using-a-private-json-rpc-api&quot;&gt;Orchestrating the Drupal CMS using a private JSON-RPC API&lt;/h2&gt;
&lt;p&gt;The simplest way to orchestrate a Drupal CMS is to have a test runner click on various elements to create new entities and change their state. But this takes a lot of time to run, especially when multiple entities need to be created during each test run.&lt;/p&gt;
&lt;p&gt;To solve this problem, we decided to create a private API using the &lt;a href=&quot;https://www.drupal.org/project/jsonrpc&quot;&gt;JSON-RPC&lt;/a&gt; module. This API would only be enabled on development and testing instances of the CMS and it exposed several operations which the test runner could use to orchestrate the CMS into different states.&lt;/p&gt;
&lt;p&gt;For example, the JSON-RPC API exposed a &lt;code&gt;create.event&lt;/code&gt; method for creating new events.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;method&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;create.event&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;params&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;meeting&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;title&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Aragorn meets Gandalf&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;start_date&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2021-06-01 8:00:00&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;end_date&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2021-06-01 17:00:00&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a &lt;code&gt;clean.content&lt;/code&gt; method for resetting the CMS to the initial state with no content set.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;method&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;clean.content&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;params&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We added similar methods for creating other entities on the site and for admin tasks such as reindexing the search index. ILO Live used &lt;a href=&quot;https://aws.amazon.com/appsync/&quot;&gt;AWS AppSync&lt;/a&gt; to receive real-time updates about the current state of events so to simulate this in the tests, we set up a testing instance of AppSync and implemented methods like the &lt;code&gt;update.livestream&lt;/code&gt; method shown below to change the state of this instance.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;method&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;update.livestream&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;params&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;event_id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;status&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;live&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;speeding-up-the-drupal-cmss-startup-time-using-docker&quot;&gt;Speeding up the Drupal CMS’s startup time using Docker&lt;/h2&gt;
&lt;p&gt;Now we had to get the Drupal CMS to run while the test runner was running. We were familiar with using &lt;a href=&quot;https://www.cypress.io&quot;&gt;Cypress&lt;/a&gt; for implementing frontend tests so we decided to use the same for implementing the E2E tests. Since the E2E tests ran assertions on elements on the site / frontend, we decided to store the E2E tests within the frontend repo and use &lt;a href=&quot;https://github.com/features/actions&quot;&gt;Github Actions&lt;/a&gt; to run them since the frontend repo was hosted on Github.&lt;/p&gt;
&lt;p&gt;We used GNU Make and Docker Compose during development to run the CMS and its services (i.e. Maria DB and Redis) so initially we tried to clone the CMS and run the commands to start it. Docker is &lt;a href=&quot;https://github.com/actions/runner-images/blob/ubuntu24/20250511.1/images/ubuntu/Ubuntu2404-Readme.md&quot;&gt;preinstalled by default&lt;/a&gt; on the &lt;code&gt;ubuntu-latest&lt;/code&gt; runner on GitHub Actions so we were able to easily use it there.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Simplified Github actions workflow for running the E2E tests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Tests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;push&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  cypress&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Run Cypress tests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    runs-on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    steps&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Checkout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # Clone the CMS into a subfolder&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Clone CMS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          repository&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;github-org/ilo-live-cms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;          #legacyPath: ./cms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;          # actions/checkout@v2 only has access to the current repo by default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;          # A custom token needs to be provided to it for it to access a different repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;          # https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          token&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.CMS_ACCESS_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          ref&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;develop&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Start CMS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        run&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          cd cms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          make&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # More steps to actually run the tests&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;makefile&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Simplified Makefile from the CMS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: build install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;up&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  @echo &quot;Starting up containers for ilo_live...&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; docker compose up -d --remove-orphans&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: up&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; @echo &quot;Building ilo_live project development environment.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; docker compose exec -T php bash -c &quot;composer install&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;install&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; docker compose exec -T php bash -c &quot;vendor/bin/drush si -y --existing-config --account-pass=\&quot;admin\&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; docker compose exec -T php bash -c &quot;vendor/bin/drush deploy&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Simplified docker-compose.yml for the CMS and its services&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    mariadb&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        image&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;mariadb-image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    php&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        image&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;php-image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        volumes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;./:/var/www/html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    redis&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        image&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;redis-image&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This setup worked but it took around 2 - 4 minutes to run. Most of this was due to Composer and the Drupal site install.&lt;/p&gt;
&lt;p&gt;This delayed each test run so we started looking into ways to improve it. The solution that we came up with was to create a self-contained Docker image for the Drupal CMS. This way, the test runner only needed to pull and run a single image to start the CMS.&lt;/p&gt;
&lt;p&gt;To do this, we set up a Github Actions workflow on the CMS repo to build and push a development Docker image. This image had the JSON-RPC API enabled and it used a SQLite database instead of MariaDB.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;dockerfile&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Simplied Dockerfile for building the development docker image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; drupal-image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Replace the site files in the image with our own&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; rm -rf /opt/drupal/*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;COPY&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ./ /opt/drupal/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Install SQLite and dependencies required by composer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; apt update &amp;#x26;&amp;#x26; apt install -y sqlite3 git zip&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;WORKDIR&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /opt/drupal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; composer install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Use custom settings for the development image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; cp docker/settings.bundle.php web/sites/default/settings.local.php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; vendor/bin/drush site:install -y --existing-config --account-pass=admin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; vendor/bin/drush deploy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Simplified settings.bundle.php for the development image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Enable the config split for the JSONRPC-API for the development image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $config[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;config_split.config_split.jsonrpc&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;status&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; TRUE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Use SQLite instead of MariaDB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    $databases[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;default&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;default&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;database&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;sites/default/files/.ht.sqlite&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;prefix&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;driver&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;sqlite&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;namespace&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Drupal&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;sqlite&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Driver&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Database&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;sqlite&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;autoload&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;core/modules/sqlite/src/Driver/Database/sqlite/&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We used the &lt;a href=&quot;https://www.drupal.org/project/config_split&quot;&gt;Configuration Split module&lt;/a&gt; by our own &lt;a href=&quot;https://www.drupal.org/u/bircher&quot;&gt;Fabian Bircher&lt;/a&gt; to ensure that the JSON-RPC module is only enabled during development.&lt;/p&gt;
&lt;p&gt;Here is the Github Actions workflow we used to build and publish the image to the Github Container registry.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Simplified Github actions workflow for building and publishing the development docker image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Build and push development docker image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  push&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Usually the E2E tests can use the image built from the develop branch of the repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # But to test upcoming changes, we can prefix a branch with docker- to have this workflow build an image for it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    branches&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;develop&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;docker-*&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  build-and-push-image&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    runs-on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # Grant the default `GITHUB_TOKEN` permission to read the current repo and push images to the Github container registry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    permissions&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      contents&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      packages&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    steps&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Log in to the Container registry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;docker/login-action@v3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          registry&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;ghcr.io&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          username&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ github.actor }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          password&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # This action extracts the tags and labels that should be set on the published image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # It exposes them as an output which is consumed by the next step through steps.meta&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Extract metadata (tags, labels) for Docker&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;meta&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;docker/metadata-action@v5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          images&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;ghcr.io/github-org/ilo-live-cms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # This action uses the git repo as the build context by default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Build and push Docker image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;docker/build-push-action@v6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          push&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          file&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;docker/Dockerfile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          tags&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          labels&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ steps.meta.outputs.labels }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the updated workflow on the frontend repo for using the development image.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Simplified Github actions workflow for running the E2E tests using the development docker image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Tests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;push&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  cypress&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Run Cypress tests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    runs-on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    steps&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Checkout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Log in to the Container registry&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;docker/login-action@v3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          registry&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;ghcr.io&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          username&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ github.actor }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          password&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.ACCESS_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Run CMS container&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        run&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          docker run \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          --detach \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          -p 80:80 \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          ghcr.io/github-org/ilo-live-cms:develop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this setup, the development image is built within 5 minutes each time a pull request is merged into the &lt;code&gt;develop&lt;/code&gt; branch of the CMS and the E2E tests workflow can start the CMS in less than a minute.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;image-build-time.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Screenshot of a Github Actions workflow run for building and pushing the development docker image&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;container-start-time.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Screenshot of a Github Actions workflow run for running the E2E Cypress tests&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;An added benefit of this approach is that it was much simpler to run the CMS. Previously, the only way to run the CMS was to clone the CMS repo and set up the project locally alongside the database server. Now it just needed a single command.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --detach&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 80:80&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ghcr.io/github-org/ilo-live-cms:develop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;putting-everything-together&quot;&gt;Putting everything together&lt;/h2&gt;
&lt;p&gt;To use the JSON-RPC API more conveniently within the Cypress tests, we defined several &lt;a href=&quot;https://docs.cypress.io/api/cypress-api/custom-commands&quot;&gt;Custom Commands&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; getRPCPayload&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;method&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;params&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {}) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Get basic authorization header.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; authorization&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Buffer.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    [Cypress.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;CMS_USER&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;), Cypress.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;CMS_PASSWORD&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)].&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;:&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;toString&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;base64&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    method: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    url: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Cypress&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;CMS_URL&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}/path/to/jsonrpc`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    headers: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      Accept: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      Authorization: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`Basic ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;authorization&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    body: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      jsonrpc: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      method,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      params,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      id: Math.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;floor&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(Math.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;random&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Cypress.Commands.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;rpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;method&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;params&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {}) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getRPCPayload&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(method, params)).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Run assertions on the response from the CMS to ensure that the call ran successfully&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    expect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(response.status).to.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;eq&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    expect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(response.body).to.have.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;property&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; response.body.result;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Cypress commands can call other commands and build on top of each other&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// So we created several utility functions to reduce repeated logic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Cypress.Commands.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &quot;createPastEvent&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    type &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;meeting&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    startDate,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    startHour &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {}) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; event&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      type,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      title: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`Past ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      // Not the most robust logic but sufficient for testing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      start_date: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`2023-01-${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;startDate&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;startHour&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}:00:00`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      end_date: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`2023-01-${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;startDate&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;startHour&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}:00:00`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;rpc&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;create.event&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, event);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Command that should be run before each test run&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Cypress.Commands.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;setup&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Cypress runs each test in isolation so that they can&apos;t interfere with each other&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // To enforce that isolation across the entire system, we use this command to reset CMS to its initial state  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;rpc&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;clean.content&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Enable preview mode on Next.js to make it regenerate pages during each request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // https://nextjs.org/docs/pages/guides/preview-mode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/path/to/enable-preview&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These custom commands made it much easier to orchestrate the CMS as required.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;describe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Event page&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  beforeEach&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setup&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  it&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;should display past meetings correctly&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Call the JSONRPC-API to setup the content&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createPastEvent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      // Visit the page corresponding to the newly created content&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;visit&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(res.path);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      // Check if the elements on the page are rendered correctly&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;h1&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;should&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;contain&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Past meeting 1&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;contains&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;1 January 2023&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      cy.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;contains&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;08:00 - 09:00 GMT+1&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;final-results&quot;&gt;Final results&lt;/h2&gt;
&lt;p&gt;Ultimately we implemented over 50 end-to-end tests for this project. The final test suite takes between 7 - 8 minutes to run on a standard Github hosted runner. (i.e. Not a &lt;a href=&quot;https://docs.github.com/en/enterprise-cloud@latest/actions/using-github-hosted-runners/using-larger-runners/about-larger-runners&quot;&gt;large runner&lt;/a&gt;) This runtime is acceptable for us for now but it can be improved even further by using &lt;a href=&quot;https://docs.cypress.io/cloud/features/smart-orchestration/parallelization&quot;&gt;Parallelization&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;final-runtime.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;Screenshot of a Github Actions workflow summary for the E2E Cypress tests&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;While these tests took us some effort to implement, they definitely paid off in the long term. ILO Live had live event functionality which was mission-critical but very hard to orchestrate manually and with the E2E tests, we were able to ensure that it always worked. These E2E tests gave us the confidence to make big improvements to the codebase since we knew that bugs in key functionality would be instantly revealed and this is the biggest benefit of them all.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Written by Prabashwara Seneviratne, frontend developer at Nuvole and author of &lt;a href=&quot;https://www.frontendundefined.com/&quot;&gt;Frontend undefined&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Drupal Dev Days Athens</title><link>https://www.nuvole.org/blog/drupal-dev-days-athens/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-dev-days-athens/</guid><pubDate>Tue, 05 May 2026 11:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week was the annual Drupal Developer Days, and as always it was a fantastic event.&lt;/p&gt;
&lt;h2 id=&quot;config-ignore-34&quot;&gt;Config Ignore 3.4&lt;/h2&gt;
&lt;p&gt;Thanks to having some time away from regular projects, I released &lt;a href=&quot;https://www.drupal.org/project/config_ignore/releases/8.x-3.4&quot;&gt;Config Ignore 3.4&lt;/a&gt;.
The biggest change is that ignoring the settings for Config Ignore now works. By default, Config Ignore now has this bug fixed, but you can configure which of its settings to use.
The README contains more information on the available options, but I would recommend leaving it at the default. The other options allow configuring the behaviour of previous 3.x and even 2.x releases, but they come with their own drawbacks.&lt;/p&gt;
&lt;h2 id=&quot;refactor-to-hook&quot;&gt;Refactor to &lt;code&gt;#[Hook]&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;In the last session of the first day of the conference, I presented &lt;a href=&quot;https://devdays2026.drupal.org.gr/drupal-developer-days-athens-2026/session/refactor-hook&quot;&gt;Refactor to #[Hook]&lt;/a&gt;.
While at Nuvole we have been using the PHP attribute-based approach for hooks since 2022 thanks to the &lt;a href=&quot;https://www.drupal.org/project/hux&quot;&gt;Hux module&lt;/a&gt;, this style of hooks landed in core only a little over a year ago.&lt;/p&gt;
&lt;p&gt;The talk went briefly over what a hook is and what a PHP attribute is, but it also explained how simple it is to refactor an existing codebase to the new style.
Despite (or maybe because of) rehearsing the demo twice, there was a hiccup when showing how to refactor the code on stage.
Unfortunately, I didn’t think of the most obvious solution of restarting DDEV, which would have solved the problem.&lt;/p&gt;
&lt;p&gt;The demo would have concluded by showing the form still working, but I hope participants still took away how accessible the steps are to use Rector to refactor the code.&lt;/p&gt;
&lt;p&gt;Attached are &lt;a href=&quot;/sites/default/files/DrupalDevDays-Athens-2026.pdf&quot;&gt;the slides&lt;/a&gt; with the links to the documentation.&lt;/p&gt;</content:encoded></item><item><title>Drupal Global Training Day in Italy</title><link>https://www.nuvole.org/blog/drupal-global-training-day-italy/</link><guid isPermaLink="true">https://www.nuvole.org/blog/drupal-global-training-day-italy/</guid><pubDate>Wed, 12 Sep 2012 10:08:08 GMT</pubDate><content:encoded>&lt;p&gt;After the &lt;a href=&quot;https://nuvole.org/node/47&quot;&gt;successful experience from last June&lt;/a&gt;, Nuvole is proud to join again the &lt;a href=&quot;http://drupal.org/learn-drupal&quot;&gt;Drupal Global Training Day&lt;/a&gt; initiative on September 14th.&lt;/p&gt;
&lt;p&gt;This time we will hold the event in Parma, Italy, and, following the initiative guidelines, we will give a free generic introduction to Drupal, focusing on Drupal as a platform, its community, what can be easily done with the available modules and what can be reached with a bit (or a lot!) of customization.&lt;/p&gt;
&lt;p&gt;Since there’s nothing better than real use cases to show what Drupal can do, we will also show and discuss in detail some recent Nuvole projects. If you can’t make it to Parma, here are the fact sheets, with screenshots and short texts in Italian, we prepared to describe our most interesting projects:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/sites/default/files/factsheet-1-alfapuentes.pdf&quot;&gt; &lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;factsheet-1-alfapuentes.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt; &lt;/a&gt;
&lt;a href=&quot;/sites/default/files/factsheet-2-bolognaexperts.pdf&quot;&gt; &lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;factsheet-2-bolognaexperts.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt; &lt;/a&gt;
&lt;a href=&quot;/sites/default/files/factsheet-3-esncard.pdf&quot;&gt; &lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;factsheet-3-esncard.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt; &lt;/a&gt;
&lt;a href=&quot;/sites/default/files/factsheet-4-orgalime.pdf&quot;&gt; &lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;factsheet-4-orgalime.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt; &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The event is free to attend, and attendees are also welcome to stay for a buffet lunch. Registration is required. Please see &lt;a href=&quot;http://drupal-parma.eventbrite.it/&quot;&gt;the registration page&lt;/a&gt; for all details.&lt;/p&gt;
&lt;p&gt;factsheet-1-alfapuentes.pdf (1.98 MB) &lt;a href=&quot;/sites/default/files/factsheet-1-alfapuentes.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;factsheet-2-bolognaexperts.pdf (1.75 MB) &lt;a href=&quot;/sites/default/files/factsheet-2-bolognaexperts.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;factsheet-3-esncard.pdf (1.96 MB) &lt;a href=&quot;/sites/default/files/factsheet-3-esncard.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;factsheet-4-orgalime.pdf (3 MB) &lt;a href=&quot;/sites/default/files/factsheet-4-orgalime.pdf&quot;&gt;download&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Open Atrium: a one-stop solution for international cooperation projects</title><link>https://www.nuvole.org/blog/open-atrium-one-stop-solution-international-cooperation-projects/</link><guid isPermaLink="true">https://www.nuvole.org/blog/open-atrium-one-stop-solution-international-cooperation-projects/</guid><pubDate>Fri, 27 Jul 2012 14:06:35 GMT</pubDate><content:encoded>&lt;p&gt;At Nuvole we have always supported the idea that Open Atrium can deal with complex use cases. Modules like &lt;a href=&quot;http://drupal.org/project/spaces&quot;&gt;Spaces&lt;/a&gt;, &lt;a href=&quot;http://drupal.org/project/purl&quot;&gt;PURL&lt;/a&gt; and &lt;a href=&quot;http://drupal.org/project/og&quot;&gt;Organic groups&lt;/a&gt; can push the limit of the platform far beyond being a simple-yet-powerful intranet software. We were already experimenting with building &lt;a href=&quot;https://nuvole.org/blog/2011/feb/07/open-atrium-beyond-intranet&quot;&gt;public websites&lt;/a&gt; and simple distributions-like &lt;a href=&quot;https://nuvole.org/blog/2011/nov/28/minisites-open-atrium&quot;&gt;mini-sites&lt;/a&gt; with Open Atrium for quite some time, but now the new  Alfa Puentes project gave us the opportunity to blend together all those customizations in one powerful platform.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;These concepts will be covered in more detail in the Nuvole &lt;a href=&quot;http://munich2012.drupal.org/program/training/code-driven-development-features-and-beyond&quot;&gt;DrupalCon Munich Training&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h1 id=&quot;the-alfa-puentes-project&quot;&gt;The Alfa Puentes project&lt;/h1&gt;
&lt;p&gt;The &lt;a href=&quot;http://alfapuentes.org/portal&quot;&gt;Alfa Puentes&lt;/a&gt; project’s main aim is to enhance the international cooperation between the European and the Latino-American Higher Education environments by creating a community of teachers, rectors and other related stakeholders. The members of this community need to share data and information both online, using an intranet platform, and offline, by participating to a series of mid-size seminars and conference spread across the two interested regions. Given the diversity of this community, multi-language support was also a strong requirement.&lt;/p&gt;
&lt;h1 id=&quot;open-atrium-to-the-rescue&quot;&gt;Open Atrium to the rescue&lt;/h1&gt;
&lt;p&gt;The platform is organized in three main parts: a public portal, a series of private self-managed intranet groups and &lt;a href=&quot;https://nuvole.org/blog/2011/nov/28/minisites-open-atrium&quot;&gt;mini-sites&lt;/a&gt; for the organization of real-life events. Since it’s developed on a single Open Atrium installation users of the platform can have different roles in each components, e.g., a rector can start a private discussion group while a project partner may help with translations.&lt;/p&gt;
&lt;h1 id=&quot;public-portal&quot;&gt;Public portal&lt;/h1&gt;
&lt;p&gt;The portal is based on our mini-site features set, it contains information about the project, like funding periods and partners involved. All content is available in three languages: English, Spanish and Portuguese. Site managers can easily assign content translation to partners or collaboratively work on a specific section of the website. Even though the portal is implemented as a specific Open Atrium group type, the site look and feel does not resemble in any way its default theme: visitors perceive the portal as being an independent site.&lt;/p&gt;
&lt;div class=&quot;profile-info&quot;&gt;&lt;i&gt;All portal content is available in three languages:&lt;/i&gt;&lt;/div&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ap-multilanguage2.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;profile-info&quot;&gt;&lt;i&gt;Even though it is built as a group the portal does contain highly customized sections:&lt;/i&gt;&lt;/div&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ap-partners.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;private-intranet-groups&quot;&gt;Private intranet groups&lt;/h1&gt;
&lt;p&gt;In a cooperation project it is essential to provide a platform where users can easily engage in a conversation and share relevant information: nothing can be simpler with Open Atrium. Each partner can start a private discussion group and can easily invite users to participate. To facilitate information sharing each group can be powered with our &lt;a href=&quot;https://nuvole.org/blog/2011/may/25/apps-open-atrium-atrium-folders&quot;&gt;Atrium Folders&lt;/a&gt; feature, which provides a more user friendly and familiar way of sharing documents and files than the built-in Notebook feature. The language of each group can be set at its creation, chosen among English, Spanish and Portuguese.&lt;/p&gt;
&lt;div class=&quot;profile-info&quot;&gt;&lt;i&gt;Group owners can invite users to join a group by using powerful search tools:&lt;/i&gt;&lt;/div&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;apinviteusers.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;profile-info&quot;&gt;&lt;i&gt;Atrium Folders enhances the document sharing user experience:&lt;/i&gt;&lt;/div&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;apfolders.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;mini-sites-for-event-organization&quot;&gt;Mini-sites for event organization&lt;/h1&gt;
&lt;p&gt;The mini-site features set also powers the creation and management of &lt;a href=&quot;https://nuvole.org/blog/2011/nov/28/minisites-open-atrium&quot;&gt;mini-sites&lt;/a&gt; for organization of events. Each event site is an independent Open Atrium public group, with a specific Spaces preset which, at every site creation, enables features like static pages, custom navigation menu pre-filled with default content, news section and a spotlight area on the front page. Each mini-site must also provide a customizable per-event registration form which will add specific information (like participation to dinners, workshops, etc…) to the usual user profile data. To implement such a functionality we have integrated the &lt;a href=&quot;http://drupal.org/project/webform&quot;&gt;Webform&lt;/a&gt; module with Open Atrium, ensuring a smooth user experience in managing such a complex use case.&lt;/p&gt;
&lt;div class=&quot;profile-info&quot;&gt;&lt;i&gt;Managers can deploy and customize a fully fledged event website in minutes:&lt;/i&gt;&lt;/div&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;ap_conference.jpg&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;profile-info&quot;&gt;&lt;i&gt;Users can integrate their profile with event-specific information:&lt;/i&gt;&lt;/div&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;apregistration3.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;glue-all-together-the-user-dashboard&quot;&gt;Glue all together: the user dashboard&lt;/h1&gt;
&lt;p&gt;The default Open Atrium user profile section has been customized to be the starting point for the user to find her way trough the system. After the login the user is redirected to her profile page, from where she can have direct access to update her personal information, get to the groups she is member of and manage her event registrations.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;#x22;src&amp;#x22;:&amp;#x22;apuserdashboard.png&amp;#x22;,&amp;#x22;alt&amp;#x22;:&amp;#x22;&amp;#x22;,&amp;#x22;index&amp;#x22;:0}&quot;&gt;&lt;/p&gt;</content:encoded></item></channel></rss>