Jul 07 2017
Jul 07

Drupal 8 at Comic Relief

Over the last year a key objective for the Technology team at Comic Relief has been to build products not websites. Tech Lead, Peter Vanhee, explained in a previous blog post how we’re using Drupal 8 to create a reusable platform product for building campaign websites. Since then the team have been working to deliver another website using the platform codebase and also preparing to open-source the codebase.

We have now opened up this codebase – you can find it here.

Being open makes us better

We strongly believe that working in the open and contributing to the open-source community makes our products better and makes us stronger as a team. It helps us prioritise work more easily as we can be clear about what is and isn’t important to the product, it allows us to say “no” to one hit wonder feature requests and it makes us stricter about dealing with technical debt and ensuring we have appropriate detail in supporting documentation. It is also a fantastic and motivating feeling for the whole team to know that their work is open to others to see and contribute to, and we hope it will allow us to engage further with the thriving open source community and get some external help to expand our codebase further.

Many charities and not for profit organisations are not big enough to have the size of development team we are lucky to have. We feel strongly that we want to help smaller organisations when we can and one of our main motivations for making our platform product open was that other organisations can use it to build their websites. This has already proved successful with Comic Relief’s sister organisation, Red Nose Day USA, using our code to deliver their website in May, shaving months off the time it took to deliver.

Journey to open source

We had to overcome a few obstacles before we were able to open our codebase. These were mostly due to a lack of understanding about what open-source meant and what benefits it could bring to Comic Relief.

Some people asked why we would give away our hard work for free – our view is that we are already benefiting from using Drupal 8, which is open source software itself, so feel that we have a duty to contribute back. We also feel that we have additional, possibly unique, knowledge to add to the community based on our experience of delivering high profile campaign websites each year. We know that Drupal is a commonly chosen technology for other charities and we believe that reducing duplication of work in the sector is worthwhile and an example of how Comic Relief is able to support other charities in a way beyond purely financial.

There was understandable nervousness around security. There is of course a lot diligence required before making code public, including selecting the right license (we choose GNU GPL v2), managing secrets and ensuring that everyone is happy for feature conversations to be available for everyone to see. These considerations are not to be underestimated and take time to resolve. It is also something that needs constant review and should be built into ongoing working practises and team culture. For us, the discipline and professionalism required for working in the open is a significant benefit.

Also, there were a few questions about why we needed to open-source. This was particularly pertinent when it came to prioritising development work as the work required to get us ready to open-source sometimes meant that there was delay to new features being delivered. We combatted this by being clear about the benefits to Comic Relief – there are many, as mentioned above, but additionally we believe the quality of our work will increase with more people to spot bugs and help fix them, we will see an increase in efficiency and reduction in duplication and we will hopefully receive contributions from others that will improve our codebase even further.

Our advice if you’re wanting to open-source your code, or code in the open, is to engage the rest of your organisation early and listen to any reservations people may have. When confronted with internal reservations around going open source, it required us to have patience and perseverance in order to educate our stakeholders about the benefits and reassure them about their perceived risk. Be clear about what the benefits are to your organisation and highlight how the way you work will continue to support your open-source software appropriately. We also found it helpful to show examples of other organisations who have worked in the open such as the BBC, the Guardian and the Government Digital Service.

What is next

Our intention is to continue to open-source our code where we think it could be useful to others and to code in the open wherever possible. We have kick started a mindset shift so that at the start of each new project we try to be open, rather than closed, from the beginning. Live examples of this include developing the pattern lab (which we hope will be useful to partners) and the grants API (demonstrating that prototypes can be developed in the open).

We’re hoping to have discussions and feature requests coming in from other charities looking to adopt our technology and we’ll continue to add new components to the codebase and will be maintaining our workflow queues to organise new work and future iterations.

Finally, we’d love to see other charity websites being powered by our codebase, so please take a look at our open GitHub repositories here. We’d love to hear what you think and your experiences of moving towards open source, or working in the open.

Our journey to open source would not have been possible without the hard work of Peter Vanhee, Caroline Rennie, Andy Phipps, Heleen Mol, Adam Clark, Gustavo Liedke, Zach Bimson, Carlos Jimenez, Pradip Pack, Girish Nair and Zenon Hannick.

Share this:

Like this:

Like Loading...
Jul 06 2017
Jul 06
7 July 2017

Drupal has a thriving community in Bristol and the south-west, and they’ve put on some excellent events over the last few years. Last weekend they had their third DrupalCamp Bristol, and I was fortunate to be able to attend and speak.

The day opened with a keynote by Emma Karayiannis on self care and supporting others within open source communities.

Emma shared some of her contributions to Drupal, where she is part of the Community Working Group and track chair for the Being Human sessions at DrupalCon.

Look after yourself. Don’t feel that you can only contribute a little bit or that your opinion doesn’t matter. Just find something rewarding and start small. Getting involved can be daunting, so get to know the part of the community that deals with your interest, ask how to get involved and ask for someone to help you.

Burnout is real, and happens much more when we work alone, taking on lots of responsibility without anyone to partner with. So look for someone to co-work or co-lead with rather than try to be a lone superhero. It gives you the freedom to step away if necessary. Ask yourself: if I had to stop this tomorrow, what would happen?

It’s easy to become overwhelmed without realising it. You need to regularly check you’re looking after yourself, are still motivated, and aren’t taking on too much. Be accountable to your family, colleagues and friends, and step back if necessary.

Look after others. It’s healthy for an open source community to have people who think differently to you. Be respectful of other people and aware that miscommunication is very easy online, particularly with people whose native language is different to yours. But also accept that you’ll never be able to make everyone happy.

Make sure the people are really ok even if they appear fine. Experienced contributors, remember that you were once a beginner, and provide opportunities and safe spaces to include new people. Appreciate people for who they are and not just the work they do.

After a short break we split into two tracks.

Deji Akala provided an interesting look into the technical details on what happens on each page request. Along the way he summarised various parts of Drupal and concepts such as the autoloader, symfony handlers, the service container and event handling.

It’s an interesting exercise to unpick the index.php file line by line and discover what happens behind the scene in a single line of code:

$response = $kernel->handleRequest();

I then gave a short talk about Composer and Drupal. I’ve spoken to a number of people recently and it’s become clear that there’s still a bit of confusion surrounding how to use Composer with Drupal. I certainly found it unclear and started to look into it.

I pitched this at beginner developers, and the main things I wanted people to go away understanding were:

  • what the require, update and install commands really do
  • the difference between Drupal itself and the various template projects available

That was the first time I’d given that talk. It felt a bit raw but led to some interesting Q&A time, and it’s given me valuable insight for enhancing this in future.

To others contemplating public speaking - do it! Events like this are an ideal place to start, everyone’s friendly and on your side. You’ll gain knowledge, experience and friends from doing it. I was really glad to see that several of the speakers here were first timers - well done!

Ross Gratton shared some insights into using front end task runners like Gulp with Drupal.

Ross has been working on a large Drupal site utilising several themes, in 24 languages and with over 125 custom modules. He discussed the pros and cons of different architectural decisions, such as where to put source code and assets, what to put in version control and how to manage conflicts on such a large site.

He then shared some of the process of separating assets out to the brand level as opposed to a project level, treating a style guide or pattern library as a separate deliverable.

After lunch, George Boobyer spoke on web security, a topic often overlooked in the planning and budgeting of projects.

Security is perceived as complex but isn’t that hard, and any effort you make is rewarded. Recently we’ve seen a lot of ransomware attacks, but often these just have the same impact as a disk failure, so alongside keeping software up to date, have backups and test them.

George gave some examples of websites that had been attacked and were now hosting spam content, very often not visible to the naked eye but only to search engines. Often user data is obtained by way of database dumps that have been left accessible to the world - don’t put these in the document root.

Ana Hassel shared some insights as a freelancer. As a site builder, Ana has been able to use Drupal to focus on her clients’ needs and come up with a repeatable process for estimating and selling her work.

Ana also shared how she had invested some time learning the learning the command line and setting up scripts for everyday tasks. This had given her a better, more repeatable workflow and more predictable deployment and hosting.

An interesting perspective on personal development came from Johan Gant. I felt it complemented Emma’s keynote well with some recurring themes, and gave the day a nice mix of technical and human elements.

Johan covered issues such as Imposter syndrome, depression and burnout. Burnout often comes from a lack of engagement, and seems to be a particular risk for knowledge workers. If the values you have aren’t aligned with those of your employer or project, you can burn out very quickly.

Be selective about what you learn—patterns and techniques will last for a long time, whereas frameworks come and go. You need to make time to explore new things, but make sure you are following your interests rather than trends. Avoid stagnation—ask yourself if what you’re doing is satisfying. It’s healthy to seek new challenges, but means getting out of your comfort zone.

Lee Stone finished by sharing about how his organisation does extensive code reviews.

As well as preventing bugs, code reviews aid in training. New developers can learn the business by reading code, and junior developers can grow by asking why something is the way it is, or by asking about things they don’t understand. They often bring fresh ideas this way.

It’s important to review the code, not the person writing it. So don’t make these things too personal, and don’t take them personally! Prefer terms like “we” rather than “I” and “you” to foster a sense of team, and provide solutions rather than just stating something’s wrong.

After the talks we headed to ZeroDegrees in Bristol for a social time. It was great to catch up over dinner with people I hadn’t seen for a while, and make some new friends.

Thanks to everyone who helped make DrupalCamp Bristol such a great event. See you next year!

Jul 05 2017
Jul 05
July 5th, 2017

We’re happy to announce the new Global Academy for continuing Medical Education (GAME) site! GAME, by Frontline, provides doctors and medical professionals with the latest news and activities to sharpen their skills and keep abreast on the latest medical technologies and techniques.

As a followup to our launch of Frontline Medical communication’s MDEdge portal last October, the new GAME site takes all of the strengths of MDEdge—strong continuing education materials, interactive video reviews, content focused on keeping medical professionals well-trained—and wraps that in a fresh new package. The new GAME site is optimized for performance so that visitors can learn from their phones on-the-go, in the field on their tablets, or at their desktops in the office between meetings. Behind the scenes, site administrators have an interface that streamlines their workflow and allows them to focus on creating content.
[NB: Read our MDEdge launch announcement, here.]

The Project

Four Kitchens worked with the Frontline and GAME teams to…

  • migrate a bevy of static and dynamic content from their existing Typo3 CMS site and ten external ASP-based conference sites.
  • create a method to streamline canonical content sharing between the GAME site and the MDEdge portal through web standard APIs, and a mirror API for automated content creation from the portal to the GAME site.
  • create a single domain home for conferences originally resting on multiple source domains, redirecting as needed while keeping the source domains public for advertising use without requiring extra domain hosting.
  • provide functional test coverage across the platform for high-value functionality using Behat and CircleCI.
  • revise the design and UX of the site to help engage users directly with the content they were seeking.

Engineering and Development Specifics

Check out the new Global Academy for continuing Medical Education (GAME) site today!

  • built on Drupal 7
  • hosted on Pantheon Elite
  • code standards enforced with ESLint and PHP_CodeSniffer
  • site migration via custom migration module plugins and Google Docs mapping
  • custom MDEdge and other 3rd party integrations
  • style guide produced and reviewed using Emulsify

The Team

The Four Kitchens team of Web Chefs included James Todd as technical lead, Chris Roane as lead engineer, Randy Oest as the designer and frontend engineer, and Scott Riley as the project manager. Additional engineering work was completed by Diego Tejera, Justin Riddiough, and Web Chef Patrick Coffey.

Web Chef James Todd
James Todd

James tinkers with hardware, software, and everything in between.

Jul 04 2017
Jul 04

Blocks, as the name suggests, are pieces of content that can be placed anywhere on your Drupal site. They can contain simple text, forms or something with complex logic.

In this tutorial, you’ll learn how to create a block using custom code and how to use Drupal Console to generate it. If you’ve used blocks in Drupal 7 then you will be familiar with the new interface in Drupal 8. If you’re a site builder, the whole process of creating, editing and deleting a block is very intuitive.

Before jumping into code, let’s talk about the Drupal Block UI and understand what has changed since Drupal 7.

Block Layout Page

This page will now list only the blocks assigned to a certain region. You’ll notice that a single block can now be assigned to multiple regions and that’s because blocks are now entities.

If you want to assign a block to a region, just click the “Place Block” button and you’ll be presented with an overlay showing all the available blocks.

This page can be accessed by the URL /admin/structure/block

If you want to learn how to create custom block types check out our tutorial: Build a Blog in Drupal 8: Managing Blocks

Create a Block using the Drupal Interface

Like the content page, custom blocks are now listed on a dedicated page, which uses a views to list all the entities of type Block. Use the toolbar menu to access that page: Structure -> Block Layout -> Custom Block Library.

In order to create a new block, click “Add custom block” and select a block type. As mentioned before, Blocks are now fieldable entities (like nodes), that’s why you can see different block types.

This was a big step when compared to Drupal 7 because we could not add new fields to blocks nor use the same block in multiple regions, which was a pain.

Create a Block using Code

Just like the previous version, Drupal 8 also allows us to create blocks using code. With the OOP concepts introduced in this version 8, it’s even more simple and intuitive to use the APIs Drupal provides. So let’s start.

1. Create a module

Go create a folder under “/modules/custom” (you will need to create the ‘custom’ folder), called “my_block_example”.

Inside this folder, create the “.info.yml” file

my_block_example.info.yml

name: My Block Example
type: module
description: Defines a custom block.
core: 8.x
package: WebWash
dependencies:
  - block

Once the folder and file has been created, go enable the module. Please note, Drupal 8 does not require a “.module”.

2. Create a Block Class

In this step, we’ll create a class that will contain the logic of our block. Following the PSR-4 standards, we’ll place our PHP class under /modules/custom/my_block_example/src/Plugin/Block. Create this folder structure and then create a new file called MyBlock.php under it.

The path to the block class should end up being my_block_example/src/Plugin/Block/MyBlock.php.

This file will contain:

  • Annotation meta data, that will allow us to identify the Block. If you want to read more about annotations, check read “Annotations-based plugins” on drupal.org
  • The MyBlock class, containing 4 methods:
build(), blockAccess(), blockForm(), and blockSubmit()

So, let’s create it

<?php

namespace Drupal\my_block_example\Plugin\Block;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Provides a block with a simple text.
 *
 * @Block(
 *   id = "my_block_example_block",
 *   admin_label = @Translation("My block"),
 * )
 */
class MyBlock extends BlockBase {
  /**
   * [email protected]}
   */
  public function build() {
    return [
      '#markup' => $this->t('This is a simple block!'),
    ];
  }

  /**
   * [email protected]}
   */
  protected function blockAccess(AccountInterface $account) {
    return AccessResult::allowedIfHasPermission($account, 'access content');
  }

  /**
   * [email protected]}
   */
  public function blockForm($form, FormStateInterface $form_state) {
    $config = $this->getConfiguration();

    return $form;
  }

  /**
   * [email protected]}
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['my_block_settings'] = $form_state->getValue('my_block_settings');
  }
}

If you prefer, grab a copy of the code from GitHub.

That’s it! Your block is created and ready to be used! Just assign the block to a region and you should see it.

Now let’s go through the methods in more detail.

build()

This method will render a renderable array. In our case, we’re returning a basic markup but we could have returned a more complex code, like a form or a views.

The code below demonstrates how to render a form as an example:

/**
 * [email protected]}
 */
public function build() {
  return \Drupal::formBuilder()->getForm('Drupal\my_module\Form\MyBlockForm');
}

blockAccess()

This method allows you to define custom access logic. In our example, any user with the ‘access content’ will see the block.

Notice that we’re calling a method from the AccessResult class here. The static method allowedIfHasPermission will check if the current user (or anonymous) has access to view content (in this case).

blockForm()

This method allows you to define a block configuration form. Let’s suppose we want to render custom text inside this block instead of the static one. All we need to do is to create a form using the Form API and then define whatever fields we need.

blockSubmit()

This is where we save the configuration defined on the previous method.

Create a Block using Drupal Console

If you’re familiar with Drupal Console you already know its power!

Luckily it’s possible to use this tool to create the boilerplate code for custom blocks. Let’s go through the steps, and you should be done in less than one minute.

If you already have Drupal Console installed, follow the steps below, otherwise just follow the instructions on the site to install it.

Generating the block boilerplate code using Drupal Console

Open the terminal and navigate to your Drupal site root folder and run this command:

drupal generate:plugin:block

Select the module you want to create the block under and answer the following questions and you are all set!

Summary

The concept of a block hasn’t really changed, but how they’re implemented has. Code for each block is neatly organized in its own class. Whereas in Drupal 7, all block code was thrown into the “.module” file and got messy quickly.

This code can be found and downloaded from: https://github.com/rafaelferreir4/my_block_example/

Resources

Jun 29 2017
Jun 29
June 29th, 2017

Recently I was working in a Drupal 8 project and we were using the improved Features module to create configuration container modules with some special purposes. Due to client architectural needs, we had to move the /features folder into a separate repository. We basically needed to make it available to many sites in a way we could keep doing active development over it, and we did so by making the new repo a composer dependency of all our projects.

One of the downsides of this new direction was the effects in CircleCI builds for individual projects, since installing and reverting features was an important part of it. For example, to make a new feature module available, we’d push it to this ‘shared’ repo, but to actually enable it we’d need to push the bit change in the core.extension.yml config file to our project repo. Yes, we were using a mixed approach: both features and conventional configuration management.

So a new pull request would be created in both repositories. The problem for Circle builds—given the approach previously outlined—is that builds generated for the pull request in the project repository would require the master branch of the ‘shared’ one. So, for the pull request in the project repo, we’d try to build a site by importing configuration that says a particular feature module should be enabled, and that module wouldn’t exist (likely not present in shared master at that time, still a pull request), so it would totally crash.

There is probably no straightforward way to solve this problem, but we came with a solution that is half code, half strategy. Beyond technical details, there is no practical way to determine what branch of the shared repo should be required for a pull request in the project repo, unless we assume conventions. In our case, we assumed that the correct branch to pair with a project branch was one named the same way. So if a build was a result of a pull request from branch X, we could try to find a PR from branch X in the shared repo and if it existed, that’d be our guy. Otherwise we’d keep pulling master.

So we created a script to do that: &lt;?php $branch = $argv[1]; $github_token = $argv[2]; $github_user = $argv[3]; $project_user = $argv[4]; $shared_repos = array( 'organization/shared' ); foreach ($shared_repos as $repo) { print_r("Checking repo $repo for a pull request in a '$branch' branch...\n"); $pr = <strong class="markup--strong markup--pre-strong">getPRObjectFromBranch</strong>($branch, $github_token, $github_user, $project_user, $repo); if (!empty($pr)) { print_r("Found. Requiring...\n"); exec("<strong class="markup--strong markup--pre-strong">composer require $repo:dev-$branch</strong>"); print_r("$repo:dev-$branch pulled.\n"); } else { print_r("Nothing found.\n"); } } function <strong class="markup--strong markup--pre-strong">getPRObjectFromBranch</strong>($branch_name, $github_token, $github_user, $project_user, $repo) { $ch = curl_init(); curl_setopt($ch,CURLOPT_URL,"https://api.github.com/repos/$repo/pulls?head=$project_user:$branch_name"); curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); curl_setopt($ch, CURLOPT_USERPWD, "$github_user:$github_token"); curl_setopt($ch, CURLOPT_USERAGENT, "$github_user"); $output=json_decode(curl_exec($ch), TRUE); curl_close($ch); return $output; } $branch=$argv[1];$github_token=$argv[2];$github_user=$argv[3];$project_user=$argv[4];$shared_repos=array(  'organization/shared'foreach($shared_reposas$repo){  print_r("Checking repo $repo for a pull request in a '$branch' branch...\n");  $pr=<strongclass="markup--strong markup--pre-strong">getPRObjectFromBranch</strong>($branch,$github_token,$github_user,$project_user,$repo);if(!empty($pr)){    print_r("Found. Requiring...\n");    exec("<strong class="markup--strongmarkup--pre-strong">composer require $repo:dev-$branch</strong>");    print_r("$repo:dev-$branch pulled.\n");  else{    print_r("Nothing found.\n");function<strongclass="markup--strong markup--pre-strong">getPRObjectFromBranch</strong>($branch_name,$github_token,$github_user,$project_user,$repo){  $ch=curl_init();    curl_setopt($ch,CURLOPT_URL,"https://api.github.com/repos/$repo/pulls?head=$project_user:$branch_name");  curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);  curl_setopt($ch,CURLOPT_USERPWD,"$github_user:$github_token");  curl_setopt($ch,CURLOPT_USERAGENT,"$github_user");$output=json_decode(curl_exec($ch),TRUE);  curl_close($ch);  return$output;

As you probably know, Circle builds are connected to the internet, so you can make remote requests. What we’re doing here is using the Github API in the middle of a build in the project repo to connect to our shared repo with cURL and try to find a pull request whose branch name matches the one we’re building over. If the request returned something then we can safely say there is a branch named the same way than the current one and with an open pull request in the shared repo, and we can require it.

What’s left for this to work is actually calling the script:

- php scripts/require_feature_branch.php "$CIRCLE_BRANCH" "$GITHUB_TOKEN" "$CIRCLE_USERNAME" "$CIRCLE_PROJECT_USERNAME" -phpscripts/require_feature_branch.php"$CIRCLE_BRANCH""$GITHUB_TOKEN""$CIRCLE_USERNAME""$CIRCLE_PROJECT_USERNAME"

We can do this at any point in circle.yml, since composer require will actually update the composer.json file, so any other composer interaction after executing the script should take your requirement in consideration. Notice that the shared repo will be required twice if you have the requirement in your composer.json file. You could safely remove it from there if you instruct to require the master branch when no matching branch has been found in the script, although this could have unintended effects in other types of environments, like for local development.

Note: A quick reference about the parameters passed to the script:

$GITHUB_TOKEN: #Generate from <a class="markup--anchor markup--pre-anchor" href="https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Fsettings%2Ftokens" target="_blank" rel="nofollow noopener noreferrer" data-href="https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Fsettings%2Ftokens">https://github.com/settings/tokens</a> $CIRCLE_*: #CircleCI vars, automatically available $GITHUB_TOKEN:#Generate from <a class="markup--anchor markup--pre-anchor" href="https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Fsettings%2Ftokens" target="_blank" rel="nofollow noopener noreferrer" data-href="https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Fsettings%2Ftokens">https://github.com/settings/tokens</a>$CIRCLE_*:#CircleCI vars, automatically available

[Editor’s Note: The post “Running CircleCI Builds Based on Many Repositories” was originally published on Joel Travieso’s Medium blog.]

Web Chef Joel Travieso
Joel Travieso

Joel focuses on the backend and architecture of web projects seeking to constantly improve by considering the latest developments of the art.

Web Chef Dev Experts
Development

Blog posts about backend engineering, frontend code work, programming tricks and tips, systems architecture, apps, APIs, microservices, and the technical side of Four Kitchens.

Read more Development
Jun 27 2017
Jun 27

Content with many fields can be overwhelming when it comes to adding and editing data. Also, creating layouts to display the content is often a complex task. Field Group can solve both of these issues.

Using this module, fields can be grouped in a variety of ways including tabs, accordions and HTML elements. Field Group not only works for editing content, it can also be used to group and structure fields so that great layouts can be created with little effort.

In the first part of this tutorial, we’ll show how to group fields to make editing content easier. The second part will demonstrate how to display groups of fields to create a simple but effective layout.

Field Group Types

There are several different options when creating a field group:

  • Accordion – expandable groups of fields with only one group expanded at a time.
  • Details – expandable groups of fields that can be independently expanded or collapsed.
  • Fieldset – simple grouping of fields.
  • HTML element – grouping of fields by element such as div or section.
  • Tabs – grouping of fields in vertical or horizontal tabs.

Each option allows you to add CSS classes making it easy to style the output. Groups can be nested inside other groups and for some options this is required. For example, “Accordion items” need to be nested within an Accordion group and individual Tab groups would normally be created within a surrounding Tabs group.

We initially used version 8.x-1.0-rc6 to write this article but noticed that some of the effects, such as collapsing/expanding accordions didn’t work as expected. Updating to the latest available dev build (8.x-1.0-rc6+12-dev at the time of writing) resolved these issues. As always, be cautious when using pre-release or dev builds of modules on production servers.

Getting Started

Field Group doesn’t have any dependencies apart from the Field module in Core, so you can get started straight away by downloading the Field Group module.

If you prefer command line tools then you can download and install Field Group using Drush or the Drupal Console.

Using Drush:

$ drush dl field_group
$ drush en field_group

Using Drupal Console:

$ drupal module:download field_group --latest
$ drupal module:install field_group

Configuring Field Group on Content Form

In this section, we’ll show how to group fields to make editing them easier on the content form. This is particularly useful when there are many fields, as grouping them into appropriate sections makes it easier to find the fields you need to change.

As we’ve already seen, fields can be grouped in a variety of ways. There are pros and cons for each option but we will use Details in this section as it offers a simple interface that works well on all screen sizes.

In this tutorial, we’ll create a simple system for storing contact information. We’ll start off with a set of individual fields and then we’ll add Field Group to create sections based on address, phone numbers and a description. Feel free to pick your own field names but we’ll use the following:

  • Title (change the field label to Name)
  • House number
  • Street
  • City
  • ZIP code
  • Home phone
  • Mobile phone
  • Description

1. Create a content type called “My Contacts” and add the fields listed above. For help on setting up new content types and fields have a look at our “Content Types and Fields” tutorial. We’ve also changed the Title’s field label to Name on the Edit tab.  You should have a screen that looks similar to this.

Screenshot of Manage Fields, listing the fields used in this tutorial

2. With the content type and fields set up, click on the “Manage form display” tab. Field Group adds a new button near the top called “Add group”. Click on that button.

Screenshot showing Field Group's "Add group" button

3. On the next screen, select Details from the drop-down list. The label will be used as the heading of the section, so use an appropriate name such as Address and then click on “Save and continue”.

Screenshot showing a new Details group being created

4. By default, when using the Details group, it’s closed so only the heading is visible. If you want the fields for that group to be shown by default then tick the “Display element open by default” checkbox. It’s normally best to leave the “Mark group as required if it contains required fields” checkbox ticked, especially if the group is closed by default, as this adds the red “required” asterisk to the group heading if any of the fields it contains are required. If you want to add styling to the group then you can add an ID and classes. Click on “Create group” to complete the process.

Screenshot showing options for a Field Group

5. Repeat the process for the other groups, which in our example are Phone and Description. On the “Manage form display” tab you should now see the list of fields and the groups that you have created.

Screenshot listing all the fields and new groups

6. Now it’s just a simple matter of dragging the fields so they appear under the groups. To become part of the group the fields should be below the group name and indented as shown below.

Screenshot showing the fields listed under their Field Group

7. Click on Save to complete the process.

8. Add a new node based on this content type. When editing the content, you should see expandable sections that contain fields.

Screenshot showing fields grouped when creating a new node

Configuring Field Group on Content View Modes

In the section above we dealt with grouping when editing content. In this section, we’ll look at how creating a few groups can greatly help with content layout. With very little effort it’s simple to create great layouts that are easy to style. And you can do this without the use of complex modules like Display Suite or Panels.

We’ll create a few “HTML element” groups and then apply simple CSS to style the output. The instructions below assume that you have set up the fields above.

1. Edit your content type, click on the “Manage display” tab and then click on the “Add group” button.

Screenshot showing the "Add group" button on the "Manage display" tab

2. Select “HTML element” from the “Add a new group” drop-down box, give the group a label such as Address and click on “Save and continue”.

Screenshot showing "HTML element" selected

3. On the next screen there are several options. Under Element, enter the most appropriate element for your content. We’re just going to use a simple div. You can decide to show or hide the label that you entered on the previous screen. We’ll stick with the default of No. You can also add attributes and configure effects if required. As we want to apply some basic CSS to our layout, we’ll add a class of simple-box under “Extra CSS classes”. Once you have entered all the settings, click on “Create group”.

Screenshot showing options for "HTML elements"

4. Create any remaining groups using the same settings. As before, in addition to Address, we’ve created Phone and Description groups.

5. On the “Manage display” tab drag the fields under the appropriate groups, making sure each field is indented as shown below. At this point we’ll also change our labels to be inline rather than above and hide the Description label.

Screenshot showing fields listed under their "HTML element" Field Group

6. Click on Save to complete the process.

7. Go back to the content you created earlier and you should see something like this.

Screenshot of node before CSS applied

8. The screenshot above doesn’t look any different from normal but if you inspect the markup you’ll see that the fields are grouped into three divs, with the class of simple-box that we added above. The screenshot shows the output from the Bartik theme.

Screenshot of Firebug output showing the Field Group divs

9. Then it’s just a matter of adding CSS as appropriate. An example that works for the Bartik theme is:

.node__content {
  display: flex;
}

.simple-box {
  flex-grow: 1;
  flex-basis: 0;
  margin: 10px;
  padding: 10px;
  border: 1px solid #ccc;
}

And the output is transformed into this:

Screenshot of node after CSS applied

This example gives a simple demonstration of how using groups can make laying out information on a page much easier. Field Group also allows you to nest groups within groups, so you can create more complex layouts easily, making this an even more powerful module.

Summary

In this tutorial, we’ve seen how the Field Group module can help organize fields into sections to make editing simpler. We’ve also demonstrated how adding groups can structure markup to make creating layouts much simpler.

FAQs

Q: Why can’t I get the effects to work?
There seems to be an issue with 8.x-1.0-rc6. Try upgrading to the latest dev build and try again.

Q: Can I nest groups within groups?
Yes!

Jun 18 2017
Jun 18
Recently I set out to make a simple instrument for running simpletest tests without having LAMP stack installed on your local environment. I needed this for two reasons:
  1. for running tests locally
  2. for running tests on CI server
I've decided to use Docker and create monolith container with Drupal and all the LAMP stuff inside and here what I've got: docker-tester.


How to use


Before running container you have to setup next ennvironment variables:
  1. KEEP_RUNNING - specify  yes  if you want to keep container running when tests will be executed. Use for debugging purposes only. Default value is  no .
  2. DRUPAL_VERSION - specific version of Drupal. Supported Drupal 7 and Drupal 8. Example:  8.3.2 .
  3. MODULES_DOWNLOAD - a list of modules to download (by Drush) separated by comma. Example:  module_name-module_version,[...] .
  4. MODULES_ENABLE a list of modules to enable (by Drush) separated by comma. Example:  module_name,[...] .
  5. SIMPLETEST_GROUPS - a list of simpletest groups to run separated by comma. Example:  Group 1,[...] .
  6. SIMPLETEST_CONCURRENCY - amount of test runners to test code in parallel. Default value is  1 .
Then you need to build an image for container:
docker build -t drupal-tester .
Next you have two options: either run container with docker-compose tool or run container manualy with docker command.
For local usage I prefere to use docker-compose because it's easier than write all the CLI docker command manualy. Just specify what module you want to test inside of docker-compose.yml file and run:
docker-compose up && docker-compose down
It will run the container, install Drupal inside of it and run tests. That's all.

For running tests on CI server I use docker command and specify all the needed environment variables manualy:

docker run -v $(pwd)/test_results:/var/www/html/test_results -v $(pwd)/custom_scripts:/var/www/html/custom_scripts -e KEEP_RUNNING=no -e DRUPAL_VERSION=8.3.2 -e MODULES_DOWNLOAD=module-version -e MODULES_ENABLE=module -e SIMPLETEST_GROUPS=module_test_group -e SIMPLETEST_CONCURRENCY=1 drupal-tester
It allows you to override environment variables and volumes that you want to mount inside of the container. So you can setup different jobs on your CI server to test different modules on different Drupal versions with the help of this one container.

When docker finished the process all test results by default will be placed into test_results directory but you can easily override this by mounting some other directory inside of a container.

Setup and customization


Sometimes you need to do something before running tests. For example override some module specific settings or setup some Drupal variables etc. You can get it done with custom *.sh scripts. Just write sh file with all needed actions/commands and put it inside custom_scripts folder. All the files inside of this directory will be executed before running tests.
Jun 14 2017
Jun 14

Pathauto is a module which lets you automate the generation of URL aliases in Drupal. Instead of the URL being “/node/123”, you can have “/blog/article/why-use-drupal”.

The module allows you to define custom patterns which are generated when an entity is created.

URL aliases or URL slugs, help with search engine optimization and they’re more user-friendly.

Drupal core has supported URL aliases for a long time, but they weren’t automatically generated. Pathauto helps with automating the process.

In this tutorial, you’ll learn how to create aliases and patterns, and how to bulk generate paths.

Getting Started

Before we begin, go download and install the following modules:

  1. Pathauto
  2. Token
  3. Ctools

Using Drush:

$ drush dl pathauto token ctools
$ drush en pathauto

Or, using Composer:

$ composer require drupal/pathauto

Manually Create URL Aliases

Pathauto is not required to create aliases. Drupal core uses a module called Path to create them, and it depends on this module. Pathauto simply helps you automate the creation process.

URL aliases can be created in two ways: from the content edit form and the “URL Aliases” page.

To create an alias from the form, click on the “URL path settings” field-set on the right of the form. Then enter the path into “URL alias”.

Another way, go to Configuration, “URL Aliases” and click on “Add alias”.

Create Pathauto Patterns

Let’s first look at how to setup Pathauto patterns. A pattern lets you define what the structure of the URL alias should be. For example, we’ll add “article/[node:title]” for the Article content type.

The module will convert “article/[node:title]” to “article/node-title”. [node:title] will be replaced by the article title.

1. Go to Configuration, “URL aliases” and click on the Patterns tab.

2. Click on “Add Pathauto pattern”.

3. Select Content from “Pattern type” and enter “article/[node:title]” into “Path pattern”.

If you want to see all available tokens, click on “Browse available tokens”.

4. Check Article from “Content type”.

This means that this pattern will only be applied to Article content types.

5. And finally, add Article into Label.

Then click on Save.

Generating an Alias

If you go to the “URL path settings” on a content type, you’ll notice that it looks different once a pattern has been enabled. Now you get a new checkbox “Generate automatic URL alias”.

If this stays checked, then an alias will be generated. If you want to override the generated one, then uncheck it and add your custom alias into the “URL alias” field.

Pathauto Settings

The module settings can be configured by clicking on the Settings tab from the “URL aliases” page.

You can configure a lot on this page, but the few important ones are:

Enable entity types

This lets you turn on Pathauto support for custom entities.

Update action

This lets you define what the module should do when an entity is updated.

Strings to Remove

This lets you define which words will be stripped from the alias.

Punctuation

This allows you to control how special characters are handled.

Now just a friendly warning. Do NOT play around with these settings on a live site. The last thing you want to do is break the URLs on a site that’s already in production. Backup the database before you make any changes.

Bulk Generate Aliases

If you already have a ton of content and want to generate aliases or you want to regenerate them, you can do this by clicking on the “Bulk generate” tab.

First, select which entity type you want to bulk generate. Then select which aliases you want to be generated.

Before running any bulk generation make sure you backup your database.

Delete Aliases

You can batch delete aliases from the “Delete aliases” tab. You can choose which entity types you want to be deleted, or delete all aliases.

But take note of the “Delete options”, make sure you check “Only delete automatically generated aliases”.

This won’t delete aliases which are manually created.

Menu Structure as Path

The challenge in creating a good pattern is trying to figure out which token to use.

Just click on “Browse available tokens.” and look at all the available options. It can be overwhelming to try and figure out what token does what.

One common pattern which I’ve used a few time is to have a path use the parent menu path.

Take for example the following structure:

- Drupal (/drupal)
-- Site Building (/drupal/site-building)
--- Using Views (/drupal/site-building/using-views)

Just imagine the above example is part of the main navigation. “Drupal”, is the first level, “Site Building” is the second and “Using Views” is the third.

Notice how the path for “Using Views” has its parent path, “/drupal/site-building/using-views”. To achieve this type of path just use “[node:menu-link:parent:url:path]” to get the parent.

The full pattern with the title will be: “[node:menu-link:parent:url:path]/[node:title]”.

If you know of any useful tokens, let us know by leaving a comment.

Summary

Pathauto is an essential module which I’ve installed on every Drupal site I’ve worked on. The importance of URL aliases isn’t obvious at first. But if you spend a bit of time coming up with a good set of patterns, it’ll help your site rank well in search engines.

Jun 14 2017
Jun 14

Being one of the first early adopters of Drupal Commerce 2.x, starting our first project in early 2016 (on alpha2 version) and soon after a second one, I originally planned to write a blog post soon after beta1 was published. Unfortunately, I was too busy at that time. Beta1 was released by the end of September 2016, and I postponed writing the post from one time to another. I wanted to share my experience, giving valuable tips, stating my opinion on maturity and the project's overall quality, etc.

By time of writing, we already have beta7 out and we're approaching the first release candidate. In the past couple of weeks/months, I could observe how the project gathered more and more pace. There's a significant amount of people building Commerce 2.x sites and many great developers are participating in both reporting bugs and features requests, as well as fixing the bugs, developing the features, writing documentation, and so on.

And there's also a number of blog posts, tutorials, and conference speeches out there, so I won't add another one here. If you want to get a quick overview of some of the great improvements and changes in the new Commerce 2.x, I can recommend e.g. this post by Sascha Grossenbacher.

Instead I've decided to write about the personal benefits I've gathered throughout my work on our first Commerce 2.x projects, besides having a great and immense flexible e-commerce framework. Where developers can enjoy working, and actually concentrate on implementing their business logic, to build great solutions for their clients without being limited. This post may get a bit longer (don't say, I haven't warned you before :p)

Decision Making Process

First, we should take a short excursion to the decision making process of our projects, and turn back the time a little bit. In the end of 2015 we signed a deal to re-launch a B2B store, previously running on an outdated Magento version having lots of individual needs, especially when it comes to pricing, shipping calculation, etc. Those are the kind of features in many e-commerce systems that are either not realisable at all, or only in a very time-consuming and inconvenient way. Those are the features where you, while developing, have to work against the system you build on. Staying on Magento was no real option at all, as in version 1.9 some of the feature requests would have been a nightmare to implement. The customer wasn't too much satisfied with their old 1.x at all and the EOL was already visible on the horizon. At that time Magento 2 was quite surprisingly released, after having a long period where development progress stuck a bit. The first insights, however, were there was as its best alpha mature software released stable under a great time pressure. I can't remember any other software where I encountered as many bugs on a test installation in such a short time.

However, we were preferring Drupal Commerce over Magento anyway, as already Commerce 1.x had this great flexibility you need for highly customized projects, so that would have been our logical first choice. But at that time, we have already fully switched the development of websites to Drupal 8, starting with a late beta in autumn and fully switching after rc1 release in October 2015. Although that was a huge change, developing on Drupal 8 was like love at first sight. The new architecture is so great, leaving no limits for fulfilling any wish and offering the necessary base for writing quality code. As I stated in an earlier blog post, to a developer with Java origins Drupal 8 feels far more like "real" programming, quite close to writing Java or .NET applications.

Code review

I had been reading the Commerce Guys blog posts about progress and future plans in the Commerce 2.x project with great interest for the preceding couple for months, and was already quite excited about that. There were many great ideas about architecture and sustainable decisions, such as first building Drupal independent PHP libraries for addressing, currency, taxes and formatting related tasks.

The time had come to have a closer look at the source code - and I was really excited about what I saw. The code base wasn't feature complete at all - and for sure, one or another smaller bug could be hiding somewhere - but the things that already existed, were well considered, well written, cleanly formatted, and greatly covered by unit tests extracting nearly every possible advantage the Drupal 8 and Symfony API is offering. I'd go so far to say that reading Commerce 2.x source code is mostly like reading a teaching book (a good one of course). You hardly find that in other open source projects. Off the top of my head, I could only think of the legendary Spring Framework (ok, to be fair, of course also of great PHP frameworks like Symfony, Laravel,..).

In Commerce Guys We Trust

Beside of the code review, the "Commerce Guys factor" was the most important one. Imho it's essential to assess a module also on its maintainership, especially if the module is playing a central role in your project, and even more if it's not finished and stable at the time of starting your project. And here are some points why I fully trusted in CG:

Their CEO Ryan Szrama can look back on many years of experience in developing Drupal based e-commerce frameworks and solutions. He started the Ubercart project in Drupal 5 and also lead the Drupal 6 version. Then he proved that he won't always go the easy way and he has got a sure feeling for making decisions, that are both challenging but also wise and sustainable. He decided to combine both the lessons learned from Ubercart and the new possibilities Drupal 7 offered and started to write a new e-commerce suite from ground up - Drupal Commerce was born. This was a shift from the encapsulated out-of-the-box solution Ubercart was, to a fully flexible platform utilizing Core improvements (e.g. entity API) as well as the power of contrib modules such as Rules and Views. The success justified this move.

And now again in the Drupal 8 version, brave decisions were taken instead of choosing the easy path. Switching to D8 is especially for bigger and more complex modules tied up with some work anyway. But still many try to just port their functionality 1:1 in order to just get a working D8 version. But the Commerce developers with leading Bojan Živanović the way as new project mastermind and Matt Glaman as his congenial co-maintainer, decided to utilize the unique chance to rewrite the whole project in order to make full use of the opportunities Drupal 8 offers. And again, the new architecture was build up on the results of self-evaluation of Commerce 1.x pros and cons, as well as intensive research on competitors, existing e-commerce platforms, research papers, etc

Especially I'm raising my hat for the incredible consequence the team showed all the time. We all know how time pressure can lead to trade-offs in quality and abandon plans of certain features etc. Although the development of Commerce 2.x took far longer as expected and planned, they never left the right track. They never huddled and committed immature stuff. They never wrote a feature without backing this by tests. They always went the extra mile, if certain lower level functionality that should be rather part of Core or a dedicated contrib module, wasn't present or working as expected, and rather tried to push core APIs or contrib modules forward, instead including a quick custom solution inside Commerce. While Magento tricked the world by releasing and selling an immature and at best alpha state software as stable after being under time pressure, the Commerce Guys kept working honestly to deliver a great piece of quality software at the end. Thank you so much for that!

The Win-Win-Win Situation

Let's return to the actual topic of this post. What I love most on working with open-source software is that, in the best case, every stakeholder benefits: you, your client, and lots of other people in the open source community, when you give and contribute back.

Our clients now have got decent e-commerce solutions tailored to their needs with a long lifespan. We did not only benefit from getting money for doing our work, but also I can proudly say that my Drupal skills improved during my work on Commerce projects. On the one side, it boosted the speed of my Drupal 8 adoption. Although we've implemented already a couple of Drupal 8 websites before, all having some individual requirements, resulting in writing custom modules, custom entity types, etc, working with Commerce showed me an ideal-typical way of how things like service collectors and plugin managers work in Symfony/Drupal 8.  I also learned how to implement certain design patterns in Symfony/Drupal 8 and it showed me some hidden, at that time undocumented, features like some specific properties of entity type annotations, that aren't present in the typical examples. Or simply reminded me to cleanly document every function.

But it wasn't only the coding stuff that helped me to improve. More important is that it somehow got me much more involved into the community than before. I always tried to contribute as much as possible by proposing patches, commenting on issues, founding and maintaining a few smaller modules. But (e.g.) I never used IRC before. I must admit that I missed the access to this rather nerd communication tool. I didn't deal with how to use this (tool support in Windows is rather bad imho), and I didn't see much benefit in using it all. Doing asynchronous communication on issue reports was all I needed. However, there were many things to discuss and coordinate with Bojan especially. So I dove into using IRC, where I since then regularly hang out to both ask for help and help others - as well as in Slack since shortly. And there's one aspect, I've not considered before. That may sound a bit sentimental, but having these direct conversations gives you a lot more of that team feeling, even - or maybe especially because - they are spread all over the world, and you know none of them personally. And you can see that there are also great persons behind that great developers you meet. That said, I should give some shouts out now. To Bojan, who is for sure one of the best PHP developers I encountered so far. To Matt and Ryan, to Jingsheng Wang, who was also one of the very first brave early adopters out there, to Josh Miller, with whom I've worked together on Commerce Wishlist module, to Guy Schneerson, maintainer of Commerce Stock and techno enthusiast *nz nz nz nz* *shaking*, and many others, who work hard for our community.

And finally, some words about the third win. I feel it as my duty to contribute and give something back to the community, because that's how OSS works, and that's especially, why Drupal stands out of other OSS projects. So I'm also quite proud that I'm the author of no less than 22 commits of Drupal Commerce and overall credited in 37 issues of Drupal Commerce over the past 13 months. Of course, there're some smaller issues part of it, but there were also more important ones like implementing the price resolvers. I've also helped to rewrite Commerce Wishlist module - which was rather simple because I could based this on the work of Bojan and Matt in commerce_order, and started three Commerce contrib modules (Commerce order number, Commerce Quantity Increments, Commerce Open Payment Platform), as well as contributed several patches to other contrib modules and also to Core.

I'm looking forward to the forthcoming release candidate. If you haven't tried Commerce 2.x so far, you should definitely try and consider it for your next project. Despite being labelled as "beta" (or soon release candidate), it's very stable already, only missing some UX in certain parts. Trust me, it's better to rely on conservative self-evaluation taking semantic versioning serious than on marketing-focused solutions that just label any immature crap as stable.

original post: https://www.agoradesign.at/blog/how-drupal-commerce-2x-improved-my-skills

Jun 13 2017
Jun 13

As the technological landscape changes, there has emerged a growing discussion amongst developers regarding the best way to go about generating content to be used across multiple applications. There are two trains of thought -- a traditional, coupled Content Management System (CMS) with supporting Application Program Interface (API), tied to both a backend and frontend, OR a headless API-only CMS tied only to a backend. Naturally, as a Drupal web development agency, we look to understand this discussion so that we know where Drupal fits in.

Why does it matter?

We’ve long since moved passed the days of website only content. While websites drive the majority of the digital content we consume, we now have emerging technologies challenging this traditional interface.

As an example, take a smart thermostat in your home. There’s no way you’re going to be viewing the weather network’s website on a small screen that may not have much of a graphical display. So, how does the network provide content to this thermostat? Simple, the thermostat’s software accesses the information via a CMSes API, taking only what it needs and displaying it on the display.

This same API might also be providing information to a weather app on your phone, and your smartwatch, and your car, and … the list goes on.

This is the power of a centralized content system with an API that has the ability to distribute that content to any device that can access the API. If it wasn’t for this, someone would need to generate that content separately for each device, a monumental task.

Coupled vs. Headless CMSes

The first question to understand is: What’s the difference is between these two approaches?

Coupled CMS (with supporting API)

A traditional CMS, such as Drupal, allows content editors to add or edit content and have immediate feedback as to how the content will display. This is because a traditional CMS is tied to a front end.

The front end is what a user sees when viewing an application, which, in Drupal’s primary case, is a website. Marketers and content editors can view the content before it’s publicly available using tools such as inline editing or pre-published previews. Drupal shines in this regard, and it’s graphical interface and available modules allow for quick and relatively easy modification to how the data is displayed on the frontend. This makes it so that a developer isn’t always needed to make simple changes, which can be more efficient for both time and cost, possibly a huge benefit to using a coupled CMS.

Of course, a coupled CMS must have an API that other applications can interface with. This is more of a secondary feature of a traditional CMS, but that is changing with the times. Drupal 8 has a strong emphasis on providing many API services out of the box (https://www.drupal.org/docs/8/api), and there is a strong push to make sure ongoing development takes an API-first approach.

Headless CMS (the API-only approach)

A headless CMS is considered an API-only approach. It is a content hub only and therefore has no front end. These backend only systems allow marketers, content editors and software to publish content which then gets distributed automatically to any integrated application. Applications must be developed in order to access this content, since there is no coupled front end interface to immediately view the data

A benefit to this approach is that people, or software, generating content don’t need to know anything about development or UI in order to provide the content for their applications. All they need to provide is the data. In this way, teams can be separated and roles can be clearly defined.

The downside, of course, is that without a fully integrated front end, marketers and content editors are limited to what they can do with the content. With no immediate feedback as to how content it will appear before it gets pushed out to the public, trialing and proofing content before can be difficult. Layout can also be a limitation to marketing teams. A developer would need to step in if any application presentation needs to change.

Conclusion - Where does Drupal fit?

Being a big part of the Drupal community (https://www.drupal.org/acro-media-inc) we’re seeing the discussion first hand about where Drupal fits in. Luckily, with Drupal 8, the community has already taken a solid step towards making sure Drupal continues to be a relevant contender as either a coupled OR headless CMS.

We already love and use Drupal as a traditional CMS for building websites. There will always be a use case out there where a headless CMS is best, but, by making sure that there is a strong underlying API that applications can interact with, we get the best of both worlds with a single package. And, for our customers and anyone else hiring a Drupal agency, you also continue to benefit from the massive, dedicated, open source community that is always ready to help. With Drupal, you’re covered.

Jun 12 2017
ao2
Jun 12

Most of the information I have come across about migrating from Drupal 6 to Drupal 8 is about migrating content, however before tackling this problem another one must be solved, maybe it is obvious and hence understated, so let's spell it out loud: preserving the site functionality.

That means checking if the contrib modules need to be ported to Drupal 8, and also checking if the solution used in the previous version of the site can be replaced with a completely different approach in Drupal 8.

Let's take ao2.it as a study case.

When I set up ao2.it back in 2009 I was new to Drupal, I choose it mainly to have a peek at the state of Open Source web platforms.

Bottom line, I ended up using many quick and dirty hacks just to get the blog up and running: local core patches, theme hacks to solve functional problems, and so on.

Moving to Drupal 8 is an opportunity to do things properly and finally pay some technical debt.

For a moment I had even thought about moving away from Drupal completely and use a solution more suited to my usual technical taste (I have a background in C libraries and linux kernel programming) like having the content in git and generate static web pages, but once again I didn't want to miss out on what web frameworks are up to these days, so here I am again getting my hands dirty with this little over-engineered personal Drupal blog, hoping that this time I can at least make it a reproducible little over-engineered personal Drupal blog.

In this series of blog posts I'll try to explain the choices I made when I set up the Drupal 6 blog and how I am re-evaluating them for the migration to Drupal 8.

The front page view

ao2.it was also an experiment about a multi-language blog, but I never intended to translate every content, so it was always a place where some articles would be in English, some in Italian, and the general pages would be actually multi-language.

This posed a problem about what to show on the front page:

  • If every node was shown, there would be duplicates for translated nodes, which can be confusing.
  • If only nodes in the current interface language were shown, the front page would list completely different content across languages, which does not represent the timeline of the blog content.

So a criterion for a front page of a partially multi-lingual site could be something like the following:

  • If a node has a translation in the current interface language, show that;
  • if not, show the original translation.

The “Select translation” module

In Drupal 6 I used the Select translation module which worked fine, but It was not available for Drupal 8.

So I asked the maintainers if they could give me the permission to commit changes to the git repository and I started working on the port myself.

The major problem I had to deal with was that Drupal 6 approached the multi-language problem using by default the mechanism called "Content translations" where separate nodes represented different translations (i.e. different rows in the node table each with its own nid), tied together by a tid field (translation id): different nodes with the same tid are translations of the same content.

Drupal 8 instead works with "Entity translations", so one single node represents all of its translations and is listed only once in the node table, and actual translations are handled at the entity field level in the node_filed_data table.

So the SQL query in Select translation needed to be adjusted to work on the node_filed_data rather than of the node table, as it can be seen in commit 12f70c9bb37c.

While at it I also took the chance to refactor and clean up the code, adding a drush command to test the functionality from the command line.

The code looks better structured thanks to the Plugin infrastructure and now I trust it a little more.

Preserve language

On ao2.it I also played with the conceptual difference between the “Interface language” and the “Content language” but Drupal 6 did not have a clean mechanism to differentiate between the two.

So I used the Preserve language module to be able to only switch the interface language when the language prefix in the URL changed.

It turns out that an external module is not needed anymore for that because in Drupal 8 there can be separate language switchers, one for the interface language and one for the content language.

However there are still some issues about the interaction between them, like reported in Issue #2864055: LanguageNegotiationContentEntity: don't break interface language switcher links, feel free to take a look and comment on possible solutions.

More details about the content language selection in a future blog post.

Jun 04 2017
Jun 04
5 June 2017

This is the last part of a series on improving the way date ranges are presented in Drupal, by creating a field formatter that can omit the day, month or year where appropriate, displaying the date ranges in a nicer, more compact form:

  • 24–25 January 2017
  • 29 January–3 February 2017
  • 9:00am–4:30pm, 1 April 2017

The first post, looked at porting some existing code from Drupal 7 to Drupal 8, adding an automated test along the way. In the second post, we made the format configurable.

There’s currently no administrative interface though, so site builders can’t add and edit formats from Drupal’s UI. We’ll add that in this last post.

Routing

According to the routing overview on drupal.org, a route is a path which is defined for Drupal to return some sort of content on.

For our administrative interface, we want to define a number of routes:

  • /admin/config/regional/date_range_format - show a list of the formats, with links to:
  • /admin/config/regional/date_range_format/add
  • /admin/config/regional/date_range_format/*/edit
  • /admin/config/regional/date_range_format/*/delete

There are two ways in which our module can provide routes. We could include a routing.yml file along with our module. This file contains the same kind of information as would have been in hook_menu in Drupal 7. But it’s a static file—if we want something that’s dynamic we can provide it at runtime using a route provider.

For dealing with entities, it’s often much easier to use Drupal’s bundled AdminHtmlRouteProvider class. This examines various properties on the entity annotation—we’ll look at those next—and provides suitable routes for us automatically.

To use this route provider, we add the following to the entity annotation:

@ConfigEntityType(
  …
  handlers = {
    "route_provider" = {
      "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
    },
  },
  …
)

At this point we need to run the drupal router:rebuild command from Drupal console. We must do this whenever we change a routing.yml file or any of the properties in the entity that affect routes.

The collection view

An entity can define a collection view—typically a page showing a list of entities with links to edit them. Drupal provides a list builder which can be used to show a list of entities with buttons for common add/edit/delete type tasks. We’ll create one of these for our new configuration entity:

<?php
namespace Drupal\daterange_compact;

class DateRangeFormatListBuilder extends ConfigEntityListBuilder {
  function buildHeader() {
    /* return an array of column headings */
  }
  function buildRow(EntityInterface $entity) {
    /* return an array of column values for the given entity */
  }
}

We then associate this list builder with our entity by declaring it within the @ConfigEntityType annotation:

handlers = {
  "list_builder" = "Drupal\daterange_compact\DateRangeFormatListBuilder",
}

The actual list builder is quite a rich, showing examples of different ranges. You can see the full implementation here.

The collection page

Once we have the list builder in place, we can add the collection link to our @ConfigEntityType annotation. The route provider will pick up on this link template and provide a route for the entity collection page automatically.

links = {
  "collection" = "/admin/config/regional/date_range_format"
}

By defining the link, our page appears at the appropriate URL. Note that the add/edit/delete links won’t show just yet—we still have to define those.

The date and time range configuration page, showing a list of available formats The screen for listing date/time range formats, provided by the entity list builder.

Updating the main configuration page

In order to reach this new page, we’ll create a menu link on the main configuration page, within the regional and language section. We do that by supplying a daterange_compact.links.menu.yml file:

entity.date_range_format.collection:
  title: 'Date and time range formats'
  route_name: entity.date_range_format.collection
  description: 'Configure how date and time ranges are displayed.'
  parent: system.admin_config_regional
  weight: 0

That link gives us the starting point for our interface:

The system configuration navigation, showing a link to date and time range formats Date/time range formats are accessed via the main configuration page.

We can now view all the date and time range formats from the main administrative interface in Drupal. Next we’ll build some forms to maintan them, after which the add/edit/delete links should start to appear on our collection page.

Forms

We need a form to be able to edit date range formats. The same form is used to create new ones. Drupal provides a lot of built-in functionality via the EntityForm class which we can extend. Drupal will then take care of loading and saving the entity. We just need to provide the form elements to map values on to our entity’s properties.

Adding & editing

We can add any number of forms, but we only need one to edit an existing format, and we can reuse the same form for adding a new format. This form is defined as a class, and lives in src/Form/DateRangeFormatForm.php:

<?php
namespace Drupal\daterange_compact\Form;

class DateRangeFormatForm extends EntityForm {
  /* implementation */
}

Configuration entities don’t use the field API, so we need to build the form ourselves. Although the form looks quite complicated and has a lot of options, it’s reasonably easy to build—each property in the configuration entity can be populated by a single element, like this:

$form['label'] = [
  '#type' => 'textfield',
  '#title' => $this->t('Label'),
  '#maxlength' => 255,
  '#default_value' => $this->entity->label(),
  '#description' => $this->t("Name of the date time range format."),
  '#required' => TRUE,
];

The full implementation of the form is here.

We also need to tell Drupal about this form, which we can do by adding the following to the @ConfigEntityType annotation:

"form" = {
  "add" = "Drupal\daterange_compact\Form\DateRangeFormatForm",
  "edit" = "Drupal\daterange_compact\Form\DateRangeFormatForm",
}

We also add some links, to match up operations such as add and edit with the new form. These are also defined in the @ConfigEntityType annotation:

links = {
  "add-form" = "/admin/config/regional/date_range_format/add",
  "edit-form" = "/admin/config/regional/date_range_format/{date_range_format}/edit",
}

If we look at the collection view again we see that alongside each format there is a link to edit it. That is because of the edit-form link declared in the annotation.

We also want a link at the top of that page, to add a new format. We can do that by providing an action link that refers to the add-form link. This belongs in the daterange_compact.links.action.yml file:

entity.date_range_format.add_form:
  route_name: 'entity.date_range_format.add_form'
  title: 'Add format'
  appears_on:
    - entity.date_range_format.collection

At this point we have a means of adding and editing formats. Our form looks like this:

The date and time range configuration page, showing our new format for editing The screen for editing date/time range formats.

Deletion

Deleting entities is slightly different. We want to show a confirmation page after a before performing the actual deletion. The EntityDeleteForm class does just that. All we need to do is subclass it and provide the wording for the question:

<?php
namespace Drupal\daterange_compact\Form;

class DateRangeFormatDeleteForm extends EntityDeleteForm {
  public function getQuestion() {
    return $this->t('Are you sure?');
  }
}

We declare this form and link on the @ConfigEntityType annotation in the same way as for add/edit:

"form" = {
  "delete" = "Drupal\foo\Form\DateRangeFormatDeleteForm"
}
links = {
  "delete-form" = "/admin/config/regional/date_range_format/{date_range_format}/delete",
}

Conclusion

That’s it. We’ve got a field formatter to render date and time ranges in a very flexible way. Users can define their own formats thorough the web interface, and these are represented as configuration entities, giving us all the benefits of the configuration management initiative, such as predictable deployments and multilingual support.

The module is available at https://www.drupal.org/project/daterange_compact.

I hope you found this write-up useful.

Want to help?

I’m currently working on getting this module up to scratch in order to have coverage from the Drupal security team. If you want to help make that happen, please review the code following this process and leave a comment on this issue. Thanks :-)

May 31 2017
May 31
May 31st, 2017

In the last post, we created a nested accordion component within Pattern Lab. In this post, we will walk through the basics of integrating this component into Drupal.

Requirements

Even though Emulsify is a ready-made Drupal 8 theme, there are some requirements and background to be aware of when using it.

Emulsify is currently meant to be used as a starterkit. In contrast to a base theme, a starterkit is simply enabled as-is, and tweaked to meet your needs. This is purposeful—your components should match your design requirements, so you should edit/delete example components as needed.

There is currently a dependency for Drupal theming, which is the Components module. This module allows one to define custom namespaces outside of the expected theme /templates directory. Emulsify comes with predefined namespaces for the atomic design directories in Pattern Lab (atoms, molecules, organisms, etc.). Even if you’re not 100% clear currently on what this module does, just know all you have to do is enable the Emulsify theme and the Components module and you’re off to the races.

Components in Drupal

In our last post we built an accordion component. Let’s now integrate this component into our Drupal site. It’s important to understand what individual components you will be working with. For our purposes, we have two: an accordion item (<dt>, <dd>) and an accordion list (<dl>). It’s important to note that these will also correspond to 2 separate Drupal files. Although this can be built in Drupal a variety of ways, in the example below, each accordion item will be a node and the accordion list will be a view.

Accordion Item

You will first want to create an Accordion content type (machine name: accordion), and we will use the title as the <dt> and the body as the <dd>. Once you’ve done this (and added some Accordion content items), let’s add our node template Twig file for the accordion item by duplicating templates/content/node.html.twig into templates/content/node--accordion.html.twig. In place of the default include function in that file, place the following:

{% include "@molecules/accordion-item/accordion-item.twig"
   with {
      "accordion_term": label,
      "accordion_def": content.body,
   }
%}

As you can see, this is a direct copy of the include statement in our accordion component file except the variables have been replaced. Makes sense, right? We want Drupal to replace those static variables with its dynamic ones, in this case label (the node title) and content.body. If you visit your accordion node in the browser (note: you will need to rebuild cache when adding new template files), you will now see your styled accordion item!

But something’s missing, right? When you click on the title, the body field should collapse, which comes from our JavaScript functionality. While JavaScript in the Pattern Lab component will automatically work because Emulsify compiles it to a single file loaded for all components, we want to use Drupal’s built-in aggregation mechanisms for adding JavaScript responsibly. To do so, we need to add a library to the theme. This means adding the following code into emulsify.libraries.yml:

accordion:
  js:
    components/_patterns/02-molecules/accordion-item/accordion-item.js: {}

Once you’ve done that and rebuilt the cache, you can now use the following snippet in any Drupal Twig file to load that library [NB: read more about attach_library]:

{{ attach_library('emulsify/accordion') }}

So, once you’ve added that function to your node–accordion.html.twig file, you should have a working accordion item. Not only does this function load your accordion JavaScript, but it does so in a way that only loads it when that Twig file is used, and also takes advantage of Drupal’s JavaScript aggregation system. Win-win!

Accordion List

So, now that our individual accordion item works as it should, let’s build our accordion list. For this, I’ve created a view called Accordion (machine name: accordion) that shows “Content of Type: Accordion” and a page display that shows an unformatted list of full posts.

Now that the view has been created, let’s copy views-view-unformatted.html.twig from our parent Stable theme (/core/themes/stable/templates/views) and rename it views-view-unformatted--accordion.html.twig. Inside of that file, we will write our include statement for the accordion <dl> component. But before we do that, we need to make a key change to that component file. If you go back to the contents of that file, you’ll notice that it has a for loop built to pass in Pattern Lab data and nest the accordion items themselves:

<dl class="accordion-item">
  {% for listItem in listItems.four %}
    {% include "@molecules/accordion-item/accordion-item.twig"
      with {
        "accordion_item": listItem.headline.short,
        "accordion_def": listItem.excerpt.long
      }
    %}
  {% endfor %}
</dl>

In Drupal, we don’t want to iterate over this static list; all we need to do is provide a single variable for the  Views rows to be passed into. Let’s tweak our code a bit to allow for that:

<dl class="accordion-item">
  {% if drupal == true %}
    {{ accordion_items }}
  {% else %}
    {% for listItem in listItems.four %}
      {% include "@molecules/accordion-item/accordion-item.twig"
        with {
          "accordion_term": listItem.headline.short,
          "accordion_def": listItem.excerpt.long
        }
      %}
    {% endfor %}
  {% endif %}
</dl>

You’ll notice that we’ve added an if statement to check whether “drupal” is true—this variable can actually be anything Pattern Lab doesn’t recognize (see the next code snippet). Finally, in views-view-unformatted--accordion.html.twig let’s put the following:

{% set drupal = true %}
{% include "@organisms/accordion/accordion.twig"
  with {
    "accordion_items": rows,
  }
%}

At the view level, all we need is this outer <dl> wrapper and to just pass in our Views rows (which will contain our already component-ized nodes). Rebuild the cache, visit your view page and voila! You now have a fully working accordion!

Conclusion

We have now not only created a more complex nested component that uses JavaScript… we have done it in Drupal! Your HTML, CSS and JavaScript are where they belong (in the components themselves), and you are merely passing Drupal’s dynamic data into those files.

There’s definitely a lot more to learn; below is a list of posts and webinars to continue your education and get involved in the future of component-driven development and our tool, Emulsify.

Recommended Posts

  • Shared Principles There is no question that the frontend space has exploded in the past decade, having gone from the seemingly novice aspect of web development to a first-class specialization.…
  • Webinar presented by Brian Lewis and Evan Willhite 15-March-2017, 1pm-2pm CDT Modern web applications are not built of pages, but are better thought of as a collection of components, assembled…
  • Welcome to Part Three of our frontend miniseries on style guides! In this installment, we cover the bits and pieces of atomic design using Pattern Lab.
Evan Willhite
Evan Willhite

Evan Willhite is a frontend engineer at Four Kitchens who thrives on creating delightful digital experiences for users, clients, and fellow engineers. He enjoys running, hot chicken, playing music, and being a homebody with his family.

Development

Blog posts about backend engineering, frontend code work, programming tricks and tips, systems architecture, apps, APIs, microservices, and the technical side of Four Kitchens.

Read more Development
May 31 2017
May 31

Drupal Commerce Marketing

We are prepping hard this week for IRCE 2017, the big internet retailer show June 6-9th in Chicago, which means readying demos, writing marketing material and practicing pitches. If you will be attending IRCE 2017, come see us at booth 1948 and we'll show you what we've been upto.

While Drupal Commerce offers great features and flexibility, I find people don't know all of its capabilities because the people involved are great at coding, but maybe not so great at marketing. On that note, I thought I would share some of the marketing collateral we've done up for IRCE, all of which we will be providing back to the community so anyone can use it to help sell Drupal Commerce. Our big goal is to have lots of nice web content and even somewhat of a "press kit" that everyone can use to pitch Drupal Commerce to their clients, bosses, friends, family, casual acquaintences, etc.

We have a whole bunch of material written up, as well as a nice Commerce 2.x demo. We're still in the mad rush of finishing it up, so it isn't all available yet, but it will be available over the next few weeks (stay tuned).

Without further rambling, here is our first go at a feature list for Drupal Commerce 2.x. I have provided a PDF and source text at the bottom of this article, should you want to use this content in your own marketing. Also, if you have any feedback, feel free to email, tweet @shawnmmccabe, yell at me on the street or use our contact form.

acro_DC_feature_sheet-1.jpg

acro_DC_feature_sheet-2.jpg

May 30 2017
May 30

You’re not short on choice when it comes to debugging a Drupal website.

You can install Devel and use Kint to print variables to the screen. Or you could use Web Profiler to add a toolbar at the bottom of your site to see how things are performing.

If you’re looking for a proper debugger look no further than Xdebug. It integrates with a lot of IDEs and text editors and I’d recommend you try it out if you’ve never used it.

I recorded a webinar about Drupal 8 debugging which you can watch above.

Here is what I covered in the video:

  • Turn off caching (02:01)
  • Twig debugging (08:26)
  • Using Kint (19:25)
  • Print variables using Kint in Twig template (21:16)
  • Using WebProfiler (22:15)
  • WebProfiler IDE link (26:37)
  • Drupal console debugging commands (31:41)
  • Adding breakpoints to PhpStorm (39:14)
  • Adding breakpoints to Twig templates (43:48)
  • Drupal integration with PhpStorm (45:26)
  • PhpStorm plugins (47:52)

PHP Functions

PHP has two handy functions which can be used to print variables, objects and arrays to the screen.

print_r()

var_dump()

Drupal core comes with its own function: debug().

Devel Module

Devel has been around for as long as I’ve been using Drupal. It comes with a bunch of helper functions for module developers and it has a few handy sub-modules.

The two sub-modules worth mentioning are Kint and Web Profiler.

Kint

This module integrates the Kint library into Drupal and allows you to print variables using the PHP functions: ksm() and kint().

You can also print variables in Twig templates using {{ kint() }}.

Click here to learn how to use Kint in Drupal 8.

Web Profiler

The Web Profiler sub-module adds a toolbar at the bottom of your site and displays useful stats about the number of queries, memory usage and more.

The toolbar gives valuable insight into what’s happening in your Drupal site.

If you want to learn more about Web Profiler, check out our tutorial on using Web Profiler in Drupal 8.

Drupal Console

Drupal Console is a CLI tool for Drupal. It’s implemented using the Symfony Console component. It can be used to provision new Drupal sites, generate boilerplate code and debug Drupal.

Drupal Console comes with a bunch of debug commands. Just search for any command with the term “debug”.

drupal list | grep "debug"

The two that I found most useful are router:debug and container:debug.

Drupal Settings

Drupal 8 caches a lot more things than Drupal 7. Individual rendered elements like a block for example will be cached. Even if you’re logged in or not.

Rendered Twig templates are also cached. This makes Drupal 8 fast, but it can complicate things when you’re writing code. You don’t want to rebuild the site cache every time you make a change in a template or a rendered array.

Most of this caching can be turned off by disabling them in a settings file.

Drupal.org has a good page: “Disable Drupal 8 caching during development”.

Twig Template Discovery

To turn on Twig debugging, make sure you follow the link above. Then add the following into development.services.yml:

parameters:
  twig.config:
    debug: true
    auto_reload: true
    cache: false

The debug: true parameter turns on Twig’s debugging, Twig will display information such as which template it used and its path. It does this my adding HTML comments.

Use the HTML comments to figure out which Twig template you should override and its file name.

PhpStorm

PhpStorm is a commercial IDE which is popular with PHP and Drupal developers. It integrates nicely with Xdebug and the Drupal code base.

Xdebug

Using PhpStorm, you can add a breakpoint somewhere in PHP code and step through as the Drupal request is executed. You can also see what variables are available.

Learn how to configure Xdebug in PhpStorm.

Drupal Integration

PhpStorm also offers Drupal integration and when enabled it allows autocomplete functionality for hooks. No longer will you have to remember a specific hook and its arguments.

Make sure you turn on the integration by searching for “drupal” in the Preferences section.

Extra PhpStorm Plugins

Drupal 8 uses the YAML format for a lot of things throughout its code base; services, routing, permissions, etc…

And in these files you’ll see references to classes and methods.

Take for example this route:

node.add:
  path: '/node/add/{node_type}'
  defaults:
    _controller: '\Drupal\node\Controller\NodeController::add'
    _title_callback: '\Drupal\node\Controller\NodeController::addPageTitle'

There’s no easy way to navigate to the NodeController other than searching for the class name.

However, if you install these three PhpStorm plugins, you’ll be able to navigate to the class or method by pressing command or control then clicking on the reference.

Once you’ve downloaded these plugins, enable them and then enable “Symfony integration”.

Then you should be able to navigate to classes by clicking on the reference while pressing command or control.

Summary

As you can see, you have options when it comes to debugging. If you’re looking for something simple then use Kint. If you prefer a proper debugger then look at Xdebug.

What’s your preferred technique? Leave a comment below.

May 25 2017
May 25

The Weekly Updates are Back!

As you probably noticed, the Commerce weekly updates were on hiatus. I have officially pawned off all my difficult work to other staff, so I can get back to doing these! So much to talk about with Commerce 2 on the home stretch and how community contrib has really picked up speed, I will get back to doing these every week.

Commerce POS

https://www.drupal.org/project/commerce_pos

We're nearly at Release Candidate 1, which should release by the end of the month. We have only 5 remaining blockers that need to be finished.

https://www.drupal.org/project/issues/search/commerce_pos?project_issue_...

After that, we plan to start work on the Drupal 8 version. I have Bojan, from Commerce Guys, tentatively lined up to give us some help with architecture, so hopefully it will mesh very nicely with Commerce 2.x.

Commerce Migrate

https://www.drupal.org/project/commerce_migrate

u/quietone did a bunch of work on this and, although she is finishing off a client project right now, she will be back on full time contrib shortly and remain so for the rest of the year. Since she works on migrate for core a lot, she's been making really good progress on this.  Bojan and I were shooting for a stable version around late July when Commerce 2.x is expected to release. Right now it has working, but rough, implementations for Ubercart 6, Ubercart 7 and Commerce 1.x. We hope to have versions for Shopify and Magento as well, and there are prototypes already finished.

Commerce Licensing/Recurring

https://www.drupal.org/project/commerce_license

u/Kazanir is working on this, as he wrote the licensing stuff for 1.x and is doing the port to 2.x.  He's newer to Drupal 8 and Commerce 2.x and it's kicking his butt a bit, but he's getting there.  We should be getting an alpha probably within a month.

Commerce 2.x

Some of our new hires are going to be working on this as they are on their last phase of training. They do contrib for a few weeks before jumping into teams to do client work.

Commerce XLS Import

https://www.drupal.org/project/commerce_xls_import

There are a number of patches that have been submitted by other people in the community that need review. I have reviewed a few, but more waiting. Hopefully I get those finished before we leave for IRCE.

May 24 2017
May 24
May 24th, 2017

In the last post, we introduced Emulsify and spoke a little about the history that went into its creation. In this post, we will walk through the basics of Emulsify to get you building lovely, organized components automatically added to Pattern Lab.

Prototyping

Emulsify is at its most basic level a prototyping tool. Assuming you’ve met the requirements and have installed Emulsify, running the tool is as simple as navigating to the directory and running `npm start`. This task takes care of building your Pattern Lab website, compiling Sass to minified CSS, linting and minifying JavaScript.

Also, this single command will start a watch task and open your Pattern Lab instance automatically in a browser. So now when you save a file, it will run the appropriate task and refresh the browser to show your latest changes. In other words, it is an end-to-end prototyping tool meant to allow a developer to start creating components quickly with a solid backbone of automation.

Component-Based Theming

Emulsify, like Pattern Lab, expects the developer to use a component-based building approach. This approach is elegantly simple: write your DRY components, including your Sass and JavaScript, in a single directory. Automation takes care of the Sass compilation to a single CSS file and JavaScript to a single JavaScript file for viewing functionality in Pattern Lab.

Because Emulsify leverages the Twig templating engine, you can build each component HTML(Twig) file and then use the Twig functions include, embed and extends to combine components into full-scale layouts. Sound confusing? No need to worry—there are multiple examples pre-built in Emulsify. Let’s take a look at one below.

Simple Accordion

Below is a simple but common user experience—the accordion. Let’s look at the markup for a single FAQ accordion item component:

<dt class="accordion-item__term">What is Emulsify?</dt>
<dd class="accordion-item__def">A Pattern Lab prototyping tool and Drupal 8 base theme.</dd>

If you look in the components/_patterns/02-molecules/accordion-item directory, you’ll find this Twig file as well as the CSS and JavaScript files that provide the default styling and open/close functionality respectively. (You’ll also see a YAML file, which is used to provide data for the component in Pattern Lab.)

But an accordion typically has multiple items, and HTML definitions should have a dl wrapper, right? Let’s take a look at the emulsify/components/_patterns/03-organisms/accordion/accordion.twig markup:

<dl class="accordion-item">
  {% for listItem in listItems.four %}
    {% include "@molecules/accordion-item/accordion-item.twig"
      with {
        "accordion_item": listItem.headline.short,
        "accordion_def": listItem.excerpt.long
      }
    %}
  {% endfor %}
</dl>

Here you can see that the only HTML added is the dl wrapper. Inside of that, we have a Twig for loop that will loop through our list items and for each one include our single accordion item component above. The rest of the component syntax is Pattern Lab specific (e.g., listItems, headline.short, excerpt.long).

Conclusion

If you are following along in your own local Emulsify installation, you can view this accordion in action inside your Pattern Lab installation. With this example, we’ve introduced not only the basics of component-based theming, but we’ve also seen an example of inheriting templates using the Twig include function. Using this example as well as the other pre-built components in Emulsify, we have what we need to start prototyping!

In the next article, we’ll dive into how to implement Emulsify as a Drupal 8 theme and start building a component-based Drupal 8 project. You can also view a recording of a webinar we made in March. Until then, see you next week!

Recommended Posts

  • Webinar presented by Brian Lewis and Evan Willhite 15-March-2017, 1pm-2pm CDT Modern web applications are not built of pages, but are better thought of as a collection of components, assembled…
  • Welcome to the final post of our frontend miniseries on style guides! In this installment, the Web Chefs talk through how we chose Pattern Lab over KSS Node for Four…
  • Shared Principles There is no question that the frontend space has exploded in the past decade, having gone from the seemingly novice aspect of web development to a first-class specialization.…
Evan Willhite
Evan Willhite

Evan Willhite is a frontend engineer at Four Kitchens who thrives on creating delightful digital experiences for users, clients, and fellow engineers. He enjoys running, hot chicken, playing music, and being a homebody with his family.

Development

Blog posts about backend engineering, frontend code work, programming tricks and tips, systems architecture, apps, APIs, microservices, and the technical side of Four Kitchens.

Read more Development
May 24 2017
May 24

WordPress and Drupal

President of Mobomo, Ken Fang, recently sat down with Clutch for a Q and A about all things WordPress and Drupal.

What should people consider when choosing a CMS or a website platform?

They should probably consider ease of use. We like open-source because of the pricing, and pricing is another thing they should take into account. Finally, for us, a lot of it revolves around how popular that particular type of technology is. Being able to find developers or even content editors that are used to that technology or CMS is important.

Could you speak about what differentiates Drupal and WordPress from each other?

Both of them are open-source platforms, and they’re probably the most popular CMS’s out there. WordPress is probably the most popular, with Drupal running a close second. Drupal is more popular in our federal space. I think the main difference is that WordPress started off more as a blogging platform, so it was typically for smaller sites. Whereas Drupal was considered to be more enterprise-grade, and therefore a lot of the larger commercial clients and larger federal clients would go with Drupal implementation.

They’ve obviously both grown a lot over the years. We’re now finding that both of the platforms are pretty comparable. WordPress has built a lot of enterprise functionality, and Drupal has built in a lot more ease of use. They’re getting closer and closer together. We still see that main segregation, with WordPress being for smaller sites, easier to use, and then Drupal for more enterprise-grade.

Could you describe the ideal client for each platform? What type of client would you recommend each platform for?

Definitely on the federal side, Drupal is a much more popular platform. Federal and enterprise clients should move to the Drupal platform, especially if they have other systems they want to integrate with, or more complex workflow and capability. WordPress we see much more on the commercial side, smaller sites. The nice thing about WordPress is that it’s pretty quick to get up and running. It’s a lot easier for the end user because of its limited capability. If you want to get something up more cost-effectively, that’s pretty simple, WordPress is a good way to go.

Could you speak about the importance of technical coding knowledge when building a website on either platform, from a client’s perspective?

Most of these main CMS’s are actually built in PHP, and most of them have a technology stack that requires different skillsets. So, on the frontend side, both of them require theming. It’s not only knowing HTML, CSS, and JavaScript, but it’s also understanding how each of the content management systems incorporate that into a theme. You usually start off with a base theme, and then you customize it as each client wants. As such, you need either WordPress or Drupal themers to do that frontend work. For any backend development, you do need PHP developers. For Drupal, it’s called modules. There are open-source modules that people contribute that you can just use, you can customize them, or you can even build your own custom modules from scratch. For WordPress, they’re called plugins, but it’s a very similar process. You can incorporate a plugin, customize it, or write your own custom plugin.

In between all of this, because it is a content management framework and platform, there are site builders or site configurators. The nice part about that is that you can literally fire up a Drupal website and not have to know any PHP coding or whatever. If you’re just doing a plain vanilla website, you can get everything up and running through the administrative interface. A Drupal or WordPress site builder can basically do that, provided they are savvy with how the system actually works form an administration standpoint. So, those are the technical skills that we typically see, that clients would need to have. In many cases, we’ll build out a website and they’ll want to maintain it. They’ll need somebody in-house, at least a Drupal site builder or a themer, or something like that.

Do you have any terms or any codes that clients should be aware of or should know prior to trying to launch a project in Drupal or WordPress?

PHP is definitely the main language they should know, and then HTML, JavaScript, and CSS for the frontend stuff. Drupal 8 has some newer technologies. Twig is used for theming as an example, so there’s a set of technologies associated with Drupal 8 they need to know as well.

Is there a particular feature of WordPress or Drupal that impressed you and potential users should know about?

I’m going to lean a little more into the Drupal world because a lot of people are starting to move to Drupal 8, which was a big rewrite. There are now a lot of sites starting to use that in production. They did quite a bit of overhaul on it. It is more API-driven now. Everything you do in Drupal 8 can be published as a web service. You can even do a lot of what they call headless Drupal implementations. That means you can use some of the more sexy frameworks, like Angular or React, to build out more intricate frontends, and still use Drupal as a CMS, but really as a web service.

Are there any features of the two platforms that could be improved to make it a better CMS?

I think they’re pretty evolved CMS’s. On both of them, platforms are getting into place to build right on the CMS’s without having to install them. Platforms like Acquia, WordPress.com, Automaticc. These platforms are profitable because from an enterprise standpoint right now, it is hard doing multisite implementations at that scale, managing all of the architecture, and stuff like that. From a technical standpoint, if you get into an enterprise, clients who says they want to be able to run a thousand sites on a single platform, that becomes difficult to do from a technical perspective. They both have the ability to support multisite implementations, but advancements in there to make those types of implementations easier to use and deploy would be a significant advancement for both platforms.

What should companies and clients expect in terms of cost for setting up a website, maintaining it, and adding new features?

For a very basic site, where you’re just taking things off the shelf – implementing the site with a theme that’s already built, and using basic content – I would say a customer can get up and running anywhere from two to six weeks, $20,000-30,000. Typically, those implementations are for very small sites. We’ve seen implementations that have run into the millions, that are pretty complex. These are sites that receive millions of hits a day; they have award-winning user experience and design, custom theming, integration with a lot of backend systems, etc. Those can take anywhere from six to twelve months, and $500,000 to $1 million to get up and running.

Can you give some insight into SEO and security when building a website?

The nice thing about Drupal and WordPress is that there are a lot of modules and plugins that will manage that, from Google Analytics to HubSpot, all sort of SEO engines. You can pretty much plug and play those things. It doesn’t replace the need for your traditional content marketing, analyzing those results and then making sure your pages have the appropriate content and keywords driving traffic into them, or whatever funnel you want. All your analytic tools usually have some sort of module or plugin, whether it’s Google, Salesforce, Pardot, or whatever. A lot of those things are already pretty baked in. You can easily get it up and running. That’s the nice thing about the SEO portion of it.

The other nice thing about it being open-source is that there are constant updates on sort of security. Using these CMS systems, because they tie to all the open-source projects, if you download a module, anytime there’s a security update for it, you’ll get alerted within your administrative interface. It’s usually just a one-click installation to install that upgrade for security patches. That’s nice, as you’re literally talking hundreds of thousands of modules and millions of users. They’re usually found and patched pretty quickly. As long as you stay on that security patching cycle, you should be okay. You could still do stupid stuff as an administrator. You could leave the default password, and somebody could get in, so you still have to manage those things. From a software perspective, as long as you’re using highly-active, contributed modules and the core, security patches and findings come out pretty regularly on those things.

As a company, because we do stuff with some regulated industries like banking and federal agencies, we usually have to go a level above on security. Take a WordPress site or whatever, we would actually remove that form the public so it couldn’t be hit from outside of a VPN or internal network, and then have it publish out actual content and static pages so the outside just doesn’t even connect to the back-end system. That does take some custom programming and specialty to do. Most people just implement your regular website with the appropriate security controls, and it’s not a big issue.

Are there any additional aspects of building a website or dealing with a CMS that you’d like to mention? Or any other CMS platforms you’d like to give some insight on?

For us, because we are such a big mobile player, we typically would say that, whatever you build, your CMS, obviously focus on user experience. Most people are doing a good job of that these days. One of the areas that is still a little weak is this whole idea of a content syndication. There’s still a big push where the content editors build webpages, and they want to control the layout, pages, etc. They get measured by the number of visitors to the website and all that stuff. I’m not saying that’s not important; however, we’re trying to push an idea of a web service content syndication. So, how you use these CMS’s to do that, so your content gets syndicated worldwide. It doesn’t necessarily have to be measured by how many people hit your website. It should be measured by the number of impressions.

For instance, with the work we’ve done at NASA, they announced the TRAPPIST-1 discovery of potential Earth-like planets. That drove a huge amount of traffic to the website, probably close to nine million hits that day. If you look at the actual reach of that content and NASA’s message – through the CMS’s integration with social media, with API’s that other websites were taking, with Flickr, that sort of thing – it hit over 2.5 billion social media posts. That’s an important thing to measure. How are you using your content management system more as a content syndication platform, opposed to just building webpages? USGS has also done a really solid job of this ‘create once, publish everywhere’ philosophy. I think people should be looking at content management systems as content management systems, not as website management systems.

We ask that you rate Drupal and WordPress on a scale of 1 – 5, with 5 being the best score.

How would you rate them for their functionalities and available features?

Drupal – 5 – We have a bias towards Drupal because it’s more enterprise-grade. It fits what a lot of our clients need. I think they’ve come a long way with both the 7 and 8 versions and have really brought down the cost of implementation and improved the ease of use.

WordPress – 4 – I think it’s fantastic. It’s obviously extremely popular and very easy to set up and use. I give it a 4 and not a 5 because it’s not as easy to extend to enterprise-grade implementations. For some functionalities, you still have to dig into core, and nobody wants to be modifying core modules.

How would you rate them for ease of use and ease of implementation?

Drupal – 4.5 for ease of use, because it’s not as easy as WordPress, and 4.5 for ease of installation.WordPress – 5 for ease of use, and 4 for ease of implementation. If you want to go out of the box, it’s a little more difficult. Configuring multisite is a real difficulty in WordPress.

How would you rate them for support, as in the response of their team and the helpfulness of available online resources?

Drupal – 4

WordPress – 4

Being open-source projects, there are a ton of people contributing. They’re very active, so you usually can get your answers. In many cases, to get something embedded into core, it does have to get reviewed by the organization, which is a bunch of volunteers for the most part. Because of that, it does take a while for things to get embedded.

How likely are you to recommend each platform for a client?

Drupal – 5

WordPress – 5

I think they’re the strongest CMS’s out there for the price.

How likely are you to recommend each platform for a user to build their own DIY website?

Drupal – 3

WordPress – 4  

If you’re going to build your own website, and you have zero technical skills, you might want to look into a Weebly, Wix, or something like that. There is a need to know how to do site-building if you use Drupal or WordPress. Somebody has to configure it and understand it.

How would you rate your overall satisfaction collaborating with each platform?

Drupal – 5

WordPress – 5

We implement on both of them regularly, and they’re really great. They solve the need for a lot of our clients to migrate from much more expensive legacy systems.

Clutch.co interview: https://clutch.co/website-builders/expert-interview/interview-mobomo-dru...

May 24 2017
May 24
Drupal logo used in Ashday Blog

In Drupal 7, site deployments could be rather difficult on ambitious sites. Some database level elements were worth programming out in hook_updates (turning on modules, reverting views, etc) and some usually weren't (block placement, contrib module configuration). I remember days where a deployment involved following a three page long Google doc of clicks that had to be carefully replicated. Ugh.

A New Hope

So if you've taken the dive into Drupal 8, you'll quickly discover one of it's most prominent features - Configuration Management. Drupal 8's ability to manage configuration with yml files is absolutely amazing! It's nearly akin to watching Star Wars and thinking "Hey, I can do anything with a lightsaber! Fight bad guys, cut holes in doors, remove my hand cuffs. Sweet!"

The Empire Strikes Back

Here's the rub. Managing Drupal 8 configuration in complex real world apps is akin to building a real world laser sword after watching Star Wars only to promptly burn your face off and lose two limbs as soon as you try to fight with it. "Ambitious digital experiences" essentially equates to "arduous development concerns" and even config management can't save the day simply by existing. You must use it for good. You must unlearn what you have learned. I blogged a bit on this shortly after Drupal 8 released, but oh how much I learned since then!

We've been doing Drupal 8 pretty heavy for about a year and a half here at Ashday and had both the fortune and misfortune of needing to manage a more complex set up which quickly revealed our deficiencies in understanding how to properly manage config.

Here's the scenario: A client needs a site that will become the model for many sites, but they don't want them to be a single site with multiple domains and they also don't want it to be costly or complicated to keep them mostly similar from a functional perspective. Given that our preferred hosting solution is Pantheon, this quickly turned into an obvious Upstream project. And that means figuring out a new way to manage D8 config other than just import/export of the whole site.

If you aren't familiar, a Pantheon Upstream works nearly identical to their core updates - you have a remote repository that, upon code getting pushed to it, notifies you through the dashboard of your updates where you can apply them in the same way you do Pantheon core updates. It's pretty slick because it provides an easy way to have a big shared chunk of code and apply updates to many sites with a few clicks (well, except when nearly every update is major and requires hands-on management - but I'm not bitter).

The Phantom Menace

Our first try at this was to give the Features module a go, but at the time the interface was just too buggy to give us enough confidence to rely on it, it auto-selected what we didn't want and didn't select what we did, and it didn't support some key things we needed like permissions. As a result we decided to home brew our own solution. We knew these sites were going to have a lot of config in common, and a lot of config unique, and we needed to deploy to many of them all in different states without tragedy striking. So to accomplish this, we concocted the following procedure that we would run at deployment time, all from a single drush command.

  • Export the current site's live config (using drush) to the config sync folder
  • Copy all config files (with uuids removed) in our cross-site custom module over top of the config sync directory
  • Copy all config files (with uuids removed) in our site-specific custom module over top of the config sync directory
  • Import all config.

What this allowed us was the ability to allow each upstream site to stray a bit as they needed to, but we could be assured the config we cared about was prioritized in the proper stacking order. The approach ultimately wasn't that different than the goal of Features, but we were in control of the process, it was all live and it was relatively quick. And you know what? It worked! For a while...

And then it didn't. You see, the method we used caused Drupal to see every config file we were tracking (upwards of 300) as changed simply because of the missing uuid. So if only 8 config files changed in a deployment, Drupal was attempting to import hundreds of config files every time. This meant that it started to slow significantly over time as the site grew in complexity and eventually, we started having timeout issues and long deployments. We also started to run into issues when there was a significant core update (ie: 8.3) because so much config was being imported unnecessarily that wasn't compatible in that moment with the new code because db updates hadn't run yet. Not good. It was time for something else.

Return of the Jedi

The Jedi in question here is again the Features module. Or maybe it's Mike Potter. At least it's not me anyways. At DrupalCon Baltimore, I was set on speaking with Mike about how we were handling config because I simply knew there was a better way. If you don't know, Mike is one of the founders of the Features module and ran a great BOF on config management in Baltimore.

So I found this delightful man and laid out what we were doing and he reacted exactly as I had hoped. He didn't say that what we were doing was terribly wrong, but it made him visibly uneasy. After a chat, I discovered that Features had come a long way since we initially tried to use it and we should really give it another shot. He also explained some of the configuration of Features to help me better understand how to use it.

So we returned to Features now and are much happier for it. The thing is though that I don't think I would have really known how to manage it if we hadn't taken the deep dive into config and figured out how it needed to work. It all helped us a lot to decide how to incorporate Features properly for this particular situation so that I actually feel good about relying on it again. And that's how most good Drupal development goes. You really should know how something works before simply relying on a contrib module or someone else's code to take care of everything because otherwise you won't really know how to deal with problems - heck, you might not even know you have a problem! I personally don't prefer spending weeks writing code and then depending at a critical moment on a mysterious piece to make it all successfully roll out to production.

So as it all played out, we now understand what Drupal puts in config, what we care about and don't, what belongs in the upstream vs our site-specific modules vs no where, etc. Here is our current process after this 6 month long journey.

  • Revert the global base feature
  • If needed, revert the site specific feature
  • Run our previous script outlined above, but now on only the 5 or 6 role config files so we handle the permissions in the same fashion

So there you have it! For how long-winded this turned out to be, I'm glossing over a lot of details that are pretty critical to understanding Drupal 8 configuration (ex: blocks are a mix of config and content), but I recommend you do the same thing we did and really get your hands dirty and understand what's going on so that you don't get bit at rollout. After all of this, we feel even moreso that Configuration Management is an astoundingly useful component of Drupal 8 and now we find ourselves a bit sad when we update our Drupal 7 sites (a version we absolutely loved!) where we don't have this amazing tool. 

So good luck and don't hesitate to drop us a note if you have any questions or thoughts on this stuff. I'll probably change my mind on all of it anyways tomorrow. That's why this job is awesome.

P.S. I apologize that I didn't find room to incorporate Attack of the Clones, Revenge of the Sith, The Force Awakens or Rogue One, but the reality is that I just didn't have time to modify our whole approach to configuration in order to make this blog post more cohesive.

Offer for a free consultation with an Ashday expert

May 17 2017
May 17
May 17th, 2017

Shared Principles

There is no question that the frontend space has exploded in the past decade, having gone from the seemingly novice aspect of web development to a first-class specialization. At the smaller agency level, being a frontend engineer typically involves a balancing act between a general knowledge of web development and keeping up with frontend best practices. This makes it all the more important for agency frontend teams to take a step back and determine some shared principles. We at Four Kitchens did this through late last summer and into fall, and here’s what we came up with. A system working from shared principles must be:

1. Backend Agnostic

Even within Four Kitchens, we build websites and applications using a variety of backend languages and database structures, and this is only a microcosm of the massive diversity in modern web development. Our frontend team strives to choose and build tools that are portable between backend systems. Not only is this a smart goal internally but it’s also an important deliverable for our clients as well.

2. Modular

It seems to me the frontend community has spent the past few years trying to find ways to incorporate best practices that have a rich history in backend programming languages. We’ve realized we, too, need to be able to build code structures that can scale without brittleness or bloat. For this reason, the Four Kitchens frontend team has rallied around component-based theming and approaches like BEM syntax. Put simply, we want the UI pieces we build to be as portable as the structure itself: flexible, removable, DRY.

3. Easy to Learn

Because we are aiming to build tools that aren’t married to backend systems and are modular, this in turn should make them much more approachable. We want to build tools that help a frontend engineer who works in any language to quickly build logically organized component-based prototypes quickly and with little ramp-up.

4. Open Source

Four Kitchens has been devoted to the culture of open-source software from the beginning, and we as a frontend team want to continue that commitment by leveraging and building tools that do the same.

Introducing Emulsify

Knowing all this, we are proud to introduce Emulsify—a Pattern Lab prototyping tool and Drupal 8 starterkit theme. Wait… Drupal 8 starterkit you say? What happened to backend agnostic? Well, we still build a lot in Drupal, and the overhead of it being a starterkit theme is tiny and unintrusive to the prototyping process. More on this in the next post.
[NB: Check back next week for our next Emulsify post!]

With these shared values, we knew we had enough of a foundation to build a tool that would both hold us accountable to these values and help instill them as we grow and onboard new developers. We also are excited about the flexibility that this opens up in our process by having a prototyping tool that allows any frontend engineer with knowledge in any backend system (or none) to focus on building a great UI for a project.

Next in the series, we’ll go through the basics of Emulsify and explain its out-of-the-box strengths that will get you prototyping in Pattern Lab and/or creating a Drupal 8 theme quickly.

Recommended Posts

Evan Willhite
Evan Willhite

Evan Willhite is a frontend engineer at Four Kitchens who thrives on creating delightful digital experiences for users, clients, and fellow engineers. He enjoys running, hot chicken, playing music, and being a homebody with his family.

Development

Blog posts about backend engineering, frontend code work, programming tricks and tips, systems architecture, apps, APIs, microservices, and the technical side of Four Kitchens.

Read more Development
May 17 2017
May 17

Introduction

10 years ago (at the end of 2006), Drush appeared to make it easy for Drupal developers to do some common tasks, it wasn’t immediately popular as it was a Command-LIne tool and a lot of people didn’t appreciate the idea, but year after year it’s popularity grew as did its functionality.

By the time Drupal 7 came out it was unimaginable for most developers to build a site without Drush because of the incredible boost they got in their development process and the option of generating a site from a makefile and avoiding the need to add in their project contrib code or risk of people hacking the behaviour of this module and unfortunately this was a common practice.

But Drush with the makefile wasn’t good enough, if they update some module the makefile won’t show any update, sometimes some releases breaks other modules and it wasn’t a clear way to say “I need up to this version of that module”, indeed the whole update process itself wasn’t very automated at all, and it was only useful for download a specific version of a module and with his dependencies (it gets the latest version of them without check if they are compatible)

In parallel, 5 years ago the PHP community started development of “Composer“, a tool for dependency management of  open source PHP libraries. It quickly became very popular, and big frameworks like Symfony adopted Composer the same year it was released (Symfony 2.1.0 was the first version to use it, but nowadays all PHP frameworks use it as default install method). This tool addressed all the dependency issues that Drush had and also implemented Autoloading.

When Drupal 8 come out in 2015 it tried to use Composer in a very non-intrusive way, as a result Composer was hardly worth using. It was very slow, every new version override the composers files, and had a few other disadvantages but at least we got autoloading (finally!!). However things changed once the GitHub project “drupal-composer/drupal-project” appeared, suddenly it was possible to create a Drupal site and add/remove/update modules very easy without the help of any external tools other than Composer.

How Composer works

Composer uses a JSON file where you define the setup of your project and a lock file that contains the exact versions of installed packages when you last run composer update, or when this lock file is generated. This file is especially useful to make sure deployed versions of dependencies are the same on every build and deployment. This file is commonly committed into version control.

When you execute ‘composer install’ it first reads the lock file and installs everything from there, if this file doesn’t exist it reads the JSON file and  installs the latest version that is specified there and then generates the lock file.

Executing ‘composer update’ ignores the lock file in order to get the latest version of what you specified in the JSON file so it’s a very similar process to when you execute the install without the lock file.

Composer does more things than just download packages, it also provides an autoload for all the namespaces, and you can also define custom namespaces for your project, for example a namespace for your tests.

You have also the option to add hooks (pre, post, etc..) on the scripts commands to further automate your application build process.

Configuring Composer

Repositories

This is probably one of the most important to understand for Drupal developers.

Composer by default uses packagist as a unique source of packages and Drupal is available under “packages.drupal.org/8”. If we have some custom module, profile or theme in another place like GitHub, Bitbucket, your own packagist account or any other private repository we are going to have to add them as a custom repository.

Composer lets you define settings for repositories, such as the type of a repository For more information read this documentation.

Dev mode

In the JSON file you can define libraries and namespaces that are going to be used only for development, in this way you can deploy minimal code to Production while in your development environments you can have that plus some test frameworks and debugging tools while defining everything in the same file.

By default Composer uses the dev mode but you can add –no-dev to install only your Production libraries.

Scripts

You can create custom commands, which are very useful for things like  providing an alias for very common commands, or running multiple commands with a single command, or aggregating commands before or after execute. You can find more documentation here.

Config option

In config you can add tokens for a private repo, or change the default behavior of Composer, most of them are really useful to avoid having to add parameters when you execute any Composer command. The official documentation of this section can be found here.

Conclusion

Composer is not a replacement for Drush or Drupal Console, but Composer has so many other great benefits that it’s worth persevering with it and adopting it more widely across the Drupal and PHP communities as the base of development workflows.

It has a very intuitive standard and you can build an amazing easy deployment process, which is easy to understand for other developers and use it in CI/CD tools.

So don’t be afraid of Composer, start using it today and find out for yourself how useful it can be!

Also posted in here

Share this:

Like this:

Like Loading...
May 12 2017
May 12

Two weeks ago, I presented our story of rebuilding rednoseday.com on Drupal 8 at DrupalCon Baltimore, Drupal’s largest gathering with an attendance of over 3000!

I talked about our journey of building a product to power all our editorial websites at Comic Relief (see my previous blog post), and focused on three topics: editor experience, automation & streamlining, and using decoupled services.

So far, our product ecosystem proudly powers www.rednoseday.com, and the upcoming Red Nose Day USA Campaign, and we are currently working hard to bring www.comicrelief.com on board as well!

Check out the video of my presentation (audio+slides),

[embedded content]

or the slides only.

I’d be happy to hear your thoughts, questions and feedback below.

Featured image by Jeff Geerling

Share this:

Like this:

Like Loading...
May 10 2017
May 10
May 9th, 2017

DrupalCon is many things to many people. For me, this year’s North America DrupalCon in Baltimore was a chance to connect with my remote co-workers in the same place, help share knowledge while learning things myself, and celebrate all the things that Drupal makes possible.

The Drupal 8 with React.js and Waterwheel Training

Our first big event was “API First Drupal 8 with React.js and Waterwheel Training”, where Web Chef Luke Herrington took a canonical JavaScript application—a todo list built with React—and hooked it up to Drupal 8 through a new JavaScript library called Waterwheel.js. Todos were stored in a headless Drupal site via the JSON API module, and we even provided a login page and a `like` button for todos. Although we had a small army of Web Chefs available to help, Luke had created such a great training that our extra support wasn’t needed, and the attendees were really able to dive deep into how everything worked.

Future of the CMS: Decoupled

“I’ve completely rewritten my talk,” said Todd, the Four Kitchens CEO, at the team dinner on Monday night. I’ve seen him give this talk before but this declaration really piqued my curiosity.

There were a lot of talks at DrupalCon about the “how” of decoupling, but Todd’s revised talk is a great summary of the “why”. In it, Todd talks about the differences between CMSes being “content management systems” versus “website management systems” and about how that content can be managed so that it is reuseable on all categories of devices. Because the technology is always changing, it’s a talk he rewrites at least once a year, and I’m glad I got to see this version of the 2017 talk when I did.

Supercharge Your Next Web App with Electron

To show off his work in Electron, Web Chef James Todd brought two drawing robots to DrupalCon that he set up in our booth. Each machine was powered by RoboPaint, a packaged-up web app. I’ve been curious about Electron for a while, and when I learned that James was giving a talk on the subject I immediately reached out to help him build his slide deck so that I could learn more. His presentation was thorough and entertaining, and he encouraged people to “experiment and play with it, it’ll be fun”.

Drinks with a Mission

The Drupal community believes that open source technology has the power to improve the lives of others, so instead of the usual DrupalCon party, this year, Four Kitchens teamed up with Kalamuna and Manatí to host “Drinks with a Mission”.

We started the night by asking, “If you had a magic wand that would fix a problem, what problems would you fix?” Answers were written down on post-it notes, which were then sorted into groupings, and finally assigned to teams. Each team took their topic, such as How to Better Connect with Nature, and had to come up with solutions to the topic problem. Great ideas can begin in unexpected places, and the ensuing solutions were as thoughtful as they were hilarious.

Watch the recorded stream of the event: Part 1, Part 2

Taking the Train Home

In the last few years I’ve started to become enamored with the concept of “taking the train”. So at the end of DrupalCon I got my wish, and instead of flying, I spent an entire day traveling by rail: from Baltimore, through Philadelphia’s gorgeous train station, and then on to home in the middle of Pennsylvania.

Recommended Posts

  • A mostly full report on what went down last week in the Big Easy, gonzo journalism -style.
  • Fun & Games DrupalCon Baltimore is next week and we’re so excited to get back together in Baltimore! As the official Drupal Games sponsors, we take fun very seriously and…
  • "API First" or, as some may call it, "Decoupled Drupal", remains a topic of much discussion among the Drupal community. Here are just a few sessions being presented at Drupalcon…
Randy Oest
Randy Oest

Randy Oest is an avid Star Trek fan, plays too many board games, and bought his mother an iPad so that he wouldn't have to fix her computer anymore.

May 04 2017
May 04

Busy as all hell to get this out before DrupalCon, so this will just be a lot of quick housekeeping and some announcements.

New Releases

Commerce Authorize.net

Updates to use the new API endpoints as required by authorize.net, recommended to update promptly as the old endpoints will be discontinued by June 1st 2016. This release also contains a number of bugfixes.

https://www.drupal.org/node/2715593

Commerce Features

Commerce Checkout Panes and Commerce Payment Rules are both exportable now.

https://www.drupal.org/node/2716159

Issue of the Week

Compatibility settings other than "all" causes Discount+Coupon to be removed

Could use some extra testing and feedback on this patch to finally get compatibility fixed up on Commerce Discounts.

https://www.drupal.org/node/2621526

DrupalCon Reminder

DrupalCon New Orleans next week! We’ve got a booth (#623 to be exact) right by the Commerce Guys and Platform.sh, in the Commerce Quarter.

We’re Going Sprinting!

So the Commerce Guys are coming up to Kelowna in July to do some hardcore sprinting on Drupal Commerce 2.x with us, check out our blog post on it.

Commerce 2.x

So gonna cheat a little and include some 2.x news, Alpha4 released late last week and it has checkout! Imagine that, ecommerce with checkout :) Seriously though, it is a big jump and worth checking out. We’re going to try to have a follow-up UX post in a week or two, but right now it’s mostly just framework and not all sexed up yet. Big shoutout to Matt and Bojan at Commerce Guys who have been rocking a ton of Commerce 2.x work as of late.

May 04 2017
May 04

A candid talk from our CTO Shawn McCabe and Senior Developer Josh Miller on how they have identified their own personality traits and what they do to create new habits that help them overcome their communications weaknesses.

Josh Miller, a long time contributor to the Drupal community, will document what the large distributed team he works with does to identify team member’s personalities and smokescreens using various tools. Some of the tools that will be discussed:

  • The DISC* spectrum (Dominance, Influence, Support, and Caution) A way of defining dominant personality traits and suggestions on how an I (Josh) and a D (Shawn) can communicate effectively.
  • BAT** (Behaviour, Attitude, Technique) method for determining how you view and how your peers view your modus operandi.
  • EDP*** (Employee Development Plan) method for annual reviews of progress.

Shawn McCabe, the CTO of the same distributed team, will share how recognizing his own personality flaws have led to huge gains in communication and overall success in his position of leading multiple teams of developers. As someone who struggles with empathy and understanding others, he will discuss how he daily recognizes his weak points and deliberately does certain things even though they feel silly or like a waste of time, but ultimately bridges the gap between his personality and the successful outcomes we all want.

At the end of the session, we hope that sharing this positive case study and the tools we use will spread the idea that we can identify ourselves without judgement and learn a few new tricks to help ourselves and those around us cope.

May 04 2017
May 04

Our very own Shawn McCabe hosted a great session in regards to Drupal Commerce Performance at last week’s DrupalCon Baltimore.

We run some high volume Drupal Commerce sites, taking upwards of 10,000 orders an hour and have done a lot of performance work to get these values. Commerce traditionally can't be cached and tuned the same way an unauthenticated content site can, and normal tricks like varnish and other static caching are of limited use.

Shawn’s session provides an overview of performance problems with Drupal Commerce and Drupal 7 and ways to fix or mitigate them.

  • Caching with authenticated users
  • Caching with dynamic content
  • Scaling Orders
  • Database Locking
  • Order Locking
  • PHP 7 benefits specific to Commerce
  • Scaling Attributes and Options

He will also touch on changes coming with Commerce 2.x for Drupal 8 and how this will affect performance in Drupal, especially related to cache contexts and bigpipe.

After this talk, Commerce site admins should be able to increase their order throughput hopefully by a factor or 3x or more, depending what their existing site setup is.

May 02 2017
May 02
3 May 2017

This is the second part of a series on improving the way date ranges are presented in Drupal, by creating a field formatter that can omit the day, month or year where appropriate, displaying the date ranges in a nicer, more compact form, e.g.:

  • 24–25 January 2017
  • 29 January–3 February 2017
  • 9:00am–4:30pm, 1 April 2017

The first post dealt with porting some existing code from Drupal 7 to Drupal 8, adding an automated test along the way.

In this post, we’ll do some of the work to make the the format customisable:

  • a new config entity to store formats
  • moving the rendering logic into a service

This is a long post, and we’ll cover a lot of ground. You may want to make a cup of tea before we start.

Configurable formats

Drupal’s core date formats are stored as configuration entities. Each one consists of a single pattern made up of PHP’s date & time formatting symbols. We’ll create another type of configuration entity for date range formats. Each format will need several different patterns:

  • a default date pattern. This is used to show the full single date if the start and end dates are the same. It’s also used to show both dates fully where the range spans several years.
  • patterns for the start and end dates, where a range spans several days within the same month
  • patterns for the start and end dates, where a range spans several months within the same year

We can also support times in a similar fashion:

  • a default pattern. This is used to show the full single date & time if the start and end values are the same. It’s also used to show both dates & times fully if the range spans several days.
  • optional patterns for the start and end values, where we have a range contained within a single day

All of these 8 patterns can be stored within a single configuration entity, alongside custom separator text.

Defining a configuration entity

To create the configuration entity, we need some custom code. We can use Drupal console to generate a certain amount of boilerplate code as a starting point. Be aware that Drupal console will produce a lot of code all in one go, including the admin interface which we’ll look at in part 3.

I find it helpful to generate this boilerplate into a temporary location, then copy the files over one at a time, editing them as I go. It slows things down in a good way, and forces me to understand the code I’m going to be responsible for maintaining.

Entity class and definition

Let’s start with the entity class, which lives in src/Entity/DateRangeFormat.php:

<?php
namespace Drupal\daterange_compact\Entity;

class DateRangeFormat extends ConfigEntityBase implements DateRangeFormatInterface {
  /* implementation */
}

In order to tell Drupal that this is a configuration entity, we need to add an annotation to the class. Similar to the annotation on the field formatter in part 1, we’re telling Drupal about the existence of a new date range format configuration entity, plus some information about it.

/**
 * @ConfigEntityType(
 *   id = "date_range_format",
 *   label = @Translation("Date range format"),
 *   config_prefix = "date_range_format",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label",
 *     "uuid" = "uuid"
 *   }
 * )
 */
class DateRangeFormat...

The class adheres to a corresponding DateRangeFormatInterface. We’ll refer to the interface, rather than the entity class directly, elsewhere in the code.

The complete implementation is here.

Schema

End users will create instances of the configuration entity—one per format. These can be represented as a single YAML file, and imported and exported as such. The schema describes the structure of these YAML files, dictating the type of data we’re storing inside the entity.

The schema definition is itself a YAML file, and lives in config/schema/daterange_compact.schema.yml:

daterange_compact.date_range_format.*:
  type: config_entity
  label: 'Date range format config'
  mapping:
    # properties of the config entity

The complete schema implementation is here.

Updating entity definitions

If we look at the status report of our site, we’ll see that there is an error: Mismatched entity and/or field definitions. Each time we add or change code that defines entities, we need to update Drupal’s internal copy of the definitions. We can do this with the drush entup command, and we should get the following output:

The following updates are pending:
date_range_format entity type :
  The Date range format entity type needs to be installed.

Do you wish to run all pending updates? (y/n): y
 [success] Cache rebuild complete.
 [success] Finished performing updates.

That’s the bare minimum for defining a config entity. Right now the only way to manage them is by editing YAML files by hand, but that’s enough to start working on the improved functionality for now. In the next post we’ll look at an administrative interface for editing these formats.

Providing a default entity

Any Drupal module can provide configuration entities as part of their install process. The contact module is a good example—enabling the module will create the feedback and personal contact forms, each configuration entities. You can then change those forms, remove them or add new ones.

Let’s make our module provide a date range format called Medium, following the same naming convention as Drupal’s standard date formats. We do that by providing a file called {$modulename}.{$config_entity_type}.{$machine_name}.yml in the config/install directory.

So the module will contain a config/install/daterange_compact.date_range_format.medium.yml file that looks like the following. You’ll see the properties follow the schema defined earlier. The patterns are made up of PHP date & time formatting symbols.

langcode: en
status: true
dependencies: {  }
id: medium
label: Medium
date_settings:
  default_pattern: 'j F Y'
  separator: ' - '
  same_month_start_pattern: j
  same_month_end_pattern: 'j F Y'
  same_year_start_pattern: 'j F'
  same_year_end_pattern: 'j F Y'
datetime_settings:
  default_pattern: 'j F Y H:i'
  separator: ' - '
  same_day_start_pattern: 'j F Y H:i'
  same_day_end_pattern: 'H:i'

We’ll need to re-install the module for this to take effect, but if we do, and then export the site configuration, we should get a copy of this YAML file along with the rest of the configuration.

A note on cacheability

Normally, whenever we produce some sort of output in Drupal 8, we need to provide its cacheability metdata, which describes how it may be cached. Whenever something changes, the render cache can be examined and anything that depended on it can be cleared.

Certain items, like date formats, are so widely used they a treated differently. From drupal.org:

The DateFormat config entity type entity type affects rendered content all over the place: it’s used pretty much everywhere. It seems appropriate in this case to not set cache tags that bubble up, but to just clear the entire render cache. Especially because it hardly ever changes: it’s a set-and-forget thing.

Invalidating the rendered cache tag should be done sparingly. It’s a very expensive thing to do, clearing the entire render cache. But it’s appropriate here to follow what the core date format entity does.

We need to add this to the @ConfigEntity annotation:

list_cache_tags = { "rendered" }

and this to the DateRangeFormat class:

public function getCacheTagsToInvalidate() {
  return ['rendered'];
}

Refactoring

Until now, the code for rendering date ranges has been part of the field formatter. As it’s getting more complicated, it makes sense to move it out of there into it’s own, distinct location. That means we’ll be able to use it outside of the context of a field.

We’ll use a Drupal service for this. A service is a separate class in which we can handle the business logic independently of field formatters. When Drupal manages a request, it will take care of creating an instance of the service class and making it available to other parts of the system.

Our service class it quite straightforward:

<?php
namespace Drupal\daterange_compact;

class DateRangeFormatter implements DateRangeFormatterInterface {

  function __construct(…) {
    /* implementation */
  }

  function formatDateRange($start_timestamp, $end_timestamp, $type = 'medium', …) {
    /* implementation */
  }

  function formatDateTimeRange($start_timestamp, $end_timestamp, $type = 'medium', …) {
  /* implementation */
  }

}

The complete implementation of DateRangeFormatter is here.

We then tell Drupal about the service by including it in the module’s daterange_compact.services.yml file. We can also specify dependencies on other services, and these will be passed to our object’s constructor.

services:
  daterange_compact.date_range.formatter:
    class: Drupal\daterange_compact\DateRangeFormatter
    arguments: ['@entity_type.manager', '@date.formatter']

Now our formatting functions are available to use within other parts of Drupal via the daterange_compact.date_range.formatter service. We’ll access it from the field formatter next.

You can find more documentation about Drupal 8 services on drupal.org.

Pulling it all together

We need to revisit the field formatter and make a couple of changes. First we remove the hardcoded formatting logic that was there previously, and instead delegate that work to the service. Second, we need a way to let the site builder choose a particular format.

Dependency injection

The field formatter needs to be able access to the new service. Drupal 8 makes use of dependency injection for this sort of thing—a way to access dependencies at runtime without being tied to any particular implementation.

There are a few steps involved in getting to use our service this way:

First, we want it for the lifetime of the field formatter, so it needs to be in the constructor. The constructor for FieldFormatterBase takes quite a lot of parameters. We need to accept them all as well, plus our formatter. Some of the other parameters are hidden with here for clarity.

function __construct(, DateRangeFormatterInterface $date_range_formatter) {
  parent::__construct();

  $this->dateRangeFormatter = $date_range_formatter;
}

Next, we need to state that this formatter makes use of dependency injection. We do that by making the class implement ContainerFactoryPluginInterface, which declares a create function that should be used to create instances. The create function is passed the container, an object from which we can get services by name. In the create function we get the service by name and pass it to the constructor:

static function create(ContainerInterface $container, ) {
  return new static(,
    $container->get('daterange_compact.date_range.formatter')
);

Now we will always have access to a formatter via the $this-dateRangeFormatter variable.

Field formatter settings

Whenever we use this formatter, we can choose a particular date range format. We store that choice in the field formatter settings, once for each time a field is displayed.

Adding field formatter settings is documented quite thoroughly on drupal.org, but it involves a YAML file describing the type of data we want to store, some default settings, a form and a summary.

The YAML file is named field.formatter.settings.{$formatter_name}:

field.formatter.settings.daterange_compact:
  type: mapping
  label: 'Date/time range compact display format settings'
  mapping:
    format_type:
      type: string
      label: 'Date/time range format'

The extra functions we need to implement in the DateRangeCompactFormatter class are as follows:

public static function defaultSettings() {
  /* an array of default values for the settings */
}

public function settingsForm(array $form, FormStateInterface $form_state) {
  /* form from which to choose from a list of formats */
}

public function settingsSummary() {
  /* text describing what format will be used */
}

Now when anyone opts to use the compact formatter to render a date range field, they will be prompted to choose a date range format.

Display

Finally, we have everything we need to render the date range using the chosen format. We can change the viewElements function, removing the hardcoded stuff we had before, and delegating to our date range formatter service:

$format = $settings['format_type'];
$formatter = $this->dateRangeFormatter;
$output = $formatter->formatDate($start_timestamp, $end_timestamp, $format, );

The complete implementation of the formatter is here

Test it!

We’ve added substantial functionality, so we need to make sure there have been no regressions on what we had before. We also want to test the new configurable formats.

The test should pass as before, with one small tweak. In the setUp function, we need to load the configuration for the daterange_compact module, so that the medium format is present.

We’ll also define a new usa format, for US-style month, day, year display. That is created in the setUp function:

protected function setUp() {
  parent::setUp();
  /* existing set up code */
  /* create a new date range format called "usa" */
}

We’ll add another test specifically for rendering the USA format:

function testUSAFormats() {
  $all_data = [
  ['start' => '2017-01-01', 'end' => '2017-01-01', 'expected' => 'Jan 1, 2017'],
  ['start' => '2017-01-02', 'end' => '2017-01-03', 'expected' => 'Jan 2–3, 2017'],
  ['start' => '2017-01-04', 'end' => '2017-02-05', 'expected' => 'Jan 4–Feb 5, 2017'],
  ['start' => '2017-01-06', 'end' => '2018-02-07', 'expected' => 'Jan 6, 2017–Feb 7, 2018'],
];

  foreach ($all_data as $data) {
    /* 1. programmatically create an entity and populate start/end dates */
    /* 2. programmatically render the entity */
    /* 3. assert that the output contains the expected text */
  }
}

Whilst the goal should be to have as much test coverage as possible, is isn’t feasible to cover every combination of dates, formats and settings. But we should try to test lots of variations of date and datetime ranges, edge cases, possible formats and results that would vary by timezone. And if something doesn’t behave as expected later, we can write a test to demonstrate it before changing code. Later we can verify any fixes via the new test, and make sure there are no other regressions too!

You can find the full test implementation here.

Phew, that was a lot!

In the final post, we’ll provide an admin interface for editing the date range formats and look at some implications of using this in a multilingual environment.

Apr 28 2017
Apr 28

Don’t panic! If you don’t use Content Moderation- and Layout Plugin-based components (like Display Suite, Panels, Panelizer and Contexts) then you’ll be fine upgrading to Drupal 8.3. If you do, there are just a few things you need to know first.

“It looks insanely complicated, and this is one of the reasons why the snug plastic cover it fitted into has the words Don’t Panic printed on it in large friendly letters.” – The Hitchhiker’s Guide to the Galaxy

One of our Drupal 8 projects relies on the core experimental module Content Moderation. The module has been partially re-written with Drupal 8.3, becoming dependent on the new core experimental module Workflows, so we decided to put some effort into updating to this latest minor version and studying all the changes.

An initial upgrade path workflow

We’ve been aware of Content Moderation changes for a while, thanks to the change record “Experimental Workflows module added to core”, circulating since the beginning of the year. The section on “Important upgrade information” was as scary as it was useful: “you will need to uninstall it before updating the codebase and re-install after”. And of course, “as this is an ALPHA experimental module data upgrade paths are not supported”.

We decided to experiment with what the best upgrade path would have been for our scenario, and came up with a pretty strong workflow:

  • Put website in maintenance mode.
  • Approve/Reject all the content waiting for approval. The only allowed states are Published or Draft/Unpublished. This is because after uninstalling Content Moderation, Published and Unpublished are the only states Drupal will recognise.
  • Delete content_moderation_state entities (/admin/modules/uninstall/entity/content_moderation_state).
  • Uninstall Content Moderation and dependencies, if any.
  • Upgrade Drupal codebase to 8.3 latest version.
  • Run database updates.
  • Re-install Content Moderation and dependents, if any.
  • Re-create the moderation States and Transitions from the new Workflow based UI. If you use the same name machines, you probably don’t need to update any of your custom code or views.
  • Restore the website, disabling Maintenance Mode.

After doing this, you check some random pages… and you notice something is wrong.

What’s the problem?

Visiting one of your content types’ custom display modes, made with Display Suite, may show you some fields have been disabled (they’ve either been moved to the Disabled region, or they’re missing altogether)!

Or you may notice your custom Layout theme suggestions are not loaded for your Panels.
Shame on you. You haven’t read the other 8.3 Change Records, nor the full 8.3.0 release notes.

Layouts, layouts everywhere…

The Layout Plugin contrib modules, used by modules like Display Suite and Panels, has been dropped. Instead, we have the Layout subsystem and a new experimental module, Layout Discovery, in core.

The reason for this is to unify the way contrib and custom modules (and themes) can define their own layouts, and enable them to reuse layouts created with all the modules implementing the Layout API. Putting it in core is an incentive for doing it once and doing it well.

Both Display Suite and Panels suggest using brand new branches with Drupal 8.3 (as now they both make use of the new Layout subsystem). Though apparently, you could still use your original versions, with a bit of custom effort/code. It doesn’t work for me no matter what I do, as most of the fields are still missing on my display modes which are managed through a Display Suite layout.

Let’s update those modules

Display Suite needs to be updated to version 3.0 or higher, Panels and Page Manager to 4.0 or higher. Running database updates will uninstall Layout Plugin contrib and install the Layout Discovery core module replacement, enabling the new Layout subsystem.

The Display Suite issues queue is full of people experiencing problems at this point of the process. Missing Fields, Fields in the wrong regions or Disabled, Fields settings reverted to default values. The guys are doing a wonderful job of trying to fix the bugs, and explain the best solutions for a full recovery.

DS 3.0-beta3 contains most of these fixes, and restored most of my fields and settings.

You’ll still need to review ALL your form and view display modes, because here or there, there could still be glitches.

In my scenario, I have six content types and around four custom display modes for each, so it took me around an hour to re-configure all the fields in the right way. For this reason, I do suggest going through this review process in a staging environment, before exporting the configuration and re-importing it in Production. Much quicker and safer.

Dude, where’s my layout?

You may notice that something still looks weird on a Panel or when viewing content. You think it might be related to the template, and then you remember that that specific node type or page uses a custom template, through a template suggestion.

The file is there, twig debug tells you the suggestion is on the list and so should be considered, but the file is not loaded. This is a bug (and there is already an issue for it).

Layouts used in Display Suite displays should not be affected by this bug as long as, when you define the layout on your mytheme.layouts.yml, you set the class property to ‘\Drupal\ds\Plugin\DsLayout’.

i.e.

custom_1col_hero:
  label: Custom one column layout with hero (fixed)
  category: Custom
  template: templates/layouts/custom-1col-hero
  class: '\Drupal\ds\Plugin\DsLayout'
  regions:
    hero:
      label: Hero
    main:
      label: Main
    footer:
      label: Footer

This is a workaround made for and by Display Suite. For websites using any other Layout-based contrib modules like Panels, there’s currently no solution, other than patching the core codebase.

Conclusion

The big lesson here is about contrib modules: updating core to the new version is a requirement for developers and site-builders. But forcing websites to use the unstable branches or experimental modules is as problematic as it is dangerous. Unstable code is, by definition, unstable, possibly unsecure and isn’t easy to update.

Implementing new features and surfing the new trends is essential for staying relevant. But to do that, site owners should really be given the option of choosing between stable and experimental modules.

Here are some useful links to more info:

Apr 27 2017
Apr 27

My blog post from last week was very well received and sparked a conversation in the Drupal community about the future of Drupal. That conversation has continued this week at DrupalCon Baltimore.

Yesterday during the opening keynote, Dries touched on some of the issues raised in my blog post. Later in the day we held an unofficial BoF. The turn out was smaller than I expected, but we had a great discussion.

Drupal moving from a hobbyist and business tool to being an enterprise CMS for creating "ambitious digital experiences" was raised in the Driesnote and in other conversations including the BoF. We need to acknowledge that this has happened and consider it an achievement. Some people have been left behind as Drupal has grown up. There is probably more we can do to help these people. Do we need more resources to help them skill up? Should we direct them towards WordPress, backdrop, squarespace, wix etc? Is it is possible to build smaller sites that eventually grow into larger sites?

In my original blog post I talked about "peak Drupal" and used metrics that supported this assertion. One metric missing from that post is dollars spent on Drupal. It is clear that the picture is very different when measuring success using budgets. There is a general sense that a lot of money is being spent on high end Drupal sites. This has resulted in less sites doing more with Drupal 8.

As often happens when trying to solve problems with Drupal during the BoF descended into talking technical solutions. Technical solutions and implementation detail have a place. I think it is important for the community to move beyond this and start talking about Drupal as a product.

In my mind Drupal core should be a content management framework and content hub service for building compelling digital experiences. For the record, I am not arguing Drupal should become API only. Larger users will take this and build their digital stack on top of this platform. This same platform should support an ecosystem of Drupal "distros". These product focused projects target specific use cases. Great examples of such distros include Lightning, Thunder, Open Social, aGov and Drupal Commerce. For smaller agencies and sites a distro can provide a great starting point for building new Drupal 8 sites.

The biggest challenge I see is continuing this conversation as a community. The majority of the community toolkit is focused on facilitating technical discussions and implementations. These tools will be valuable as we move from talking to doing, but right now we need tools and processes for engaging in silver discussions so we can build platinum level products.

Share this post

Apr 25 2017
Apr 25

About five years ago I was made tech lead on a Drupal 7 project. The organisation I worked for had an extremely restrictive corporate IT policy, but I was sufficiently curious to install Drupal on my own laptop and play around with it. I also went to DrupalCamp London over a weekend in March. This impressed the senior managers enough to give me a fancy-sounding job title (though in truth it involved rather a lot more meetings than it did actually writing code). I’ve gone on from there through a variety of senior Drupal developer positions, but my dirty secret is, I’ve never really contributed back to Drupal. Until the last few weeks, that is. So, I thought I’d take the time to write about why that is, and what’s changed.

Rookie mistakes

While I was learning to use Drupal, I also started learning to use StackExchange. Naively, I asked a couple of questions and got the kind of terse responses noobs who haven’t really read the contribution guidelines often get. My interior monologue said, “Well, screw those guys. I tried my best to ask constructive questions and this is what I get?”

In reality, I think I’d asked a not-particularly constructive question that at least one person had asked before, but hey, why let details get in the way of a self-righteous sense of grievance?

Fast-forward to… DrupalCon

My company sent me to DrupalCon, I think probably because I’d had the nouse to go to DrupalCamp. Amongst other things, I went to a session on the Media Module. The guy presenting said something to the effect of, we know large organisations use this, but we don’t really know how any of them use it. I put my hand up and said, well I’m the Drupal lead at a large organisation, and I’d be happy to work on some use cases for you. Someone else in the audience (I felt slightly witheringly) said, “well, yeah, but if we just do it for one organisation we’ll lose half the developers who are contributing.”

“But… but… that wasn’t even what I was suggesting,” my interior monologue wailed. “And besides, it wasn’t even me that suggested it. I was just trying to help. Screw those guys.”

Fast forward to… Drupal Global Sprint Weekend

Manifesto ran a sprint weekend a few months ago. Despite my previous experiences, I decided I’d go. I looked around for issues for a while until I found one. It was a test, so I ran it. It looked good, so I moved it into ‘Reviewed and Tested’. Almost straight away, someone moved it back.

“Well what was the point of that?” my interior monologue fulminated. “If you’re not going to trust other people to review things then why even bother?”

Actually, looking back through the issue, I realise it was perfectly reasonable to push it back to ‘Needs Work’. The issue was far from simple and the subsequent discussion covered lots of things I’d not even really thought about.

That afternoon I found another, slightly fiddly issue that needed a test. It’d been open for months, so I tested it and put it into ‘Reviewed and Tested by the Community’. I left feeling fairly negative about the whole experience. I’d spent a day mostly just looking at bug lists and the one thing I’d managed to do just got nixed straight away. “I cost £1000 a day,” said my interior monologue (actually I have no idea what my day rate is, but facts, schmacts, right?) “I donate a day of my time and this is what I get for it?”

I think we can all agree my interior monologue is sometimes kind of a dick.

Fast forward to… DrupalCamp (again)

We had DrupalCamp London a few weeks later. I went along. Amongst other things I went along to a session by @rachel_norfolk on mentoring and contribution. She talked about false starts, and even the great big unmentionable – that sometimes, especially early on, contributing might not be particularly rewarding. But she also talked about the responsibility to contribute. And about how all these people – all these organisations – are making whole careers off the back of this amazing, free thing. And if they’re not making the time to help build and maintain that thing, well, that’s pretty bad.

My inner monologue sulked a bit. “She’s right”, it said, grudgingly. “But still. Screw those guys. Let’s get a beer.”

Fast forward to… my first contribution

The following Monday morning, I logged into my Drupal.org account. And I saw that one of my issues from the Sprint day had a comment. “Chof,” said my interior monologue. “Probably just someone telling you you’ve done it wrong”. But it wasn’t. It was @xjm, closing an issue that I’d worked on and thanking me, specifically, for taking the time to document my test steps. It might not look like a whole lot, but it really felt like a big deal to me. And it made me decide to try to contribute again.

That evening when I got home, I decided to pick up another one of the issues I’d looked at at the sprint day. To be honest, I still didn’t really understand what it was about. But I tried my best to put my questions into a constructive form, and added them to the ticket.

Not long after, @wim-leers replied with answers to my questions. It moved me on just enough, and I resolved to carry on working on the ticket. Then, you know, life stuff happened and we went through a busy spell at work and I didn’t really have any time to do anything for a while. @vaplas jumped in and finished off a chunk of the work. Initially I was a bit annoyed – I’d been hoping that, having taken the time to understand what the issue was about, I’d get to complete the solution. My interior monologue was just getting ready to say something dickish when I spotted that @alexpott had credited me on the patch, and written a message saying that although I’d not written any code, I’d helped to clarify the issue.

And again, it felt pretty amazing. So I picked up another one of the tickets in the same group. Again, I didn’t have time to do as much as I would have liked, and @vaplas wrote a large chunk of the patch. But there were some bits missing, and I helped to fill them in, and @vaplas really helped me to understand what I was doing. All of a sudden, I was submitting a patch for review! Plus, I’d learned how functional tests work in Drupal 8!

And that, readers, is how I got to contributing my first core patch.

Conclusion

I guess I learned the following things:

There’s a fine line between being a help vampire and asking constructive questions

But if you try to show your working, it might help someone else, and that’s really what it’s all about. You’re also, I think, allowed to ask for help when you’re contributing in a way that maybe you’re not when you’re just looking for a solution to your own problem. In fact, I’ve learned recently it’s not unusual to upload incomplete patches, or patches that you know will fail, so that they can be discussed.

Even a small amount of acknowledgement really means a lot

Say thank you. It’s totally worth it, and it makes everyone involved feel good about themselves, and about contributing. The help and encouragement I got from Wim, Vaplas and XJM has been completely instrumental in getting me to come back and to keep on trying, and I’m very grateful to all of them.

The community’s focus on collaboration over competition isn’t just a handy, alliterative slogan

They really mean it. If you help even a little bit there’s a good chance you might get credited, and people are genuinely respectful of the effort you’re making, even if you’re just making the effort to understand.

Persistence is important

Don’t get downhearted if someone pushes an issue you’ve updated back down to its previous status. They’ve probably got a good reason for it. Don’t worry if you get too busy at work or in your life. Your ticket might move along, or it might get finished, but there will still be ways for you to help. And they’re always worth it, no matter how small they might seem.

You start to get into a rhythm

You think you won’t have time to contribute, but once you’ve found an issue or group of issues to work on, it’s quite easy to do half an hour here and there.

And lastly, if your interior monologue is anything like mine, you should probably learn to ignore it.

Apr 24 2017
Apr 24
April 24th, 2017

Making Huge Strides Back to Desktop

So what is this Electron thing everyone keeps talking about? Even if you haven’t heard of it, you may have used it! With over 4 millions daily users on Slack’s business oriented chat system, their cross-platform desktop application helps them reach their users outside of browsers, but these systems are in fact part of the same thing.

Back in May 2014, prolific bastions of open source and $2b valuated company, GitHub, took the custom application wrapper it originally created for its Atom code editor and released into the world—and Electron was born. Rebranded from “Atom Shell” in 2015, Electron began to take off almost immediately, allowing regular web developers the ability to make native-like, high performance desktop applications using the exact same HTML, CSS, and JavaScript technologies they use to make the rest of the web.

Piggybacking on the huge wave of API first work in Drupal 8 utilized via the Waterwheel client wrapper, building with Electron allows you to create nearly native desktop experiences using frameworks like React, Redux, Angular, or anything else that your team can construct to run in a web browser. Beyond even that, Electron gives JavaScript direct access to low level Node.js and operating system APIs, allowing your application direct file access, running custom binaries for data processing, execution of alternative scripting languages, serial port or hardware access, and tons more.

Supercharge Your Next Web App

This year at DrupalCon Baltimore, we present “Supercharge Your Next Web App with Electron”, a session that digs deep and covers everything you need in order to dip into the waters of Electron. We’ll talk about what big companies have already taken the plunge and even provide a checklist for when not to move from the web to a desktop app.

Though an Electron app may not be the right choice for your next application, knowing what tools are available to you—and understanding their incredible possibilities—is going to serve you anytime you’re  considering user-oriented frameworks. Don’t miss out on this interesting view into a future of low-energy/high-return desktop applications in the DrupalCon Horizons track this year.

And, during active exposition hours, make sure to come over to the Four Kitchens booth to see a live demo of an Electron app powered by JavaScript—we build a robot artist!

Four Kitchens: We make content go

Recommended Posts

  • In this issue: Launching the new EW.com, MeteorJS; plus Sane Stack, Herp Derpsum, and switching to Sublime Text 3.
  • Fun & Games DrupalCon Baltimore is next week and we’re so excited to get back together in Baltimore! As the official Drupal Games sponsors, we take fun very seriously and…
  • "API First" or, as some may call it, "Decoupled Drupal", remains a topic of much discussion among the Drupal community. Here are just a few sessions being presented at Drupalcon…
James Todd
James Todd

James tinkers with hardware, software, and everything in between.

Events

Blog posts about ephemeral news, events, parties, conferences, talks—anything with a date attached to it.

Read more Events
Apr 21 2017
Apr 21

WOW! The response to my blog post on the future of Drupal earlier this week has been phenomenal. My blog saw more traffic in 24 hours than it normally sees in a 2 to 3 week period. Around 30 comments have been left by readers. My tweet announcing the post was the top Drupal tweet for a day. Some 50 hours later it is still number 4.

It seems to really connected with many people in the community. I am still reflecting on everyone's contributions. There is a lot to take in. Rather than rush a follow up that responds to the issues raised, I will take some time to gather my thoughts.

One thing that is clear is that many people want to use DrupalCon Baltimore next week to discuss this issue. I encourage people to turn up with an open mind and engage in the conversation there.

A few people have suggested a BoF. Unfortunately all of the official BoF slots are full. Rather than that be a blocker, I've decided to run an unofficial BoF on the first day. I hope this helps facilitate the conversation.

Unofficial BoF: The Future of Drupal

When: Tuesday 25 April 2017 @ 12:30-1:30pm
Where: Exhibit Hall - meet at the Digital Echidna booth (#402) to be directed to the group
What: High level discussion about the direction people think Drupal should take.
UPDATE: An earlier version of this post had this scheduled for Monday. It is definitely happening on Tuesday.

I hope to see you in Baltimore.

Share this post

Apr 20 2017
Apr 20
21 April 2017

A while ago I wanted to present events with a date range on a Drupal 7 site. Drupal’s contributed date module provided a way to store the data, but the display wasn’t to my liking, always showing the complete start and end date.

Wouldn’t it be nice to show the dates in a more compact fashion, by not repeating the day, month or year where they aren’t necessary? Like this:

  • 24–25 January 2017
  • 29 January–3 February 2017
  • 9:00am–4:30pm, 1 April 2017

(and yes, those are en dashes!)

There didn’t seem to be a contributed module available, so I wrote some bespoke code for the project. It only supports one particular UK-specific date format, and there’s no support for different languages.

Over the last few days, I’ve spent some time porting it to Drupal 8 and improving it, suitable for release as a new contributed module. I thought I’d write about this process in the form of a tutorial over a few posts. I hope you’ll find it useful.

Let’s port this to Drupal 8

Firstly, as of Drupal 8.3, there’s an experimental core date range module, so we’re no longer reliant on another contributed module for data storage.

As mentioned, the original code doesn’t offer much in the way of customisation. Neither are there any guarantees about what will happens when different languages and timezones are introduced. While there are lots of things we can improve on later, for now let’s get what was there before working with Drupal 8.

This module consists of a field formatter. These are well documented on drupal.org, and make for quite a gentle introduction to Drupal 8 development.

It’s helpful to think of field formatters in two parts—a definition, describing what it is, and an implementation, concerned with how it works. This is quite a common pattern in Drupal. In D7, we’d see this pattern implemented as two hook functions; in D8, as a plugin and annotation.

D7 .module file → D8 plugin

In Drupal 7, lots of code lives in a single, monolithic .module file. Drupal 8 makes use of object oriented programming, so individual components such as field formatters are each defined in their own classes. A plugin is a class that implements particular functionality and is discoverable at runtime.

Our plugin is defined in src/Plugin/Field/FieldFormatter/DateRangeCompactFormatter.php and looks like this:

<?php
namespace Drupal\daterange_compact\Plugin\Field\FieldFormatter;

class DateRangeCompactFormatter extends FormatterBase {
  /* implementation */
}

D7 info hook → D8 annotation

Our Drupal 7 implementation has a hook function that specifies there is a formatter called daterange_compact (and labelled Compact), that is suitable for date/time fields:

/**
 * Implements hook_field_formatter_info().
 */
function daterange_compact_field_formatter_info() {
  $info['daterange_compact'] = array(
    'label' => t('Compact'),
    'field types' => array('datetime'),
    'settings' => array(),
  );
  return $info;
}

In Drupal 8, we supply the same information but using an annotation. Note the change of field type, Drupal 8.3 comes with a an experimental date range field type that’s separate from the singular date field.

/**
 * @FieldFormatter(
 *   id = "daterange_compact",
 *   label = @Translation("Compact"),
 *   field_types = {"daterange"}
 * )
 */
class DateRangeCompactFormatter...

D7 → D8 implementation

In Drupal 7, the formatting itself happens in another hook, that gets passed the field values (via the $items parameter) and returns the desired output. I’ve left out the actual implementation for brevity; complete versions are available here (D7) and here (D8).

/**
 * Implements hook_field_formatter_view().
 */
function daterange_compact_field_formatter_view($entity_type, $entity,
      $field, $instance, $langcode, $items, $display) {
  /* given the field values, return a render array */
}

This is really similar in Drupal 8, except that we define a function called viewElements in our class, and the field values are accessible through an object.

function viewElements(FieldItemListInterface $items, $langcode) {
  /* given the field values, return a render array */
}

Test it!

To see this in action, let’s set up a content type with a field of type date range. The field is date only—at the moment this formatter doesn’t support times (something we’ll change later). We’ll populate the field with four pairs of values, all of which should appear differently:

  • 1 January 2017 (start and end date are the same)
  • 2–3 January 2017 (same month)
  • 4 January–5 February 2017 (different months, same year)
  • 6 January 2017–7 January 2018 (different years)

A quick check reveals the output is as expected, so we’ve successfully ported the formatter to Drupal 8!

Now is an ideal time to automate that check with a unit test. We’re going to be adding more functionality to this module, during which we may well inadvertently introduce regressions. The test will help flag those up.

Writing a test in D8

Testing field formatters is done with PHPUnit. The timestamp formatter does a similar thing to our formatter, so we can examine that to see how it works. There is a TimestampFormatterTest class that extends KernelTestBase, so let’s create a similar class in modules/daterange_compact/src/Tests/DateRangeCompactFormatterTest.php:

<?php
namespace Drupal\daterange_compact\Tests;

class DateRangeCompactFormatterTest extends KernelTestBase {
  /* implementation */
}

The setUp function will create an arbitrary entity type with fields. It makes use of the entity_test module to define that entity and bundle, to which we create an appropriate daterange field and define it’s default display settings.

protected function setUp() {
  parent::setUp();
  /* 1. install the entity_test schema */
  /* 2. programmatically create a field of type daterange */
  /* 3. programmatically create a field instance based on the above */
  /* 4. set the display settings to use our new formatter */
}

Each function whose name begins with test___ corresponds to a single test. Within each test we can iterate through a set of values, populating an entity, rendering it and comparing the expected output with the actual output.

function testCompactFormatter() {
  $all_data = [
    ['start' => '2017-01-01', 'end' => '2017-01-01', 'expected' => '1 January 2017'],
    ['start' => '2017-01-02', 'end' => '2017-01-03', 'expected' => '2–3 January 2017'],
    ['start' => '2017-01-04', 'end' => '2017-02-05', 'expected' => '4 January–5 February 2017'],
    ['start' => '2017-01-06', 'end' => '2018-02-07', 'expected' => '6 January 2017–7 February 2018'],
  ];

  foreach ($all_data as $data) {
    /* 1. programmatically create an entity and populate start/end dates */
    /* 2. programmatically render the entity */
    /* 3. assert that the output contains the expected text */
  }

You can see the full implementation of the test class here.

This test won’t stop bugs, but it will mean that if the behaviour changes in such a way that the given dates start producing different output, we’ll have a way of knowing. At that point, either the code or the test might need some work.

Running the test

The run PHPUnit, we need to set it up by creating a phpunit.xml file in the core directory. This is documented on drupal.org, and there’s an example file provided.

We also need a database (different to the one used for the site itself).

To run all the tests in our module, run the following command from the core directory:

../vendor/bin/phpunit ../modules/custom/daterange_compact/

If everything worked, we should get a result like the following:

PHPUnit 4.8.27 by Sebastian Bergmann and contributors.

.

Time: 4.92 seconds, Memory: 6.75Mb

OK (1 test, 6 assertions)

The test passed! That’s a good first version of our updated contrib module.

In the next post we’ll look at some improvements, namely making the format configurable and adding support for times.

Apr 19 2017
Apr 19

Update 21 April: I've published a followup post with details of the BoF to be held at DrupalCon Baltimore on Tuesday 25 April. I hope to see you there so we can continue the conversation.

Drupal has a problem. No, not that problem.

We live in a post peak Drupal world. Drupal peaked some time during the Drupal 8 development cycle. I’ve had conversations with quite a few people who feel that we’ve lost momentum. DrupalCon attendances peaked in 2014, Google search impressions haven’t returned to their 2009 level, core downloads have trended down since 2015. We need to accept this and talk about what it means for the future of Drupal.

Technically Drupal 8 is impressive. Unfortunately the uptake has been very slow. A factor in this slow uptake is that from a developer's perspective, Drupal 8 is a new application. The upgrade path from Drupal 7 to 8 is another factor.

In the five years Drupal 8 was being developed there was a fundamental shift in software architecture. During this time we witnessed the rise of microservices. Drupal is a monolithic application that tries to do everything. Don't worry this isn't trying to rekindle the smallcore debate from last decade.

Today it is more common to see an application that is built using a handful of Laravel micro services, a couple of golang services and one built with nodejs. These applications often have multiple frontends; web (react, vuejs etc), mobile apps and an API. This is more effort to build out, but it likely to be less effort maintaining it long term.

I have heard so many excuses for why Drupal 8 adoption is so slow. After a year I think it is safe to say the community is in denial. Drupal 8 won't be as popular as D7.

Why isn't this being talked about publicly? Is it because there is a commercial interest in perpetuating the myth? Are the businesses built on offering Drupal services worried about scaring away customers? Adobe, Sitecore and others would point to such blog posts to attack Drupal. Sure, admitting we have a problem could cause some short term pain. But if we don't have the conversation we will go the way of Joomla; an irrelevant product that continues its slow decline.

Drupal needs to decide what is its future. The community is full of smart people, we should be talking about the future. This needs to be a public conversation, not something that is discussed in small groups in dark corners.

I don't think we will ever see Drupal become a collection of microservices, but I do think we need to become more modular. It is time for Drupal to pivot. I think we need to cut features and decouple the components. I think it is time for us to get back to our roots, but modernise at the same time.

Drupal has always been a content management system. It does not need to be a content delivery system. This goes beyond "Decoupled (Headless) Drupal". Drupal should become a "content hub" with pluggable workflows for creating and managing that content.

We should adopt the unix approach, do one thing and do it well. This approach would allow Drupal to be "just another service" that compliments the application.

What do you think is needed to arrest the decline of Drupal? What should Drupal 9 look like? Let's have the conversation.

Share this post

Apr 18 2017
Apr 18
April 18th, 2017

Fun & Games

DrupalCon Baltimore is next week and we’re so excited to get back together in Baltimore! As the official Drupal Games sponsors, we take fun very seriously and this year you can be sure to find some exciting things to do at our booth—we won’t spoil the surprise but let’s just say you’ll get to see some of us IRL and IVRL.

And if you visited us last year, you know we are all about that Free Throw game. Our undefeated Web Chef, Brian Lewis, will be there to take on any challenger. We’ve all been practicing and we are READY. Are you?

We’ll also have some of our widely-enjoyed Lightning Talks during lunch intervals right at our booth! Learn something new in just a few minutes, howbowdat? Stop by our booth to check out the schedule.

Web Chef Talks

It’s been an exciting year and the Web Chefs are ready to drop some knowledge, including:

Future of the CMS: Decoupled, Multichannel, and Content-as-a-Service, presented by Four Kitchens Co-Founder and CEO, Todd Ross Nienkerk.

Supercharge Your Next Web App with Electron, presented by Web Chef engineer, James Todd.

Why Klingon Matters for Content: The Secret Power of Language, presented by our content specialist, Douglas Bigham.

Training: API First Drupal 8 with React.js and Waterwheel, a training with JavaScript engineer, Luke Herrington.

Party with a Purpose

Last—but definitely not least—you’re cordially invited to our official DrupalCon gathering, Drinks with a Mission, hosted by Four Kitchens and our friends at Kalamuna and Manatí.

Join us on April 25th at Peter’s Pour House from 6-9pm for lively conversation, free-flowing libations, and a structured forum for hashing out ideas on how to use Drupal to overcome the challenges many of our communities face in today’s national and global political climate.

RSVP here!

See you in BMD!

Oh! The kittens are coming along to Baltimore as well—four of them to be exact—and we can’t wait to reveal this year’s DrupalCon t-shirt design. We’re not kitten around. We wish we could show you right meow.

P.S. Check out the 10-day Baltimore weather forecast.

Recommended Posts

Lucy Weinmeister
Lucy Weinmeister

Lucy Weinmeister is the marketing coordinator at Four Kitchens. She loves to share all the new and exciting things the Web Chefs are cooking up at 4K. She is forever reading a book.

Events

Blog posts about ephemeral news, events, parties, conferences, talks—anything with a date attached to it.

Read more Events
Apr 18 2017
Apr 18

Prefer watching a video?

Click here to sign up to our newsletter and watch an exclusive video on Image Widget Crop and Focal Point.

If you’ve done any Drupal site building, I’m sure you’ve experienced the following issue. You create an image style with the “Scale and crop” effect and everything is going great until an editor uploads an image with a different aspect ratio.

Now instead of images getting cropped correctly, they’re getting cut in half, or the top part is chopped off, and images are not displaying nicely.

You could fix this problem by tweaking the image style to handle different aspect ratios, but it’ll never be the perfect solution.

The best option is to crop images directly in Drupal, and this is what you’ll learn today.

In this tutorial, you’ll learn how to avoid these situations by using Crop API.

Now, Crop API doesn’t offer any interface on its own; it’s just an API. The two modules that provide an interface are Image Widget Crop and Focal Point. We’ll take a look at these modules in detail, in this tutorial.

Image Widget Crop gives an editor the most flexibility by allowing them to crop images directly within Drupal.

The only downside is they’ll need to manually crop an image every time they upload an image which is okay if you only have a single crop. But it can be cumbersome if you have multiple crops.

If you want full control over how the image is resized then look at using this module.

Focal Point lets an editor select a point on an image and Drupal will crop around it. It’s more automated but doesn’t give you absolute control like Image Widget Crop.

If all you want to do is make sure a particular region of the image is cropped then look at using this module.

Caching

If you’re using some reverse proxy or CDN such as Varnish or CloudFront, you will have issues with images not changing after being cropped. I can’t offer a solution in this tutorial because every case is different so this won’t be covered.

If you know of a good workaround to handle invalidating images, then leave a comment.

Getting Started

Before we begin, go download Crop API, Image Widget Crop and Focal point. For the first part, we’ll only install “Image Widget Crop.”

Using Drush:

$ drush dl crop image_widget_crop focal_point

Crop Images using Image Widget Crop

We’ll start things off my using Image Widget Crop to allow content editors to crop the image on the Article content type.

First, make sure you installed the “ImageWidgetCrop” module.

Create Crop Type

1. Go to Configuration, Crop types and click on “Add crop type”.

2. Enter in Large into Name and “Used to crop the Large image style.” into Description.

3. In Aspect Ratio add 1:1.

This will make it easier to select a crop because the aspect ratio will stay the same.

4. Once completed click on “Save crop type”.

What’s a Soft Limit and Hard Limit?

When you set a soft limit, the select region will change color indicating the soft limit has been reached. But the user can still resize the crop area smaller.

Hard limit, on the other hand, will stop a user from selecting a crop smaller than what’s been defined.

Configure Image Style

Now that we’re created a crop type. The next bit of work we need to do is configure an image style.

In this example, we’ll use the “Large (480×480)” image style which comes with the Standard installation profile.

1. Go to Configuration, “Image styles” and click on Edit on the image style named “Large (480×480)”.

2. Select “Manual crop” from the “Select a new effect” drop-down and click on Add.

3. Select Large from the “Crop type” and click on “Add effect”.

4. Re-order the effect, so it’s above “Scale 480×480” and click on “Update style”.

Let’s recap what we’ve done.

We added a manual crop effect which use the Large crop type we defined earlier. The image style will first process what’s been cropped manually, and then it’ll scale the image to 480×480.

The last effect, “Scale 480×480” is still necessary because the cropped size can be anything greater than 480×480.

Configure the Image Widget Crop

So far we’ve created a crop type and tweaked the Large image style to use the crop. Now let’s set up Image Widget Crop on the image field.

1. Go to Structure, “Content types” and click on “Manage form display” on the Article row.

2. From the Widget drop-down on the Image row, select “ImageWidget crop”.

If you can’t see the option make sure you’ve installed Image Widget Crop module.

3. Click on the cog-wheel and select Large from within the “Crop Type” drop-down.

If you can’t see your crop type make sure you’ve added it to an image style. You can’t just create a crop type; it must be added to an image style for it to appear here.

4. Click on Update to close the “Widget settings”, then scroll to the bottom and click on Save.

Test Image Cropping

Create a test article or modify an existing one. Upload an image, and you should see a collapsed fieldset called “Crop image”, click on it.

Select your crop and click on Save.

Now if you go to the article page the Large image style should display the cropped region.

Automatic Crop using Focal Point

In the last section, you learnt how to manually crop images. The problem, however, is that cropping an image is a manual process. An editor will have to crop each image they upload. It’s okay if you only have one, but on large projects, you could have a couple of crop types. Manually cropping images could be tedious.

Focal Point takes the manual work away and automatically crops around a defined focal point.

Select a focal point once, when you upload an image, and Drupal will handle the rest.

If you’re following along, go ahead and install Focal Point. We’ll configure Focal Point on the Large and Medium image style.

Set Focal Point

Go to any image field widget, and you should see a crosshair on the image. You’re not required to configure a custom image widget. Focal Point takes over the standard image widget.

The only bit of configuring we need to do is modify the Large and Medium image style.

1. Go to Configuration, “Image styles”

2. Click Edit on the Large row.

3. Select “Focal Point Scale and Crop” from the “Select a new effect” drop-down and click on Add.

4. Enter 480 into widget and height.

5. Delete the “Scale 480×480” effect.

6. Click on “Update style”.

Do the same thing for the medium image style. But instead of adding 480 to the width and height, add 220.

Image Widget

Focal Point will only work if you’re using the standard Image widget. You can’t use Focal Point and Image Widget Crop at the same time.

Preview Focal Point

Once the image styles have been configured, you can preview what the crop will look like.

Just go back to an image field and click on the Preview link.

Set Focal Point

To select a focal point just move the crosshair to where you want the focal point and save the form. Drupal will handle the rest.

Summary

Drupal has always been great at manipulating images but Crop API gives power back to the content editors and offers a flexible solution. No longer are you required to hardcode widths and heights into image styles.

Let the editor choose how they want their images cropped.

FAQs

Q: Can I use Image Widget Crop and Focal Point together?

No.

Q: I can’t select a crop from the “Crop type” drop-down even though I created a crop type.

You need to add a crop to an image style for it to appear in the drop-down.

Q: I re-cropped an image, but it hasn’t updated.

Try performing a hard refresh, ctrl+f5 on Windows or cmd + shift + r on Mac.

Apr 12 2017
Apr 12

As a Swiss-based Drupal Agency, we have to create a lot of multilingual sites. Since Switzerland has three official languages (German, French, Italian) and even one more national language (Rumantsch), we are used to this requirement and we found our way with Drupal to make this an easy task (usually). We mainly used node translations in Drupal 7 for maximum flexibility. We used to separate languages from each other using the various i18n modules, language specific menus, blocks, URL-patterns, terms and so on.

With Drupal 8, things changed.
I struggled a little doing multilingual sites in Drupal 8 the same way I was used to in Drupal 7 because node translation is not available anymore (which is good) so I had to find another way to achieve the same easy to handle translations system. For us and for our clients. Let me explain, what I have learned.

Drupal 8 multilanguage

Image: drupal8multilingual.org

Drupal 8 issues multilanguage challenges

Challenge 1: Node add / edit menu handling

The main challenge I had using Drupal 8, was the ease to build your menus directly from the node creation page. You can do it, but only for the initial language. If you try to add a translated node to another menu or rename the item, it always ends up moving / renaming the source node instead of adding a link to the translation. So it can become quite confusing building a navigation directly from the node creation page or to add translations to the menu. A workaround was to add all navigation items manually in the menu administration if you are using a menu per language. With lots of languages and menus / items, this is not really a convenient task. Fortunately, translations from the node creation page have been implemented with a later release of Drupal 8.

Challenge 2: Untranslated Nodes show up in Menu

Another thing which bothered me was that untranslated nodes show up in the navigation (if you use only one menu). This can be quite confusing since most of the times not every page is translated in every language. Or in some languages, you need a little more than in others. You can read a lot about this topic and the reasons behind (e.g. here and here). However you do it, it’s always wrong in some situations and perfectly fine in others. But to be “limited” and “locked in” to a certain way is not nice and you have to deal with it. To sum up, once a node is put into a menu, it will show up everywhere. Regardless if there are translations or not.

Challenge 3: Language Switcher shows all languages – always.

Somewhat confusing is the Language Switcher. In Drupal 7, a language link was not available or strikethrough if there was no translation available. In Drupal 8, every language is always visible and linked. So if you look on a German page which is only available in German, the language switcher will present you all language links to the same node. A click on those language links mainly changes the interface language but the node content remains the same (since not translated). Usually also with a drupalish URL (node/xxxx) because there is no translation for the node and therefore also no URL alias available. This behavior is confusing and wrong in my point of view

An example to illustrate the above-written challenges.

multilanguage issues with Drupal 8

English Front-Page with mixed navigation items.

The screen above shows an installation with 2 languages (English and German). The English Page is a basic page which has a translation. English is selected. If you choose Deutsch on the language switcher, the English Page becomes Deutsche Seite (see image below) and shows the German content. So far so good. But the second menu item you see with the title Über uns (nur Deutsch) should not appear here since it’s only available in German. But it does. And if you actually go on this page, you will see the German text with everything English around it and no URL-Alias (/node/2 in this example). This is usually not very useful for us.

multilanguage issues with Drupal 8

German only Page – Language Switcher visible.

Also, the language switcher shown in the image above is from my point of view wrong or not very useful. It shows a link to the English version, but there is no English translation for this node. So why is it there? To see a German page with English decoration? Not sure. But I want to get rid of this link or at least modify it to be stroked through if the language is not available.

How to fix improve this?

Luckily, the Drupal community is always good for help. After some “research” on the web, I finally found (besides lots of discussions and comments in the issue queues) a way to achieve the desired setup.

To sum up again: I want to see only menu items which are available in my language and only see a link to another language, if a translation is available.

Since there is no patch and still some ongoing discussions on drupal.org you need to implement it on your own. Implement the following two modules.

Hide untranslated menu items

Code from https://www.drupal.org/node/2466553#comment-11991690. Credits go to michaelkoehne.

<?php use Drupal\Core\Menu\MenuLinkInterface; use Drupal\menu_link_content\Plugin\Menu\MenuLinkContent; use Drupal\Core\Language\LanguageInterface; /** * Implements hook_preprocess_menu(). */ function MYMODULE_preprocess_menu(&$variables) { if ($variables['menu_name'] == 'main') { $language = Drupal::languageManager() ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT) ->getId(); foreach ($variables['items'] as $key => $item) { if (!$variables['items'][$key] = MYMODULE_checkForMenuItemTranslation($item, $language)) { unset($variables['items'][$key]); } } } } function MYMODULE_checkForMenuItemTranslation($item, $language) { $menuLinkEntity = MYMODULE_load_link_entity_by_link($item['original_link']); if ($menuLinkEntity != NULL) { $languages = $menuLinkEntity->getTranslationLanguages(); // Remove links which are not translated to the current language. if (!array_key_exists($language, $languages)) { return FALSE; } else { if (count($item['below']) > 0) { foreach ($item['below'] as $subkey => $subitem) { if (!$item['below'][$subkey] = MYMODULE_checkForMenuItemTranslation($subitem, $language)) { unset($item['below'][$subkey]); } } } return $item; } } } function MYMODULE_load_link_entity_by_link(MenuLinkInterface $menuLinkContentPlugin) { $entity = NULL; if ($menuLinkContentPlugin instanceof MenuLinkContent) { $menu_link = explode(':', $menuLinkContentPlugin->getPluginId(), 2); $uuid = $menu_link[1]; $entity = \Drupal::service('entity.repository') ->loadEntityByUuid('menu_link_content', $uuid); } return $entity; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

<?php

use Drupal\Core\Menu\MenuLinkInterface;

use Drupal\menu_link_content\Plugin\Menu\MenuLinkContent;

use Drupal\Core\Language\LanguageInterface;

/**

* Implements hook_preprocess_menu().

*/

function MYMODULE_preprocess_menu(&$variables) {

  if ($variables['menu_name'] == 'main') {

    $language = Drupal::languageManager()

      ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)

      ->getId();

    foreach ($variables['items'] as $key => $item) {

      if (!$variables['items'][$key] = MYMODULE_checkForMenuItemTranslation($item, $language)) {

        unset($variables['items'][$key]);

      }

    }

  }

}

function MYMODULE_checkForMenuItemTranslation($item, $language) {

  $menuLinkEntity = MYMODULE_load_link_entity_by_link($item['original_link']);

  if ($menuLinkEntity != NULL) {

    $languages = $menuLinkEntity->getTranslationLanguages();

    // Remove links which are not translated to the current language.

    if (!array_key_exists($language, $languages)) {

      return FALSE;

    }

    else {

      if (count($item['below']) > 0) {

        foreach ($item['below'] as $subkey => $subitem) {

          if (!$item['below'][$subkey] = MYMODULE_checkForMenuItemTranslation($subitem, $language)) {

            unset($item['below'][$subkey]);

          }

        }

      }

      return $item;

    }

  }

}

function MYMODULE_load_link_entity_by_link(MenuLinkInterface $menuLinkContentPlugin) {

  $entity = NULL;

  if ($menuLinkContentPlugin instanceof MenuLinkContent) {

    $menu_link = explode(':', $menuLinkContentPlugin->getPluginId(), 2);

    $uuid = $menu_link[1];

    $entity = \Drupal::service('entity.repository')

      ->loadEntityByUuid('menu_link_content', $uuid);

  }

  return $entity;

}

Hide untranslated languages in language switcher

Code from https://www.drupal.org/node/2791231#comment-12004615 (slightly adapted. Links get a class, not removed by default). Credits to Leon Kessler.

<?php /** * @file * Hide language switcher links for untranslated languages on an entity. */ use Drupal\Core\Entity\ContentEntityInterface; /** * Implements hook_language_switch_links_alter(). */ function MYOTHERMODULE_language_switch_links_alter(array &$links, $type, $path) { if ($entity = MYOTHERMODULE_get_page_entity()) { $new_links = array(); foreach ($links as $lang_code => $link) { try { if ($entity->getTranslation($lang_code)->access('view')) { $new_links[$lang_code] = $link; } } catch (\InvalidArgumentException $e) { // This language is untranslated so do not add it to the links. $link['attributes']['class'][] = 'not-translated'; $new_links[$lang_code] = $link; } } $links = $new_links; // If we're left with less than 2 links, then there's nothing to switch. // Hide the language switcher. if (count($links) < 2) { $links = array(); } } } /** * Retrieve the current page entity. * * @return Drupal\Core\Entity\ContentEntityInterface * The retrieved entity, or FALSE if none found. */ function MYOTHERMODULE_get_page_entity() { $params = \Drupal::routeMatch()->getParameters()->all(); $entity = reset($params); if ($entity instanceof ContentEntityInterface) { return $entity; } return FALSE; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

<?php

/**

* @file

* Hide language switcher links for untranslated languages on an entity.

*/

use Drupal\Core\Entity\ContentEntityInterface;

/**

* Implements hook_language_switch_links_alter().

*/

function MYOTHERMODULE_language_switch_links_alter(array &$links, $type, $path) {

  if ($entity = MYOTHERMODULE_get_page_entity()) {

    $new_links = array();

    foreach ($links as $lang_code => $link) {

      try {

        if ($entity->getTranslation($lang_code)->access('view')) {

          $new_links[$lang_code] = $link;

        }

      }

      catch (\InvalidArgumentException $e) {

        // This language is untranslated so do not add it to the links.

        $link['attributes']['class'][] = 'not-translated';

        $new_links[$lang_code] = $link;

      }

    }

    $links = $new_links;

    // If we're left with less than 2 links, then there's nothing to switch.

    // Hide the language switcher.

    if (count($links) < 2) {

      $links = array();

    }

  }

}

/**

* Retrieve the current page entity.

*

* @return Drupal\Core\Entity\ContentEntityInterface

*   The retrieved entity, or FALSE if none found.

*/

function MYOTHERMODULE_get_page_entity() {

  $params = \Drupal::routeMatch()->getParameters()->all();

  $entity = reset($params);

  if ($entity instanceof ContentEntityInterface) {

    return $entity;

  }

  return FALSE;

}

Please note: The code above is from Drupal.org and therefore thanks to the original authors linked above.

Enable those two modules and you’re all set!

I did not encounter any issues yet using those two modules. If ever something changes in the way Drupal handles those cases, you just need to switch off the modules and everything should be back to normal. So nothing to lose right?

There are other attempts to this by altering the menu block. One of them is Menu Block Current Language but I had no luck with this one. On my most recent project, it worked with one menu but not if you separate your menu by two blocks (different starting levels).

I would love to hear how you guys handle those cases or how you deal with I18N in general. I’m sure there are a gazillion other ways to do it.

Apr 07 2017
Apr 07

After implementing some larger enterprise Drupal 8 websites, I would like to share some insights, how to solve common issues in the deployment workflow with Drupal 8 CMI.

Introduction to Drupal CMI

First of all, you need to understand, how the configuration management in Drupal 8 works. CMI allows you to export all configurations and its dependencies from the database into yml text files. To make sure, you never end up in an inconsistent state, CMI always exports everything. By default, you cannot exclude certain configurations.

Example:

If you change some configuration on the live database, these configurations will be reverted in the next deployment when you use

drush config-import

1

drush config-import

This is helpful and will make sure, you have the same configuration on all your systems.

How can I have different configurations on local / stage / live environments?

Sometimes, you want to have different configurations on your environments. For example, we have installed a “devel” module only on our local environment but we want to have it disabled on the live environment.

This can be achieved by using the configuration split module: https://www.drupal.org/project/config_split

What does Configuration Split?

This module slightly modifies the CMI by implementing a Config Filter (https://www.drupal.org/project/config_filter). Importing and exporting works the same way as before, except some configuration is read from and written to different directories. Importing configuration still removes configuration not present in the files. Thus, the robustness and predictability of the configuration management remains. And the best thing is: You still can use the same drush commands if you have at least Drush 8.1.10 installed.

Configuration Split Example / Installation Guide

Install config_split using composer. You need need at least “8.x-1.0-beta4” and > drush 8.1.10 for this guide.

composer require drupal/config_split "^1.0"

1

composer require drupal/config_split "^1.0"

Enable config_split and navigate to “admin/config/development/configuration/config-split”

drush en config_split -y

1

drush en config_split -y

Optional: Installing the chosen module will make the selection of blacklists / greylists way more easier. You can enable chosen only on admin pages.

composer require drupal/chosen "^1.0"

1

composer require drupal/chosen "^1.0"

I recommend you to create an “environments” subfolder in your config folder. Inside this folder you will have a separate directory for every environment:

Drupal 8 Configuration Management Folders

Now you can configure your environments:

Config Split in Drupal 8 Configuration Management

The most important thing is, that you set every environment to “Inactive”. We will later activate them according to the environment via settings.php

Config Split settings with the Drupal 8 Configuration Management

Here is my example where I enable the devel module on local:

Dev Environment Example

Activate the environments via settings.php

This is the most important part of the whole setup up. Normally, we never commit the settings.php into git. But we have a [environment]-settings.php in git for every environment:

settings.php (not in git) variables-dev.php (in git and included in the settings.php of dev) variables-live.php (in git and included in the settings.php of live) settings.local.php (in git and included locally)

settings.php (not in git)

variables-dev.php (in git and included in the settings.php of dev)

variables-live.php (in git and included in the settings.php of live)

settings.local.php (in git and included locally)

You need to add the following line to the variables-[environment].php. Please change the variable name according to your environment machine name:

// This enables the config_split module $config['config_split.config_split.dev']['status'] = TRUE;

// This enables the config_split module

$config['config_split.config_split.dev']['status'] = TRUE;

If you have done everything correctly and cleared the cache you will see “active (overriden)” in the config_split overview next to the current environment.

Now you can continue using

drush config-import -y drush config-export -y

drush config-import -y

drush config-export -y

and config_split will do the magic.

How can I exclude certain Config Files and prevent them to be overridden / deleted on my live environment?

The most prominent candidates for this workflow are webforms and contact forms. In Drupal 7, webforms are nodes and you were able to give your CMS administrator the opportunity to create their own forms.

In Drupal 8 webforms are config entities, which means that they will be deleted while deploying if the yml files are not in git.

After testing a lot of different modules / drush scripts, I finally came up with an easy to use workflow to solve this issue and give CMS administrators the possibility to create webforms without git knowledge:

Set up an “Excluded” environment

First of all, we need an “excluded” environment. I created a subfolder in my config-folder and added a .htaccess file to protect the content. You can copy the .htaccess from an existing environment, if you are lazy. Don’t forget to deploy this folder to your live system before you do the next steps.

Folders

Excluded

Now you can exclude some config files to be excluded / grey-listed on your live environment:

webform.webform.* contact.form.*

webform.webform.*

contact.form.*

Greylist Webform in Config Split

Set the excluded environment to “Inactive”. We will later enable it on the live / dev environment via settings.php.

Enable “excluded” environment and adapt deployment workflow

We enable the “excluded” environment on the live system via variables-live.php (see above):

// This will allow module config per environment and exclude webforms from being overridden $config['config_split.config_split.excluded']['status'] = TRUE;

// This will allow module config per environment and exclude webforms from being overridden

$config['config_split.config_split.excluded']['status'] = TRUE;

In your deployment workflow / script you need to add the following line before you do a drush config-import:

#execute some drush commands echo "-----------------------------------------------------------" echo "Exporting excluded config" drush @live config-split-export -y excluded echo "-----------------------------------------------------------" echo "Importing configuration" drush @live config-import -y

1

2

3

4

5

6

7

8

#execute some drush commands

echo "-----------------------------------------------------------"

echo "Exporting excluded config"

drush @live config-split-export -y excluded

echo "-----------------------------------------------------------"

echo "Importing configuration"

drush @live config-import -y

The drush command “drush @live config-split-export -y excluded” will export all webforms and contact forms created by your CMS administrators into the folder “excluded”. The “drush config-import” command will therefore not delete them and your administrators can happily create their custom forms.

Benefit of disable “excluded” on local environment

We usually disable the “excluded” environment on our local environment. This allows us to create complex webforms on our local machine for our clients and deploy them as usual. In the end you can have a mix of customer created webforms and your own webforms which is quite helpful.

Final note

The CMI is a great tool and I would like to thank the maintainers of the config_split module for their great extension. This is a huge step forward making Drupal 8 a real Enterprise CMS Tool.

If you have any questions, don’t hesitate to post a comment.

Apr 06 2017
Apr 06

Living in the middle of nowhere and working most of my hours in the evenings I have few opportunities to attend events in person, let alone deliver presentations. As someone who likes to share knowledge and present at events this is a problem. My work around has been presenting remotely. Many of my talks are available on playlist on my youtube channel.

I've been doing remote presentations for many years. During this time I have learned a lot about what it takes to make a remote presentation sucessful.

Preparation

When scheduling a remote session you should make sure there is enough time for a test before your scheduled slot. Personally I prefer presenting after lunch as it allows an hour or so for dealing with any gremlins. The test presentation should use the same machines and connections you'll be using for your presentation.

I prefer using Hangouts On Air for my presentations. This allows me to stream my session to the world and have it recorded for future reference. I review every one of my recorded talks to see what I can do better next time.

Both sides of the connection should use wired connections. WiFi, especially at conferences can be flakely. Organisers should ensure that all presentation machines are using Ethernet, and if possible it should be on a separate VLAN.

Tips for Presenters

Presenting to a remote audience is very different to presenting in front of a live audience. When presenting in person you're able to focus on people in the audience who seem to be really engaged with your presentation or scan the crowd to see if you're putting people to sleep. Even if there is a webcam on the audience it is likely to be grainy and in a fixed position. It is also difficult to pace when presenting remotely.

When presenting in person your slides will be diplayed in full screen mode, often with a presenter view in your application of choice. Most tools don't allow you to run your slides in full screen mode. This makes it more difficult as a presenter. Transitions won't work, videos won't autoplay and any links Keynote (and PowerPoint) open will open in a new window that isn't being shared which makes demos trickier. If you don't hide the slide thumbnails to remind you of what is coming next, the audience will see them too. Recently I worked out printing thumbnails avoids revealing the punchlines prematurely.

Find out as much information as possible about the room your presentation will be held in. How big is it? What is the seating configuration? Where is the screen relative to where the podium is?

Tips for Organisers

Event organisers are usually flat out on the day of the event. Having to deal with a remote presenter adds to the workload. Some preparation can make life easier for the organisers. Well before the event day make sure someone is nominated to be the point of contact for the presenter. If possible share the details (name, email and mobile number) for the primary contact and a fallback. This avoids the presenter chasing random people from the organising team.

On the day of the event communicate delays/schedule changes to the presenter. This allows them to be ready to go at the right time.

It is always nice for the speaker to receive a swag bag and name tag in the mail. If you can afford to send this, your speaker will always appreciate it.

Need a Speaker?

Are you looking for a speaker to talk about Drupal, automation, devops, workflows or open source? I'd be happy to consider speaking at your event. If your event doesn't have a travel budget to fly me in, then I can present remotely. To discuss this futher please get in touch using my contact form.

Share this post

Apr 05 2017
Apr 05

The pain

In the past we used a Drupal 7 multi-site powering at least 3 different sites at the same time with all our business logic bundled inside of various massive custom modules shared along all the sites and some of them with dependencies of external modules (like Message Broker) and each site was using a different version of these modules.

We were restricted to deploying the work of a large team every one or two weeks. When something broke because of the number of changes we’d just deployed together with everything, we were unable to immediately know the source of the issue and sometimes we had to wait till the next scheduled deployment in order to fix the problem.

We needed to undertake a sanity test of the whole site on every deployment because a simple core update could shut down the whole site or one change in the javascript could break all the apps in one go. Every time we felt like we were delivering a Pandora’s box.

The Dream

We still wanted a Drupal 8 site that deals with the content and users, and the other functionality is decoupled into small services with the most appropriate technology/framework for that service. This means that if a service becomes unavailable the other services will still be working and it is easier to spot the bug because the project is smaller. Obviously we’d rather nothing went wrong but inevitably it will, so being able to isolate an issue and possibly take one service down to fix is much better than having no site or apps available at all.

We wanted to deploy automatically more often and create less work and get instant feedback once our work is done.

We are moving parts of our workflow/apps to other services so we can focus on improving our expertise in CI/CD, using tools like platforms.sh, travis CI, Concourse CI, etc. and get experience from the PHP world and their best practices and try to apply them in our big apps, so we can improve our workflow.

Our way

Drupal profiles

We decided to create a profile that powers a basic config and modules to all our sites so in this way is easier to maintain/update all the sites

Continuous Delivery

Our last project was a symfony 3 app, for which we started implementing our new standard pipeline that looks like this on Concourse CI:

Pipeline

Basically on each pull request (PR) we run all the unit tests to make sure that everything looks “fine”. Once the PR gets merge it tries to deploy to a test environment and it does a full sanity test in order to guarantee that nothing breaks and then we repeat the same process to the staging environment. We usually let it deploy to production then too unless we are expecting an event that would, for instance, substantially increase traffic and in that case we have the control to delay and push it when we are ready.

All the way to production can be done in less than an hour and I’d dare to say that the release is ready in the exact moment you press the merge button.

Container ecosystem

All our projects have a docker-compose file with the same image as our site so everyone has the same environment no matter what operating system are they using. We also added all the necessary commands grouped by utility as composer scripts so it is easier to remember and we don’t need to create bin/bash files

TDD

We prefer to do TDD because our tests are like the specification of the task, so when it passes, it is done. In a non-TDD environment, the goal is usually not very clear and changes can be hard to understand and test.

For covering that we decided to experiment with PHPSpec, which is a great developer-friendly testing tool that encourages Test Driven Development and also creates the initial code structure for the functionality.

Conclusion

I think everyone in the team is happy with this new way of doing things because it gives us more security, flexibility, and the freedom to learn and refactor things without fear. We have improved our deployment timescale from weeks to minutes, which is a huge achievement.

Share this:

Like this:

Like Loading...

Pages

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web