Jan 30 2019
Jan 30

If you were an early Drupal 8 adopter you've might have downloaded and installed your Drupal 8 sites by downloading a tarball or using Drush. We did as well, but the benefits of using Composer are so great that it's time to convert those in to being Composer-managed.

Luckily, grasmash has built a great Composer plugin called Composerize Drupal which does all the heavy-lifting for us.

Here's how we did it:

Before you even begin, make sure you branch out $ git checkout -b chore/composerize-drupal

And then we installed the Composer plugin globally:

composer global require grasmash/composerize-drupal

Consider the plugin options available:

  • Use the --exact-versions option if the site is big and complex. Especially if you don't have any good test coverage to ensure your site doesn't break. The option sets the constraints of your composer.json to the exact versions of your currently downloaded modules.

Now we run the command:

composer composerize-drupal --composer-root=. --drupal-root=. --exact-versions

Next:

  • Update your .gitignore and ignore the vendor/, core/ and modules/contrib folders. If the files were already commited you also need to remove them: Ignore files that have already been committed to a Git repository
  • Re-apply your patches! Since all the core code and contrib. modules are managed by Composer you'll need to add those patches to your composer.json:
    Something like this:
    "extra": {
        "enable-patching": true,
        "patches": {
            "drupal/core": {
                "2492171 - Adds transliteration to uploaded file and images": "_kodamera/patches/use_new_transliteration-2492171-72.patch"
           }
       }
       ...

When you run composer install they are automatically applied for you. No more manually work here. Yay!

Do some regression testing and if everything looks fine, you're done! Commit and deploy :)

Jan 22 2019
Jan 22

[embedded content]

Over the years I’ve written a fair bit about Drupal and its modules, but all the videos and tutorials focused on a single module or topic.

So I decided to try something different and record a video where I build a whole website in a single go. I recorded the video in one night and only stopped recording to get a drink.

In this video, which is over 3 hours long, I’ll teach you how to build a basic directory website. We’ll start right at the beginning by setting up a local Drupal site for this we’ll use DDEV-Local. Then we create content types, create a sub-theme, create a few custom views, a search page, media management functionality and so much more.

I’ve broken out the video into sections below with timecodes and extra resources. For the content below to make any sense you should follow along by watching the video.

Enrollments are now open for the Drupal 8 Site Building Bootcamp: 7-week program, 2 live lectures per-week. Enroll NOW!

1. Set up Local Drupal Site

Time: 00:01:33 – 00:09:49

Download Drupal

We first need to download the Drupal codebase, run the following Composer command:

composer create-project drupal-composer/drupal-project:8.x-dev SITE_NAME --stability dev --no-interaction

Replace SITE_NAME with the name of the folder.

Tip: If you want to speed up Composer then install the prestissimo plugin. This is a Composer plugin and has nothing to do with Drupal.

Configure DDEV-Local

We’ll use DDEV-Local to run our local Drupal 8 site. It requires Docker to run, and you can get the install instructions from their documentation site.

Once you’ve installed DDEV-Local go to your Drupal site within the terminal and run:

ddev config

You’ll be prompted with a few options, and it’ll configure the environment.

MacOS Users: If you’re using macOS make sure you set webcache_enabled to true in the ddev config.yml.

Go to your Drupal codebase and open .ddev/config.yml and change:

# From:
webcache_enabled: false
# To:
webcache_enabled: true

Time: 00:06:58

Links:

2. Create Content types and Taxonomy Vocabularies

Time: 00:09:50 – 00:29:08

Just like on any Drupal site we need to build the data model: content types and taxonomy vocabularies.

Content Types

Listing:

  • Body
  • Email
  • Listing categories
  • Logo
  • Website

Blog:

  • Body
  • Comments
  • Featured image
  • Tags
  • Blog categories

Taxonomy Vocabularies

  • Listing categories
  • Blog categories

Links:

3. Modify Content types

Time: 00:29:08 – 00:41:20

Once the content types have been created we’ll need to modify them. For this, we’ll use Display Suite.

To install Display Suite, run the following command:

composer require drupal/ds

If you’re keen to learn more about Display Suite check out our following tutorial series:

  1. How to Customize Content Pages
  2. How to Use Display Suite Fields
  3. How to Use Switch View Mode Sub-module
  4. Webinar: Customize Content Pages using Display Suite in Drupal 8

4. Create Bootstrap Sub-theme

Time: 00:41:22 – 01:01:40

We’ll use the Bootstrap theme on the site, and we’ll create a basic CDN sub-theme.

If you need step-by-step instructions on creating a Bootstrap theme, then read our “Getting Started with Bootstrap in Drupal 8“.

Install the theme using this command:

composer require drupal/bootstrap

Please note: The Bootstrap theme (as of this writing), only supports Bootstrap 3, not 4. If you need a Bootstrap 4 theme look at Barrio or Radix.

We have a tutorial on Barrio called “Getting Started with Bootstrap 4 using Barrio in Drupal 8“.

Bootstrap Layouts

The Bootstrap Layouts module ships a bunch of prebuilt layouts for Drupal 8. We’ll use these layouts in Display Suite.

composer require drupal/bootstrap_layouts

If you want to learn more about Bootstrap Layouts, then check out our tutorial “How to Implement Layouts using Bootstrap Layouts in Drupal 8“.

Links:

5. Block and Menu System

Time: 01:01:42 – 01:15:03

Once we’ve created our sub-theme, we’ll create four new footer regions.

Add the following into your theme’s .info.yml:

regions:
  footer_one: 'Footer one'
  footer_two: 'Footer two'
  footer_three: 'Footer three'
  footer_four: 'Footer four'

Add the following into page.html.twig (make sure you override the Twig file):

<div class="footer footer-grid {{ container }}">
    <div class="row">
        <div class="col-sm-3">
            {{ page.footer_one }}
        </div>
        <div class="col-sm-3">
            {{ page.footer_two }}
        </div>
        <div class="col-sm-3">
            {{ page.footer_three }}
        </div>
        <div class="col-sm-3">
            {{ page.footer_four }}
        </div>
    </div>
</div>

6. Views

Time: 01:15:03 – 01:38:10

We need to create a few custom Views for our website. The first one, which lists blog content is fairly simple.

The second, which is “My listing” is complicated because you have to deal with contextual filters.

Read our tutorial “Add Custom Tab to User Profile Page with Views in Drupal 8” for a step-by-step tutorial on implementing this type of View.

7. Build Search page using Search API

Time: 01:38:10 – 02:10:32

We’ll use the Search API and Facets module to build our custom listing search page.

Download the required modules using the following command:

composer require drupal/search_api drupal/facets

Watch our webinar “How to Build Custom Search Pages in Drupal 8” which covers the core Search module and Search API.

Links:

8. Media Management

Time: 02:10:55 – 02:30:54

We now need to add media handling functionality to the directory site.

Run the following Composer command to download the required modules:

composer require drupal/entity_embed drupal/ctools drupal/entity_browser drupal/inline_entity_form

For a detailed tutorial on configuring all this stuff and more go to “Managing Media Assets using Core Media in Drupal 8“. And there’s a video: “Live Training: Managing Media Assets using Core Media in Drupal 8“.

9. Roles and Permissions

Time: 02:30:56 – 02:51:10

Now we need to create a role called “Contributor” and configure its permissions.

To allow users to publish/unpublish listings, you’ll need to use Override Node Options.

Install it using the command below:

composer require drupal/override_node_options

The “Contributor” role needs the following permissions:

  • Use the Contributor HTML text format
  • Image: Create new media
  • Image: Delete own media
  • Image: Edit own media
  • Listing: Create new content
  • Listing: Delete own content
  • Listing: Edit own content
  • View own unpublished content
  • Override Listing published option

Create Registration Page

To create a registration page, we’ll use Multiple Registration.

Run this command to install it:

composer require drupal/multiple_registration

Read our “Create Individual Registration Forms using Multiple Registration in Drupal 8” for a detailed tutorial on the module.

Links:

10. Paragraphs

Time: 02:51:10 – 03:06:45

We’ll use the Paragraphs module to allow an editor to add a Bootstrap Jumbotron to a page.

Install the module by running:

composer require drupal/paragraphs

If you want to learn more about Paragraphs, then check out our free course, “Build Edge-to-edge Sites using Paragraphs in Drupal 8“.

Links:

11. Webform

Time: 03:06:47 – END

We’ll use the Webform module to build functionality which sends an email to the owner of the listing.

You can install Webform by running:

composer require drupal/webform

Below is the token which is used:

[webform_submission:submitted-to:author:mail]

Links:

Summary

I don’t expect many people to make it to the end of the video but if you did, congratulations! I hope you learnt something new by seeing how a Drupal site is built.

We can often learn a lot just by watching a developer build something.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Oct 16 2018
Oct 16

[embedded content]

Video sections

Bootstrap is a powerful front-end framework which helps you build sites and web applications faster by offering prebuilt CSS and JavaScript components.

Some of the CSS components it offers are a grid system, buttons, navigation, jumbotron and so much more. On the JavaScript side, it comes with a few useful items such as a modal, collapsible divs, carousel to name a few. Read the Bootstrap documentation to find out more.

Bootstrap in Drupal

If you search for “drupal bootstrap” in Google, the first result will likely be the Bootstrap theme. This theme is the most popular on drupal.org with over 150,000 reported installs. But as of this writing, the theme only supports Bootstrap 3, not version 4 which is the latest.

So if you want to use Bootstrap 4 you’ll need to use another theme until the Bootstrap theme supports version 4.

In this tutorial, you’ll learn how to configure and use the Drupal 8 version of the Barrio theme which uses Bootstrap 4.

Getting Started

Before we can begin, go download the Barrio theme.

Using Composer:

composer require drupal/bootstrap_barrio

Create Sub-theme

The recommended way of using a theme in Drupal is to first create a sub-theme. You’ll never want to use Barrio directly. If you need to customize how things look, i.e., change CSS and override templates, then do it in the sub-theme and not Barrio itself, this way you can keep Barrio up-to-date.

If you want to learn more about sub-themes in general. Check out the Creating a Drupal 8 sub-theme, or sub-theme of sub-theme documentation page.

Let’s now look at creating a sub-theme which pulls Bootstrap via a CDN, this is the quickest and easiest way to get started.

Create CDN Sub-theme

1. Go to the bootstrap_barrio directory and copy the subtheme directory into /themes.

2. Change the subtheme directory name to anything you want, I’ll change it to barrio_custom (everywhere you see barrio_custom, use your actual sub-theme name).

Now we need to go through the sub-theme and replace bootstrap_barrio_subtheme to barrio_custom.

3. Rename the following files:

  • _bootstrap_barrio_subtheme.theme -> barrio_custom.theme
  • bootstrap_barrio_subtheme.info.yml -> barrio_custom.info.yml
  • bootstrap_barrio_subtheme.libraries.yml -> barrio_custom.libraries.yml
  • config/install/bootstrap_barrio_subtheme.settings.yml -> config/install/barrio_custom.settings.yml
  • config/schema/bootstrap_barrio_subtheme.schema.yml -> config/schema/barrio_custom.schema.yml

4. Open  barrio_custom.info.yml change the name of the sub-theme and rename the libraries section from bootstrap_barrio_subtheme to barrio_custom.

From this:

To this:

5. Open barrio_custom.theme (formerly_bootstrap_barrio_subtheme.theme) and change the function name:

// From:
bootstrap_barrio_subtheme_form_system_theme_settings_alter(&$form, FormStateInterface $form_state)
// To:
barrio_custom_form_system_theme_settings_alter(&$form, FormStateInterface $form_state)

6. Open config/schema/barrio_custom.schema.yml (formerly config/schema/bootstrap_barrio_subtheme.schema.yml) and change the following:

# from:
bootstrap_barrio_subtheme.settings:
# to:
barrio_custom.settings:

7. Open color/color.inc file and change the following:

// From:
'preview_library' => 'bootstrap_barrio_subtheme/color.preview',
// To:
'preview_library' => 'barrio_custom/color.preview',

8. Last but not least, open js/global.js and change the following:

// Change this:
Drupal.behaviors.bootstrap_barrio_subtheme = {
// To this:
Drupal.behaviors.barrio_custom = {

Now that you’ve created a sub-theme go to the Appearance page in your Drupal site and install your sub-theme by clicking on “Install and set as default”.

The front-end of your Drupal site should look something like the image below:

The actual Bootstrap 4 library is being loaded via a CDN which is the default behavior in the Barrio sub-theme and that’s why everything works without downloading the library locally. Of course, this can be changed and we’ll look at how to do that next.

Download Local Version of Bootstrap

In the sub-theme we created above, we’re pulling in Bootstrap through the CDN. Let’s configure it now to use a local version of Bootstrap.

1. Go to the Download page and click on Download in the “Compiled CSS and JS” section.

2. Extract the zipped file into /libraries directory in your Drupal site and rename the folder to bootstrap. 

The path to the css and js folder should be /libraries/bootstrap/css and /libraries/bootstrap/js.

3. Open up *.libraries.yml in your sub-theme, mine is called barrio_custom.libraries.yml and change the bootstrap section like so:

From this:

bootstrap:
  js:
    /libraries/popper/popper.min.js: {}
    /libraries/bootstrap/dist/js/bootstrap.min.js: {}
  css:
    component:
      /libraries/bootstrap/dist/css/bootstrap.min.css: {}

To this:

bootstrap:
  js:
    /libraries/bootstrap/js/bootstrap.bundle.min.js: {}
  css:
    component:
      /libraries/bootstrap/css/bootstrap.min.css: {}

Bootstrap 4 has two dependencies Popper and jQuery. Drupal comes with jQuery already and to save a bit of effort will use the bootstrap.bundle.min.js which comes with Popper whereas, bootstrap.min.js doesn’t.

4. Open the *.info.yml file in your sub-theme and change barrio_custom/bootstrap_cdn under libraries to barrio_custom/bootstrap.

From this:

libraries:
  - barrio_custom/bootstrap_cdn
  - barrio_custom/global-styling

To this:

libraries:
  - barrio_custom/bootstrap
  - barrio_custom/global-styling

5. Rebuild the site cache by running drush cr or drupal cache:rebuild or by going to /admin/config/development/performance and clicking on “Clear all caches”.

If the JavaScript or CSS file isn’t loading or the site looks broken. Make sure the paths in the *.libraries.yml file is correct and that the Bootstrap library is correctly in the /libraries directory.

Theme Settings

The Barrio theme allows you to configure a lot through the Settings page, which you can access by clicking on the Settings link after you’ve activated the theme. I won’t go through absolutely everything, however, I’ll mention some important settings.

To access the settings page, click on Appearance in the toolbar and Settings next to the installed theme.

Layout Settings

From the Layout tab, you can configure aspects of the layout such as the type of container, i.e., fluid or non-fluid and how wide the sidebar columns should be.

If you select “Fluid container”, then your website will be full-width with gutters on each side.

The “Sidebar first layout” and “Sidebar second layout” let you configure the width of each sidebar.

Components Settings

From the Components tab, you can configure the buttons and the navbar.

The most important section in this tab is the navbar. It lets you configure it without having to modify any CSS or SASS files.

Affix Settings

In this section, you can configure components like the navbar or sidebar to be affixed to the top when scrolling.

If you check “Affix navbar”, it’ll stick the navbar to the top as you scroll down the page.

Here’s an example:

Scroll Spy

In this area, you can configure the Scrollspy functionality in your Drupal Bootstrap site.

Fonts Settings

From the Fonts tab, you can configure what fonts will be used and icon set.

Colors

And finally, from the Colors tab, you can configure the color of the messages and how tables are displayed.

Color Scheme

One thing I do like about Barrio which is different to the Bootstrap theme is the ability to change the color scheme directly in the theme.

Now, this isn’t groundbreaking and Drupal’s had this ability for a long time, but being able to easily change colors without modifying CSS or compiling SASS is a nice touch.

However, there is a limitation on which colors can be changed. You can’t change Bootstrap colors: primary, secondary, success, danger, warning or info from the color scheme section.

So that is a quick overview of the settings page. My guess is you’ll spend most of your time configuring the navigation bar and grid layout. But do spend some time familiarizing yourself with the options available.

Bootstrap Library

Another way to load Bootstrap is by using the Bootstrap Library module. One of its benefits is the ability to change which version of the CDN library you want to use without modifying the *.libraries.yml file in your sub-theme.

1. Start things off by first downloading the module and then installing it.

composer require drupal/bootstrap_library

2. Go to the Settings page of your sub-theme and scroll to the bottom and from “Load library” choose how you want the library to be loaded.

  • CDN: This will load Bootstrap through a CDN.
  • Local non minimized (development): This will use the non minified version stored in the /libraries directory
  • Local minimized (production): This will use the minified version stored in the /libraries directory.

As long as you’ve downloaded Bootstrap and added it into the /libraries directory as explained early, the “Local non minimized” and “Local minimized” should work.

Bootstrap Library Settings

The Bootstrap Library module comes with a configuration page, just go to Configuration and click on Bootstrap Library.

If you choose None from “Load library” on the theme settings page, then whichever way Bootstrap is configured to load from this page is what will be implemented.

From this “Load Boostrap from CDN” section, you can choose if a CDN is used and which version. If you prefer to load Bootstrap locally, then select “Load locally”.

Then from “Minimized, Non-minimized, or Composer version” select which local version you want to use.

Form “Theme visibility” select which themes will have Bootstrap loaded.

Make sure you select your theme from the multi-select or the library will not load and your site will be broken.

From the “Activate on specific URLs” section, you can include or exclude the library on specific paths.

From the “Files settings” you can choose if you want CSS and/or JavaScript files loaded.

If you decide to use Bootstrap Library to load the library then make sure your sub-theme isn’t loading the library itself because you’ll end up loading the library twice.

Remove any library declarations from the *.info.yml file in your sub-theme. This will stop your sub-theme from loading the library.

Rebuild the site cache and you’re good to go.

Summary

The current state of Bootstrap in Drupal 8 is this. If you need to use Bootstrap 3, then use the Bootstrap theme. If, however, you want to use Bootstrap 4 then look at Barrio, Bootstrap4BootBase or Radix.

If you know of any good Bootstrap 4 base themes then please leave a comment.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Oct 02 2018
Oct 02

[embedded content]

Watch other videos on our YouTube channel. Click here to subscribe.

I was recently looking at all the default views that come with Drupal 8. For people who don’t know, the Views module is part of Drupal 8 core. In Drupal 7 and below it’s the most installed module so during Drupal 8’s development it was decided to move Views into core.

During my exploration into all of the default Views, I noticed that in the People (User) view there was a filter called “Combine fields filter”.

Want to learn about Views? Read Build a Blog in Drupal 8: Using Views or watch it as part of our FREE Drupal 8 Site Building course.

Now just a quick side note, if you’re new to Drupal and Views I’d highly recommend you spend time walking through all of the default views and see how they were configured. You can learn a lot just by seeing how things are set up.

The “Combine fields filter” does a pretty cool thing. It allows you to search across multiple fields or put another way, it allows you to combine fields and then filter by their combined value.

How to use “Combine Fields Filter”

Using this filter is relatively straightforward. Just click on Add in the Filter criteria field-set. Search for the filter by name or select Global from the Category drop-down.

When configuring the filter, you can select which fields you want to search from the “Choose fields to combine for filtering” drop-down.

If you want to see what the actual query looks like, turn on “Show the SQL query” from the Settings page (admin/structure/views/settings).

Then in the preview area, you should see the query that gets generated.

The above example is from the “People (User)” view.

Summary

If you want to add basic filtering across fields to your views, then this is the way to go. It’s useful for those custom admin pages which we create to help editors manage content. If you’re looking for something more advanced such as keyword searching, then look at using Search API.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Sep 05 2018
Sep 05

[embedded content]

Drupal has got new media management functionality in 8.6. In the above video, I’ll demonstrate what new media functionality we have in Drupal 8.6.

Thanks to the Media in Drupal 8 Initiative, media handling in Drupal has improved with every new release. In 8.4 we got the experimental core Media module. Then in 8.5, the module moved from experimental to stable and now it’s the recommended way for storing media assets. Now in Drupal 8.6 we get a few extra goodies such as oEmbed support, a Remote video media type and a media library.

Grab our FREE course on using core Media in Drupal 8.
(While you’re at it check out our list of free courses)

New Remote Video Media Type

When you install the Media module you get four media types by default: Audio, File, Image and Video. Now you get Remote video which can be used to embed YouTube and Vimeo videos.

Currently only YouTube and Vimeo are supported. If you need to support other video platforms look at using Video Embed Field.

Learn how to configure Video Embed Field with core Media module.

oEmbed Support

To handle the new Remote video media type, Drupal 8.6 also got oEmbed support (Drupal change record).

Media Library

The new Media library module is by far the most exciting new functionality. You’ll need to install the experimental module called Media library. Don’t forget this is experimental for now; use at your own risk.

The first change you’ll notice is that the Media page looks different. You get to switch between a Grid and Table view.

The module also comes with a new widget for the Media field called “Media library”. This will allow you to browse media assets from the edit screen.

To use this widget make sure “Media library” is selected as the widget on the “Manage form display” page.

Here it is in action and watch how you can bulk upload images.

Can this module replace Entity browser? At this point I’d say no. Entity browser gives you more flexibility in how you build these types of browse pages. But I hope in the future Media library will be the standard way of creating these browse pages.

Want to learn how to use Entity browser? Check out “How to Browse Media Assets using Entity Browser“.

Summary

Slowly and steadily media handling in Drupal core is improving and becoming more powerful. It’s good to see this continued momentum. But if you need to create bespoke media functionality I still think the combination of Entity embed/Entity browser is the winner for now.

Check out our epic “Managing Media Assets using Core Media in Drupal 8” tutorial where you’ll learn how to use Entity embed, Entity browser, DropzoneJS and more.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Aug 23 2018
Aug 23

[embedded content]

Drupal Console and Drush are two command line (CLI) tools built for Drupal. For a long time Drush was the only CLI tool and it was very useful for managing Drupal sites. Common tasks you’d do with Drush are rebuild caching, installing sites, import/export configuration and so much more.

Then Drupal Console came onto the scene and offered other goodies such as the ability to generate boilerplate code, which Drush 9 can now do as well. People often ask “Can you run Drush and Drupal Console together” and the answer is yes, I personally use both. If you install Drupal using drupal-composer/drupal-project then you get both Drush and Drupal

In the video above, you’ll learn how to use Drush and Drupal Console.

Drupal Console

Drupal Console is a CLI tool for Drupal built using the Symfony Console library. One of its many strengths is the ability to generate boilerplate code. With a few simple commands you can create a module or a custom entity type.

How to Install Drupal Console

Drupal Console can be installed into your Drupal site using Composer, follow the instructions over on the Drupal Console documentation page.

Use the links below to jump to a section of the video:

  • Drupal Console intro: 02:31
  • How to install Drupal Console: 04:29
  • Using Drupal Console launcher: 06:27
  • Overview of Drupal Console commands: 07:41
  • How to use debug commands: 08:33
  • Using debug:router command: 10:11
  • Using debug:container command: 11:49
  • Using debug:cron command: 15:08
  • Using debug:user command: 15:08
  • How to use generate commands: 17:15
  • Using generate:module command: 18:07
  • View module code generated by Drupal Console: 20:42
  • Using generate:entity:content command: 20:59
  • View code generated by generate:entity:content command: 24:43
  • Install module generated by Drupal Console: 27:21
  • How to download modules using Drupal Console: 31:10
  • Viewer question: What’s composer?: 34:45

Drush

Drush is the original CLI tool for Drupal. You can use it to interact with a Drupal site and generate boilerplate code.

How to Install Drush

Drush is just a PHP library and can be installed using Composer. For details read the Drush install documentation page.

Use the links below to jump to a section of the video:

  • Drush intro: 37:22
  • How to install Drush: 40:56
  • Using Drush: 42:11
  • Overview of Drush Commands: 43:00
  • Using Drush generate command: 44:11
  • Using generate module-content-entity command: 45:12
  • View module code generated by Drush: 46:23
  • Install module generated by Drush: 47:13
  • Overview of common Drush commands: 52:00
Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Jul 31 2018
Jul 31

The Superfish module allows you to create multi-level dropdown menus in Drupal 8. The module uses the JavaScript Superfish library to create and display a Superfish menu block for each menu available on your site.

With a few configuration options, you can control how it’ll behavior on mobile, turn multi-column menus, change the styling and more.

The module does come with a few styling options but you’ll have to style it yourself to match your theme. When you configure Superfish the first time the dropdown functionality will, however, it may not look good.

In this tutorial, you’ll learn how to install the module and how to configure it.

Let’s start!

Getting Started

It’s recommended that you install the module using Composer. This way Composer will automatically download the required library.

composer require drupal/superfish

Installing the Superfish module

If you don’t want to use Composer then you’ll need to manually download the library and extract it into the libraries directory in your Drupal site. View the Installation section on the project page for more details.

After downloading, enable the module either by clicking the module’s checkbox in the Extend Page or using Drush.

drush en superfish -y

Enabling the moduel with drush

Create Menu

As an example, let’s create a site with a 4 level menu. Superfish is capable of handling unlimited menu levels.

1. Click Structure, Menus and click the “Edit menu” button besides the “Main Navigation” menu.

2. Click the “Add link” button to add a menu item and make sure you mark the “Show as expanded” checkbox, if the menu item will contain children.

Creating the menu

3. Click the Save button each time you create a menu item and repeat the process for the whole menu structure. At the end you should have something like this:

Menu structure

Configure Superfish Menu Block

It’s time to get rid of the default menu block and place the Superfish menu block in one of Drupal’s regions.

1. Click Structure, “Block layout”. Look for the “Primary menu” region, click the dropdown button by the “Main Navigation” block and choose Remove. Confirm by clicking the blue Remove button.

Removing the main menu block

2. Now click “Place block” in the “Highlighted” region, type the word main in the search box and click “Place block” by the Superfish “Main navigation” block.

Inserting the Supefish block

Let’s review the settings one by one.

  • Uncheck the “Display title” option. This is Drupal’s default block configuration.
  • “Menu levels” option lets you configure the display of the menu and the number of levels permitted. Leave these options untouched.
  • “Block settings” area contains configuration about the menu appearance and its basic behavior.
  • “Menu Type” “Horizontal (double row)” shows the first and second level menu items in a horizontal display.
  • “Vertical (stack)” option is the right choice if you want to place the menu in one of the sidebars for example.
  • The style option lets you choose a menu with some predefined styles.
  • The “Slide in” effect option is there to configure the dropdown functionality of all submenus. You can install the jQuery Easing library (read the module’s documentation) if you want to have more options here.

Superfish settings

  • The “Superfish plugins” area deals with the display of the dropdown menus in the browser and on small screens, take a look at the possibilities, but generally speaking you won’t need to change this options, leave the default options. Pay attention to the Supersubs area, you can define here the width of your submenus.

Supefish plugins area

  • In the “Advanced Settings” area, you can control the animation speed of the Superfish menu, some hyperlink properties and you can also add custom CSS classes to the menu components.

Superfish advanced settings

Create Multi-column Menus

It is possible to create multi-column menus with the Superfish module, however you have to have a specific menu structure to achieve this.

Wrong menu structure

Right menu structure

Just check “Enable multi-column sub-menus” in order to use them.

Multi-column menus setting

Summary

The Superfish module allows you to build dropdown and multi-column menus and place them in your Drupal site in an easy way. But you’ll still need to spend some time styling the menus to match your theme.

Jorge Montoya

About Jorge Montoya

Jorge has been reading about Drupal and playing with it for almost 5 years. He likes it very much! He also likes to translate German and English documents into Spanish. He's lived in some places. Right now, he lives in the city of Medellín in his homeland Colombia.

Jul 17 2018
Jul 17

[embedded content]

In the video above, you’ll learn how to build powerful media management functionality using Drupal 8.5. I start the webinar with a review of what’s new in Drupal 8 and then jump right into a live demo.

If you prefer text, then read our tutorial on “Managing Media Assets using Core Media in Drupal 8“.

Modules

In the video we use the following modules:

Below is a time coded list of all the sections in the video. Just click on the time code and it’ll jump to that part of the video.

Video Sections

  • Agenda: (00:41)
  • What’s new in Drupal 8: (01:35)
  • Required Modules: (09:53)
  • Demo time: (11:02)
  • Install Media module: (11:49)
  • How to Upload and view assets: (12:34)
  • Overview of Media types: (14:42)
  • Create remote video media type: (15:34)
  • Field mapping in media types: (17:45)
  • Create remote video asset: (18:52)
  • Customize remote video formatters: (19:48)

Media Field

  • Attach media field to Article content type: (20:24)
  • Using Inline Entity Form on media field: (22:53)
  • Install Inline Entity Form: (23:27)
  • Configure IEF on media field widget: (24:46)

Entity Embed

  • Install Entity Embed: (27:44)
  • Create embed button: (28:37)
  • Configure text format for embed button: (29:40)
  • Testing embed button: (32:27)

Entity Browser

  • Install Entity Browser: (35:50)
  • Create entity browser: (38:21)
  • Integrating entity browser and entity embed: (39:45)
  • Create view for entity browser: (41:02)
  • Add IEF to entity browser: (47:35)

Entity Browser and DropzoneJS

  • Add dropzonejs to entity browser: (51:59)
  • Customize fields on media types using form modes: (54:26)

Drupal 8.6

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Jun 26 2018
Jun 26

The Block field module lets you insert a Drupal block as a field on your content.

A Drupal theme is divided into regions and you can place blocks or your own custom blocks into these regions. You accomplish this task by dragging and ordering blocks in the “Block Layout” screen. That means you can append blocks before or after the main content of your content type. This “Block Layout” screen will soon be cluttered if you have multiple content types and/or multiple single nodes, each one with a different custom block. Block Layout Screen

However, there’s a way to insert a block (or many blocks) directly into your content as a field. Thus, you don’t have to place the block in the “Block Layout” screen, instead, you insert the block as a field on the node.

Blocks as fields

In this tutorial, we’re going to cover the usage of the Block field module. Let’s start!

Installing the Block Field module

The first thing you’ll need to do is install the Block field module.

Using Composer:

composer require drupal/block_field

This module has no dependencies, so just install it and you’re good to go.

Enable Block Field module

Add Block Field to Content Type

Let’s start by adding a block field to the Article content type.

1. Go to Structure, “Content types”, click “Manage fields” on the Article now, then click on “Add field”.

2. Select “Block (plugin)” under the reference category and give it a proper label.

Adding a block field

3. Leave the “Allowed number of values” set to 1 and click “Save field settings”.

On the edit screen, you can add a default value for this field. There’s also a list of all blocks, which you can select or deselect in order to make them available to this particular field (custom blocks will appear here too). Click “Save settings” once again.

Availbale Blocks

Test Block Field

Click Content, “Add Content”, Article in order to create a test article. Choose a system block (for example the “Powered by Drupal” block) from the drop-down and click Save.

Creating content with a block field

You’ll see the selected block inserted as a field in the content. This block won’t appear in the “Block Layout” screen.

Block field inside the content

You can also use this module to insert a view via a block inside the content. Edit the article and select the “Who’s online” block under the “Lists (Views)” category, click Save and you will see this block in your content.

Please note: you won’t be able to use Views contextual filters with the Block Field module.

Display a view via a block in a field

Summary

The Block Field module lets you insert not only block views into a field but all types of blocks, even custom blocks. This allows the creation of complex content types and/or nodes and provides great flexibility when building a site.

Jorge Montoya

About Jorge Montoya

Jorge has been reading about Drupal and playing with it for almost 5 years. He likes it very much! He also likes to translate German and English documents into Spanish. He's lived in some places. Right now, he lives in the city of Medellín in his homeland Colombia.

Jun 11 2018
Jun 11

Three months ago I wrote an article on how to Create Image Styles and Effects programmatically and today we're following up on that article but introducing on how we can do that dynamically.

So, essentially what we would like to do is that we display an image, where we can adjust the way the image is outputted, given a height, width or aspect ratio etc.

Please bear in mind that all code provided in this article are experimental and does not yet cover things like access control, etc in this part.

Let's take a look at the service Unsplash.com. Its basically a free image bank with high quality images submitted by awesome freelancers and professionals that you can use for free.

Lake Tahio

Image by Eric Ward

The URL for the image above is the following:

https://images.unsplash.com/photo-1499365094259-713ae26508c5?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=26d4766855746c603e3d42aaec633144&auto=format&fit=crop&w=500&q=60

The parts we're actually interested in are: &auto=format&fit=crop&w=500&q=60 we can adjust them as we like and the image is displayed differently, i.e. changing the width of the earlier image to a smaller one:

Lake Tahio smaller

Alright, that's what we would like to do in Drupal 8. This article will be very iteratively, we'll rewrite the same code over and over until we get what we want. We'll notice issues and problems that we will deal with through out the article.

Prepare an environment to work in

We'll use a fresh Drupal 8.6.x installation.

To quickly scaffold some boilerplate code I'm going to use Drupal Console.

First let's create a custom module where we can put our code and logic in:

$ vendor/bin/drupal generate:module

I'll name the module dynamic_image_viewer

dynamic_image_viewer.info.yml

name: 'Dynamic Image Viewer'
type: module
description: 'View an image dynamically'
core: 8.x
package: 'Custom'

Next we need some images to work with, we'll use the core Media module for that. So let's enable that module:

vendor/bin/drupal module:install media

Now we can add some images. Go to Content >> Media >> Add media.

Media content

Implementing a Controller to display the image

The first step is to create a controller that will render the Media image to the browser. Again we'll use Drupal Console for a controller scaffold: vendor/bin/drupal generate:controller

We'll create a route on /image/{media} where Media will accept an media ID that due to Drupals parameter upcasting will give us a media instance in the controller method arguments. Doing this, if a invalid media ID is passed in the URL a 404 page is shown for us. Neat!

So we'll modify the generated controller slightly to this:

src/Controller/ImageController.php

<?php

namespace Drupal\dynamic_image_viewer\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\media\MediaInterface;

/**
 * Class ImageController.
 */
class ImageController extends ControllerBase {

  /**
   * Show an image.
   *
   * @param MediaInterface $media
   *
   * @return array
   */
  public function show(MediaInterface $media) {
    return [
      '#type' => 'markup',
      '#markup' => $media->id(),
    ];
  }

}


And the routing file looks like this: dynamic_image_viewer.routing.yml

dynamic_image_viewer.image_controller_show:
  path: '/image/{media}'
  defaults:
    _controller: '\Drupal\dynamic_image_viewer\Controller\ImageController::show'
    _title: 'show'
  requirements:
    _permission: 'access content'

If we install the module, vendor/bin/drupal module:install dynamic_image_viewer and hit the URL /image/1 we should see a page with the ID being outputted.

Render the original image

Ok. Currently nothing is rendered, so what we'll do is that we render the uploaded original image first.

To serve the file we'll use BinaryFileResponse. So let's update the ImageController::show method.

We'll also import the class in the top of the file:

use Symfony\Component\HttpFoundation\BinaryFileResponse;

  /**
   * Show an image.
   *
   * @param MediaInterface $media
   *
   * @return BinaryFileResponse
   */
  public function show(MediaInterface $media) {
    $file = $media->field_media_image->entity;

    $uri = $file->getFileUri();
    $headers = file_get_content_headers($file);

    $response = new BinaryFileResponse($uri, 200, $headers);

    return $response;
  }

So what we do here is that we grab the File entity from the field_media_image field on the Media image bundle. We get the URI and, using the file_get_content_headers we get the proper headers. Finally we serve the file back with the proper headers to the viewer.

And if we hit the URL again:

original-image-rendered

Before we continue, we should note some things that we'll get back to later:

  • What if the media ID is not a Media image?
  • The user can still access the media even if its unpublished.
  • What about cache?

Let's make a hard-coded image derivative

To modify the image, we'll create a new instance of ImageStyle and add an image effect.

Let's update the ImageController::show method again:

  /**
   * Show an image.
   *
   * @param MediaInterface $media
   *
   * @return BinaryFileResponse
   */
  public function show(MediaInterface $media) {
    $file = $media->field_media_image->entity;

    $image_uri = $file->getFileUri();

    $image_style = ImageStyle::create([
      'name' => uniqid(), // @TODO This will create a new image derivative on each request.
    ]);
    $image_style->addImageEffect([
      'id' => 'image_scale_and_crop',
      'weight' => 0,
      'data' => [
        'width' => 600,
        'height' => 500,
      ],
    ]);

    $derivative_uri = $image_style->buildUri($image_uri);

    $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri);

    $response = new BinaryFileResponse($derivative_uri, 200);

    return $response;
  }

So what we do here is that we create a new ImageStyle entity, but we don't save it. We give it a unique name (but we'll change that soon) and then add we add an image effect that scale and crops the image to a width of 600 and height 500.
And then we build the derivate uri and if the file exists already, we'll serve it and if not we'll create a derivative of it.

There is one big problem here. Since we use a unique id as name of the image style we'll generate a new derivative on each request which means that the same image will be re-generated over and over. To solve it for now, we could just change the

 $image_style = ImageStyle::create([
      'name' => uniqid(), // @TODO This will create a new image derivative on each request.

to a constant value, but I left it for that reason intentionally. The reason is that I want to explicitily tell us that we need to do something about that and here is how:

If we look back at the URI from Unsplash earlier &auto=format&fit=crop&w=500&q=60, these different keys are telling the code to derive the image in a certain way.

We'll use the provided keys and combine them some how in to a fitting name for the image style. For instance, we could just take the values and join them with a underscore.

Like so:

format_crop_500_60 and we'll have a unique string. If the user enters the same URL with the same parameters we'll be able to find the already existing derivative or if its another image, we'll create a derivative for it.

You'll also notice that I removed the $headers = file_get_content_headers($file); it is because those headers are not the correct ones for ur derivatives, we'll add them back soon.

Dynamic width and height values

On our second iteration of the code we'll now add the width and height parameters, and we'll also change the name of the image style to be dynamic.

Again, we'll update ImageController::show

We'll also import a class by adding use Symfony\Component\HttpFoundation\Request; in the top of the file.

  /**
   * Show an image.
   *
   * @param Request $request
   * @param MediaInterface $media
   *
   * @return BinaryFileResponse
   */
  public function show(Request $request, MediaInterface $media) {

    $query = $request->query;

    $width = (int) $query->get('width', 500);
    $height = (int) $query->get('height', 500);

    // We'll create the image style name from the provided values.
    $image_style_id = sprintf('%d_%d', $width, $height);

    $file = $media->field_media_image->entity;

    $image_uri = $file->getFileUri();

    $image_style = ImageStyle::create([
      'name' => $image_style_id,
    ]);
    $image_style->addImageEffect([
      'id' => 'image_scale_and_crop',
      'weight' => 0,
      'data' => [
        'width' => $width,
        'height' => $height,
      ],
    ]);
    
    // ... Rest of code

First we updated the method signature and injected the current request. Next, we'll get the width and height parameters if they exist and if not we fallback to something. We'll build an image style name of these dynamic values. With this we updated the name of the ImageStyle instance we create which makes sure that we can load the same derivative if the user hits the same URL. Finally we updated the width and height in the image effect.

Here is the updated ImageController::show and current file:

src/Controller/ImageController.php

<?php

namespace Drupal\dynamic_image_viewer\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\media\MediaInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Drupal\image\Entity\ImageStyle;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Image\ImageFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Class ImageController.
 */
class ImageController extends ControllerBase {

  /**
   * The image factory.
   *
   * @var \Drupal\Core\Image\ImageFactory
   */
  protected $imageFactory;

  /**
   * Constructs a ImageController object.
   *
   * @param \Drupal\Core\Image\ImageFactory $image_factory
   *   The image factory.
   */
  public function __construct(ImageFactory $image_factory) {
    $this->imageFactory = $image_factory;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('image.factory')
    );
  }
  /**
   * Show an image.
   *
   * @param Request $request
   * @param MediaInterface $media
   *
   * @return BinaryFileResponse
   */
  public function show(Request $request, MediaInterface $media) {

    $query = $request->query;

    $width = (int) $query->get('width', 500);
    $height = (int) $query->get('height', 500);

    $image_style_id = sprintf('%d_%d', $width, $height);

    $file = $media->field_media_image->entity;

    $image_uri = $file->getFileUri();

    $image_style = ImageStyle::create([
      'name' => $image_style_id,
    ]);
    $image_style->addImageEffect([
      'id' => 'image_scale_and_crop',
      'weight' => 0,
      'data' => [
        'width' => $width,
        'height' => $height,
      ],
    ]);

    $derivative_uri = $image_style->buildUri($image_uri);

    $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri);

    $headers = [];

    $image = $this->imageFactory->get($derivative_uri);
    $uri = $image->getSource();
    $headers += [
      'Content-Type' => $image->getMimeType(),
      'Content-Length' => $image->getFileSize(),
    ];

    $response = new BinaryFileResponse($uri, 200, $headers);

    return $response;
  }

}

First we added a new dependency to our controller \Drupal\Core\Image\ImageFactory which allows us to construct an Image instance, where we can get meta data from the image, but also gives us a unified interface to apply things to our image. For instance, we could desaturate the image by doing $image->desaturate(); and then resave the file. Fow now we're only using it to retrieve the meta data. We'll take advantage of that in the next part, when we refactor some of the written code and add more flexibility to what we can dynamically output.

If we hit the url and add both the width and height parameters we'll get something like this:

generated-image

In the up coming article we'll take a better look at what we have, what we miss (access control, what if a user hits the same URL at the same time), adding more effects, and exploring the use of the Image and toolkit APIs more in depth.

We'll most likely remove adding image effects through ImageStyles and only use the image style for creating derivates that we can we can later apply changes with the toolkit API.

If you want to continue on your own, take a look at ImageStyleDownloadController.php file in core which contains a lot of code that we can re-use.

Jun 05 2018
Jun 05

Webform allows you to create powerful forms in Drupal without the need for any custom code. You can use it for a basic contact us form with a few fields such as name, phone and email, or it can also be used to create complex multi page forms with conditional fields.

If you want to allow your editors to create their own forms without the need of a developer then install and teach them how to use the module. If you want to learn more about webform we have a two part series which will help you get started; Getting Started with Webform in Drupal 8 and Moving Forward with Webform in Drupal 8.

Collecting submissions using Webform is easy, but what if you want to integrate the module with a 3rd party SaaS provider? What if you want to push all contact form submissions into your CRM system, or add a row into a Google Sheets spreadsheet.

Of course, this can be done by a developer through the right APIs but you can also do it without writing any code using a service called Zapier.

In this tutorial, you’ll learn how to send Webform submissions into Zapier which will then add it as a row into a Google Sheets spreadsheet.

What is Zapier?

Zapier is an automation platform which lets you create workflows based on an action. For example, you could create a workflow when a user subscribes to a MailChimp list it’ll add the contact in a CRM (if supported by Zapier) or a Google Sheets spreadsheet.

It has integration with over 1000 SaaS applications and I do recommend that you have a play around with the service if you’ve never used it. They do offer a decent free plan which is more than enough to get you started.

Personally, if I’m looking at using a new SaaS application the first thing I check is if it has Zapier integration that way you can move data around and you have more flexibility.

How Does Webform Send Submissions to Zapier

Submissions are sent to Zapier using a POST request. This can be configured by adding a “Remote post” handler to a form. When a submission gets added, Webform will send it via a POST request using the URL in the “Remote post” handler.

Getting Started

For this tutorial, I’m going to assume you know how to use Webform. So all we’re going to do is download the module and install Webform and Webform UI. We’ll use the default Contact form that comes with the module.

Using Composer:

Composer require drupal/webform

Step 1: Create Zap on Zapier and Get POST URL

If you haven’t already, go ahead and create an account on Zapier. It is a paid service but has a free plan.

The first thing we’ll need to do is create the Zap where we’ll define a trigger and action.

1. Click on “Make a Zap!” in the top right.

2. In the “Choose a Trigger App” page, click on Webhooks in the “Built-in Apps” section down the page.

3. Choose “Catch Hook” and click “Save + Continue”.

4. Click Continue on the “Pick off a Child Key” page.

5. Copy the POST url because we’ll need it for Webform.

Step 2 : Set up Remote Post Handler

Using the POST url from Zapier which we got in the last section, we’ll need to create a “Remote post” handler in Webform.

1. Go to Structure, Webforms and click on Settings on a Webform. For this tutorial, I’ll use the default Contact form that comes with Webform.

2. Click on the “Emails / Handlers” tab, then click on “Add handler”.

3. Click on “Add handler” on the “Remote post” row.

4. In the Completed section paste in the URL we got from Zapier and click on Save.

Note: You can add a URL if the submission is updated or deleted. That’s a nice bit of added flexibility. I should also note that this is all the configuration required on Webform’s part, cool isn’t it!

Step 3: Send Test POST to Zapier and Finish Configuring Trigger

Let’s now jump back into the Zap we started building in step one.

1. Once you’ve configured the “Remote post” handler click on “Ok, I did this”.

2. Now go back to your Drupal site and submit the actual form so it sends a POST to Zapier. This is a required step.

3. Once you’ve sent a test POST, you should be on the “Pick A Sample To Set Up Your Zap” section. You can view what was in the post by clicking on the down arrow.

When you’re ready click on Continue.

Step 4: Configure Action in Zap

At this point, we’ve configured the trigger in our Zap and sent a test submission. We know that the POST request from Webform worked because Zapier received it.

Now we need to configure the action; where the submission will be stored.

1. Click on Google Sheets on the “Choose an Action App”.

2. Choose “Create Spreadsheet Row” and click on “Save + Continue”.

3. Connect Zapier to your Google account, then click on “Save + Continue”.

4. From the Spreadsheet field select the actual spreadsheet you want the submissions saved into.

5. Select the Worksheet within the spreadsheet.

If you see the message below it means your spreadsheet doesn’t have any headers.

Go and add a header for each column you want to save in your Google Sheets spreadsheet.

6. Now go and map the fields with the values below the “Catch Hook”.

Then click on Continue.

7. Click on “Send Test to Google Sheets” to test everything out.

Check your Google Sheets you should see a new row in it.

Then click on Finish.

8. Don’t forget to name and Zap and switch it on.

Step 5: Test Webform Submission

Go to your Webform and create an actual submission. Once submitted you should see the submission as a row in the spreadsheet.

Debug Zapier

If you need to debug what’s coming into Zapier or you want to see all the submission data which has been pushed into Zapier, then go to the “Task History” page.

From this page you can view if the task was successful or if there was a error. You can sometimes get an error if the API is down and not working.

You can also see what data has come in and out by clicking on the Task.

This gives you some nice visibility into what Zapier is handling.

Summary

The combination of Webform and Zapier is powerful. Almost everyone can be trained to use Zapier and create Zaps which will open a whole new world of possibilities. You’ll finally be able to connect your CRM with Drupal and without the need for custom code.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

May 22 2018
May 22

If a user needs to create an account on a Drupal site, they go to the user registration page at “/user/register”. This page is the registration form on a Drupal site. You can customize it by adding or removing fields. But what if you want to have multiple registration pages?

Let’s say you have two different roles on your Drupal site and you need a separate form for each role. How would you build that?

You could handle all of this writing custom code but remember we’re using Drupal so means there’s a module that can handle this type of functionality and It’s called Multiple Registration.

The Multiple Registration module allows you to create individual registration forms base off a user role in Drupal. When you register on one of the forms, you’re automatically assigned the configured role.

2018-05-14_22-28-00

In this tutorial, you’ll learn how to use Multiple Registration to create individual registration forms.

Installing the module

Download and enable the module with your preferred method.

Composer:

composer require drupal/multiple_registration

Drush:

drush dl multiple_registration -y

After downloading the module, click Extend in the toolbar and install it.

Enabling the Multiple Registration module

Create User Roles

The Multiple Registration module requires additional roles in order to display a customized registration form to each role. For the purpose of this tutorial, we’ll work with the example of a high school site with a registration form for parents and another for students.

The form for parents will have a field requiring information about payment methods and a billing address, while the form for students won’t have those fields, but instead of that will have a select list to vote for a sports team.

Click People, Roles, “Add role” in order to create the “Parent” role.

Creating roles

Repeat the process and create a “Student” role. After you’ve created the roles click the dropdown arrow next to the Parent role and choose “Add own registration page”.

Adding own registration page

You’ll be prompted to create a path for the Registration page for this particular role. For example, you could add “user/parent” as the path.

Creating the registration page path

Create also a registration page for the other role (in this case “Student”).

Configure Registration Forms

Now that you’ve created the roles, it’s time to configure the registration form for each one.

1. Click Configuration, “Account Settings”, “Manage fields” and click the “Add field” button.

2. Choose the “List(text)” option and label it “Payment method”.

Enter in a few options into “Allowed values list” and set the “Allowed number of values” to Unlimited, since a parent can have more than one payment method and click “Save field settings”.

1|Cash
2|Credit Card
3|Check
4|Other

List key-value pairs

You’ll be redirected to the Edit tab, scroll down to the bottom of the page and check “Users with Parent role”. This configuration is provided by the Multiple Registration module. Click “Save settings”.

Assigning fields to a role

Add another text field for the Billing address and assign it to the Parent role.

Lastly, add a list field for a sports team election, this one will be limited to one choice. In the Edit screen of this field check “Users with Student role” and click “Save settings”.

Assigning fields to a role

Test New Registration Forms

Click “Log out” on the right of your screen and click “Log in” on the same spot once again. You will see two new tabs corresponding to the roles we’ve previously created.

If you can’t see the tabs then you’ll need to rebuild the site cache. Go to Configuration, Performance and click “Clear all caches”.

Check each form and make sure the fields were assigned properly to each form.

Checking the fields in each registration form

Summary

The Multiple Registration module provides the ability to create different registration forms without having to write code. Best of all, the role configured in the form is automatically assign when a user registers. This will save you time because you want have to manually assign a role.

Jorge Montoya

About Jorge Montoya

Jorge has been reading about Drupal and playing with it for almost 5 years. He likes it very much! He also likes to translate German and English documents into Spanish. He's lived in some places. Right now, he lives in the city of Medellín in his homeland Colombia.

May 15 2018
May 15

Media Management for Drupal 8.5+

There’s a lot of momentum to fix media management in Drupal 8 thanks to the Media Entity module. By using a combination of Media EntityEntity Embed, Entity Browser and some media providers such as Media entity image you could add decent media handling in Drupal 8.

Then in Drupal 8.4, the Media Entity functionality was moved into a core module called Media. However, the core module was hidden by default. Now in Drupal 8.5 it’s no longer hidden and you can install it yourself.

In this tutorial, you’ll learn how to install and configure the Media module in Drupal 8 core. This tutorial is an updated version of the How to Manage Media Assets in Drupal 8 tutorial where we cover Media Entity.

Configuring Entity Embed and Entity Browser for the core Media module is essentially the same as with Media Entity. So if you have experience using Media Entity, then you’ll be fine using the core Media module.

Sections:

  1. Storing Media Assets
  2. Displaying Media Assets
  3. Embedding Assets into the Editor
  4. How to Browse Media Assets

What’s the Difference Between Media Entity and Media

The biggest difference between the two modules is that the API has changed. Any media provider module for Media Entity such as Media entity Instagram or Media entity Twitter need to be updated to use the new API. A lot of these modules now have a 8.x-2.0 version which only works with core Media. So if you’re going to use a media provider module read the project page.

If you want to see a technical explanation of the changes go to the “Moved a refined version of the contributed Media entity module to core as Media module” change record on drupal.org.

Getting Started

Before we begin, go ahead and install the Media module. It comes with Drupal core but it’s off by default.

Part 1: Storing Media Assets

The Media module implements a fieldable entity type called Media. This means you can add custom fields to them like content types and adjust their look-and-feel through the “Manage display” page. If you know how to manage content types then you’ll be right at home managing media types.

Let’s first look at how media assets are stored in Drupal 8. Saving assets into a Drupal site is similar to creating content.

1. Click on Content in the toolbar.

2. Click on the Media tab, then click “Add media”.

3. Choose which type of media you want to upload, in this example I’ll choose Image.

4. Enter in a name for the asset in the Name field and select an image using the Image, then click on Save.

5. Once you save the form, you’ll be redirected to the public media page.

View all Media Assets

You can view all upload assets from the Media page we were just on, go to the Content page and click on the Media tab.

You can search assets by their name using the “Media name” field or filter by media type using the Type drop-down.

Creating and managing media assets are pretty straightforward but the magic happens in the Media types, let’s look at them next.

Media Types

Media types are just like content types or taxonomy vocabularies. They are fieldable configuration entities.

Go to Structure then click on “Media types” to create and manage them.

Drupal 8 ships with four media types; Audio, File, Image and Video.

The File and Image media types are fairly self-explanatory use them to upload images or general files such as PDFs.

Audio and Video media types should be used when you want to locally host the video or audio file. The asset will be played using the HTML tags, <audio> and <video>.  You can’t use the this media type to embed videos from YouTube or Vimeo. To do that you’ll need the “Video embed field” and a new media type which we cover later in this tutorial.

Media Source

Every media type needs to use a “Media source”, this tells Drupal how the file should be handled, i.e., how the file should be stored or if a thumbnail should be generated.

The Media module in Drupal 8.5 comes with four media sources; audio, video, file and image.

So if you want to store and display tweets for example, then all you’ll need to do is create a media type called “Tweet” (or whatever you want) and install the 8.x-2.0 version “Media entity twitter” module. The 8.x-1.x version only works with Media Entity, whereas, the 8.x-2.x works with the core Media module.

Create Embed Video Media Type

Now let’s go and create a new media type for embedding YouTube videos. But first, download the 8.x-2.0 (make sure it’s 2.0) version of “Video Embed Field“. The module ships a sub-module called “Video Embed Media” which has a media source for handling embedded videos.

composer require drupal/video_embed_field

Once downloaded go and install “Video Embed Media” from the Extend page.

1. Go to Structure, “Media types” and click on “Add media types”.

2. Add “Embed video” into Name and “Used for embedding videos” into Description.

3. Select “Video embed field” from the Media source drop-down box.

4. From the field mapping section, you can store specific metadata about the video into a custom field.

For example, if you want to store the YouTube ID, which’ll be the “Video ID”. All you need to do is create a custom field and select it from the drop-down to map it.

5. Once everything is complete click on Save at the bottom of the page.

Edit Media Type

When you edit the Embed video media type, you should see three familiar tabs: Manage fields, Manage form display and Manage display.

A media type is just a fieldable configuration entity same as content types or vocabularies so managing fields are the same.

If you click on Manage fields you see a single field called “Video Url”. This is where the URL to the embedded video will be stored. We didn’t create the field, instead “Video Embed Media” programmatically attached it for us when we created the media type.

Create an Embed Video

Now that we’ve created our media type, let’s create the actual asset.

1. Go to Content and click on the Media tab.

2. Click on “Add media” then “Embed video”.

3. Enter a name into the Name field and enter in a URL to a YouTube video, then click Save.

It’s important to note that Video Embed Field supports more than just YouTube. Out-of-the-box it supports YouTube and Vimeo but it has support for other video providers via contrib modules. Check out the “Video Providers” section on the project page for more details.

“Video Providers” section on the Video Embed Field project page.

4. Once you’ve saved the form, you’ll be redirected to the media display page.

But as you can see in the screenshot above, the page looks messy. It’s displaying all the fields; date, author, thumbnail and at the bottom the actual embedded player.

Modify Media Display

Let’s clean up the display of the embedded video so only the embedded player shows.

1. Go to Structure, “Media types”.

2. Click on “Manage display” from the Operations drop-down on the “Embed video” row.

3. Move all the fields to the Disabled section except for “Video Url”.

4. Now if you go to the media display page you should only see the embedded player.

Part 2: Displaying Media Assets

You learnt how media assets are stored in part one and how to create your own media types. Now we’ll look at how to display the assets on a content type using a field.

I mentioned earlier that an asset is just an entity. So if you want to attach it to a basic page or article, all you need to do is create an entity reference field. Of course there’s a bit more to it, so let’s look at it now.

Create Media Field

The Media module comes with a field called Media. It simply lets you reference assets using an entity reference field. Let’s create one on the Article content type.

1. Go to “Content types”, “Manage fields” on the Article row.

2. Click on “Add field.

3. Select Media from the “Add a new field” drop-down and enter Asset into the Label field.

4. On the “Field settings” page, leave it as is and click on “Save field settings”.

5. Down in the “Reference type” field-set, you need to select which media type you allow. In this example, I’ll choose Image, then click on “Save settings”.

Now it’s time to test it out.

Go to Content, click on “Add content”, then Article and you should see the Asset field with an autocomplete field.

Then simply search for an asset using the “Use existing media” autocomplete field.

If you need to create a new asset, you’ll need to click on the “media add page” link or just go to Content, Media and create it from there.

But there is a problem with this method. You can’t create an asset directly from the article page. You need to go to the add media page, a totally seperate screen, upload the image then come back and search for it in the autocomplete field.

This workflow is not ideal. Now I’ll show you how to use Inline Entity Form module so you can create the assets without leaving the page.

Use Inline Entity Form to Upload Media Assets

Go download and install the Inline Entity Form module.

composer require drupal/inline_entity_form

1. Once installed, go to the “Manage form display” on the Article row.

2. Then select “Inline entity form – Complex” from the Widget drop-down on the Asset field.

3. Click on the cog wheel and make sure you check both “Allow users to add new media entities.” and “Allow users to add existing media entities.”.

3. Then click on Save.

Now if you go back to the create Article form, you’ll notice that the Asset field looks different.

Just click on “Add new media” if you want to create a new asset. When you click on the button, the create media form appears right here so you can create a new asset and attach it to a content type without leaving the form.

The “Add existing media” button allows you to select existing assets via an autocomplete field.

As you can see Inline Entity Form really does create a better user experience for editors. From the same page you can create/edit/delete and attach assets to a content type without leaving the page.

Part 3: Embedding Assets into the Editor

So far we’ve looked at how to attached assets to fields, but now let’s look at embedding them in the editor.

To build this functionality we’ll need two modules: Entity Embed and Embed.

Entity Embed allows you to create a button which is added to the editor. When you need to embed an asset, just click on the button, select the asset and embed. The module isn’t tied to the Media entity, it can be used to embed any entity type.

Using Composer, download the following modules then enable Entity Embed.

composer require drupal/embed
composer require drupal/entity_embed

Create Embed Button

1. Go to Configuration and click on “Text editor embed buttons”.

2. Click on “Add embed button”, enter Assets into Label and select Entity from “Embed type” and Media from “Entity type”.

3. Further down the page you can specify which bundles (Media types) should be allowed. If none are selected then all are allowed.

You can also upload an icon for the button. I’ll be using the media embed icon which comes with the Drupal 8 version of the Media module. You can grab a copy of it from here.

Once you’ve configured the form click on Save.

Manage Buttons

You can have as many buttons as you want and they can all be managed from the List page. If you need to modify a button then just click on the Edit button in the operations column.

Add Button to Editor

Now that we’ve created the button let’s add it to the editor. It won’t automatically appear in the editor unless we add it ourselves.

1. Go to Configuration and click “Text formats and editors”.

2. Click on Configure on the “Basic HTML” text format.

3. Find the button in the “Available buttons” section and move it into Media in the “Active toolbar” section.

Configure Filters

Now comes the tricky part, we’ve added the button but now we need to configure the filters in a specific order.

Few important steps need to happen here:

  1. Add the correct tags to the “Allowed HTML tags” text area.
  2. Enable “Display embedded entities” filter
  3. Reorder filters in specific order.

Configure “Allowed HTML tags” list

Once the button was added you should see the following tags in “Allowed HTML tags”.

<drupal-entity data-entity-type data-entity-uuid data-entity-embed-display 
data-entity-embed-display-settings data-align data-caption data-embed-button>

Enable “Display embedded entities” Filter

Next, enable the “Display embedded entities” filter from Enabled filters check list.

Confirm Order of “Align images” and “Caption images”

The Entity Embed README.txt mentions if you’re using the “Align images” and “Caption images” filters, to order “Align images” before “Caption images”.

Problem with “Restrict images to this site” Filter

The “Restrict images to this site” filter stops an image being displayed if you embed it and select an image style.

The filter stops a user from pointing to an image which is not hosted on the site. For example, if your Drupal site is hosted at my-drupal.com, then it won’t allow you to add an image such as <img src="http://random-site.com/image.jpg" />, all your images need to be <img src="http://my-drupal.com/image.jpg" />.

There is an open issue on drupal.org about it.

The workaround for now, unfortunately, is to remove the filter.

Once everything has been configured, make sure you click on “Save configuration” at the bottom of the page.

The filters list should look like this:

How to Embed Assets into the Editor

Now that the “Basic HTML” text format has been configured, we should be able to embed assets.

1. Go to Content, “Add content” and click on Article.

2. Click on the embed button and a pop-up should appear with an autocomplete field.

Search for the asset using its “Media name” and click on Next.

3. If it’s an image, select Thumbnail from “Display as”, select an image style, align and add a caption.

Then click on Embed.

4. Once embedded you should see the image on the right with the caption.

Save the page and you’re good to go.

Embedding YouTube Videos

In the section above it was easy to embed an image. You simply upload it, select a thumbnail size and you’re done.

Adding a video using the “Embed video” media type we created earlier is just as easy.

1. Click on the embed button within the editor, and search for a video in the Name autocomplete field.

2. Then select “Full content” from the “Display as” drop-down and click on Embed.

3. Once embedded you should see the YouTube player in the editor.

If you do not see an embedded player and just the video thumbnail then you’ll need to configure the formatter on the Media type.

Go to Structure, “Media types” and click on “Manage display” on the Embed video row.

Make sure the “Video Url” field is using the Video formatter.

Part 4: How to Browse Media Assets

We had to use an autocomplete field to find and embed an asset into the editor in the last section. This type of user experience is not great. You can’t upload an image after clicking on the embed icon, without going to a different page, and you can’t easily search assets, you have to know the name of it.

In section, you’ll learn how to use Entity Browser which lets you create a screen where you can search, select and upload new assets.

Using Composer, download the following modules then enable Entity Browser and Chaos tools.

composer require drupal/ctools
composer require drupal/entity_browser:~2.0

Make sure you download the 8.x-2.0 version of Entity Browser.

How to Create an Entity Browser

Configuring Entity Browser requires two steps:

  1. First you’ll need to create a view using a display called “Entity browser”. This view will be used to list out all assets.
  2. Then you’ll need to configure an entity browser and select the created view.

Create Entity Browser View

1. Go to Structure, Views and click on “Add view”.

2. Fill out the “Add view” form, using the values defined in Table 1-0 and click on “Save and edit”.

Table 1-0. Create a new view

Option Value View name Media browser Machine name Media_browser Show Media type of All sorted by Newest first Create a page Unchecked Create a block Unchecked

3. Next to the Master tab click on “Add” and select on “Entity browser.

It’s important that you select the “Entity browser” display or you won’t be able to select this view when we’re configuring the actual browser.

Let’s change the view to a table so it looks better.

4. Click on “Unformatted list” next to Format.

5. Select Table and click on Apply.

At this point we’ve switched the view from an unformatted list to a table.

Now we need to add two more fields: Thumbnail and “Entity browser bulk select form”.

6. Click on Add next to Fields, add the Thumbnail field.

This will display a thumbnail of the media asset.

7. Then add the “Entity browser bulk select form”.

This field is used to select the asset when browsing. It is a required field.

8. Reorder the fields so they’re as follows:

9. Once complete the preview should look like the image below:

10. Don’t forget to click on Save.

Create Entity Browser

Now that we’ve created the view, let’s configure the browser.

1. Go to Configuration, “Entity browsers” and click on “Add entity browser”.

2. Enter “Assets browser” into Label, select iFrame from “Display plugin” and Tabs from “Widget selector plugin”.

Leave “Selection display plugin” as “No selection display”.

Then click on Next

Do not select Model from Display plugin if you’re using the browser with Entity Embed it isn’t compatible (Issue #2819871).

3. On the Display page, configure a width and height if you like but do check “Auto open entity browser. This will save an extra click when embedding.

Then click on Next.

4. Just click Next on “Widget selector” and “Selection display”.

5. On the Widgets page, select “Upload images as media items” from the “Add widget plugin”. Change the Label to “Upload images”.

6. Then select View from “Add widget plugin”.

7. From the “View : View display” drop-down, select the view which we created earlier.

If you can’t see your view, make sure you select “Entity browser” when configuring it:

8. Once configured the Widgets page should look like:

Configure Entity Embed to use Browser

Entity Embed now needs to be linked to the browser we just created.

1. Go to Configuration, “Text editor embed buttons” and edit the embed button.

2. You should see a drop-down called “Entity browser”, select the browser you just created and click on Save.

Using the Entity Browser

Go into an article or page and click on the Entity Embed button.

You should now see a pop-up with two tabs: “Upload images and view.

From the “Upload images” tab, you can upload a new image and it’ll create an Image media entity.

If you click on view, you’ll see all the media assets.

To embed an asset, just choose which one you want and click on “Select entities”.

Summary

With every new Drupal 8 release you can see that the media functionality is getting better. In Drupal 8.5 , there’s still a lot of manual configuring required but the foundation of how assets are stored exists. If you want to keep track of how everything is progressing then look at “Media in Drupal 8 Initiative” and “Media initiative: Essentials – first round of improvements in core“.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

May 08 2018
May 08

With the Toolbar Menu module, you can add as many menus as you need to the toolbar of your Drupal installation. By default, a Drupal 8 installation has 3 menu links in its toolbar. These are:

  1. Manage – Administration of the whole Drupal site
  2. Shortcuts – Links added by the admin to administrative pages used frequently
  3. User Name – Link to the profile page

This module works also with the Admin Toolbar module, which improves the default toolbar providing dropdown menus. In this tutorial, we’re going to cover the usage of the Toolbar Menu module.

What to learn how to change the color of the toolbar depending on which environment you’re on, i.e., dev or test? Then check out Differentiate Websites using Environment Indicator in Drupal 8.

Getting Started

First of all you need to download and install the module:

With Composer:

composer require drupal/toolbar_menu

With Drush:

drush dl toolbar_menu -y

After downloading, go to Extend, look for the module and enable it. The Toolbar Menu module requires the Toolbar and Breakpoint modules. These two modules come with Drupal core and should be enabled by default.

Enabling the module

Configuring the Toolbar Menu Module

To configure Toolbar Menu, click Configuration and in the “USER INTERFACE” section click “Toolbar Menu”

Configuration of Toolbar menu

Click the “Add toolbar menu element” button, you’ll be prompted to enter a label and select a menu from the dropdown list. For the purpose of this tutorial, I’m going to select the “Footer menu”. Leave the checkbox unchecked, that way, you’ll see the label “Extra” instead of the label “Footer” as a clickable menu in the Toolbar of your Drupal installation and click Save.

Configuration of Toolbar Menu

The new menu will appear in your Toolbar and if you click on it, it will display its menu links. In this case, there’s only one link pointing to the contact page.

Toolbar Menu module

Let’s add another link.

Adding Links to a Menu in the Toolbar

To add a link to the newly added menu in the Toolbar, click Structure, Menus. Locate the dropdown widget on the right of the Footer menu, click the dropdown arrow and select “Add link”.

Adding links to a menu

Click the “Add link” button to enter a title for this menu link, for example “Disclaimer” to create a link to the Disclaimer page of your website. You could even include external links here if that makes sense in your particular case.

Choosing a link for Toolbar Menu

This is an autocomplete field, just start typing the name of the node you want to link to, once found select it and click Save.

Check the “Extra” menu link in the Toolbar. It has the new link you just added.

Reviewing the new link

If you click this new link, it will redirect you to the Disclaimer page as expected.

Summary

Toolbar Menu improves the user experience of your Drupal site by allowing site administrators to insert menu links directly in the Drupal default toolbar. With this module you can add all kinds of custom and/or default Drupal menus and grant access to certain user roles to those menus with the help of Drupal’s permission system.

Jorge Montoya

About Jorge Montoya

Jorge has been reading about Drupal and playing with it for almost 5 years. He likes it very much! He also likes to translate German and English documents into Spanish. He's lived in some places. Right now, he lives in the city of Medellín in his homeland Colombia with his wife and son.

Apr 25 2018
Apr 25

The Linkit module allow site editors to work in a more comfortable way when linking to internal entities (i.e. content, users, taxonomy terms, files, comments, etc.) and when linking to external content as well.

The benefit of the module is that your editors won’t have to copy and paste URLs of content they’re linking to, instead the module provides an autocomplete field, which they can use to search for content.

Linkit works based on a profile system. You can choose as many or as few plugins (linking options) for each profile and then assign each profile to a particular text format. This provides an extra layer of granularity, because the linking permissions are granted in the text editor and not within Linkit. That way you can add multiple roles or just one role to a Linkit profile.Linkit profiles

In this tutorial, you’ll learn how to configure Linkit by creating a profile and adding a button to CKEditor.

Getting Started

Before we can begin, go download and install Linkit.

Using Composer:

composer require drupal/linkit

Or, Drush:

drush dl linkit -y

Configuring Linkit

To configure Linkit, click Configuration, “Linkit profiles” and then on click “Add profile”.

Linkit profiles

You’ll be presented with the configuration screen for the first profile. For the purpose of this tutorial, I’m going to create two profiles: one for admin users and one for editors. Begin creating the first Admin profile. Give it a proper name and a description. After that click the “Save and manage matchers” button.

Adding a Linkkit profile

The matchers are just the entity types you’re allowed to link to. Click “Add matcher”, select Content and click “Save and continue”.

Adding the "Content" matcher

The “Results description” textbox helps us build our results in the Linkit autocomplete field in a more organized way through the use of tokens. For example, you can add [node:title]/[node:author].

The “Restrict to selected bundles” options allows us to restrict the search results to a particular bundle. All bundles will be shown in the select results if none are checked.

The “Group by bundle” option lets you group the results in the Linkit search results by bundle. This is an advantage if you have lots of content because you can search in an organized way. I’m going to include unpublished nodes, since this is the “Admin” profile. After choosing all options click “Save changes”.

Configuration of the Result description

Let’s add another matcher for the admin profile. We’ll assume that the admin user is allowed to link to User profiles while the editor profile won’t.

Adding the User matcher

Configure the “Result description” ([user:created]/[user:account-name]) as shown in the image below and click “Save changes”.

Configuration of the User matcher

You have now two matchers in your Linkit profile. You could add all of them if you want to. Now it’s time to configure the link attributes. Click the “Manage attributes” tab on the right.

Link attributes

This functionality allows you to add additional attributes to the link. Click the “Add attribute” button and choose the Title attribute. Click “Save and continue.

Link attributes

Check the “Automatically populate title” option and click “Save changes” once again.

Edition of link attributes

You can add as many attributes to your links as you want. The procedure is exactly the same as with the matchers.

Add Linkit Button to Editor

Now that you have created a Linkit profile, it’s time to add the button to the editor. Click Configuration, “Text formats and editors”. Choose the Full HTML format and click Configure on the right.

Assigning Linkit profile to a Text format

Remove the default Link button from the active toolbar and add the Linkit button instead.

Assigning Linkit profile to a Text format

Your toolbar should look like this:

Assigning Linkit profile to a Text format

Scroll down a little bit and you’ll find a “Linkit” vertical tab. Click and choose the profile you configured in the last step.

Assigning Linkit profile to a Text format

Click the blue “Save configuration” button.

Now it’s time to create a node and add some links pointing to another article and to a user profile.

Test Linkit

Click Content, “Add content”, Article. Write a proper title for you article and write some text in the body section. Make sure the “Text format” of your current editor is set to “Full HTML”, otherwise you won’t be able to see the Linkit button.

Linkit test

Highlight some text and click the Linkit button. If you type in the letter a for example in the Linkit modal, you’ll see all items that match that letter. These items will be grouped by bundle (when it’s about content). Also you’ll see the users that match that particular letter. Notice that the search results are presented according to the “Result description” (with tokens) that you configured in the Matchers section.

Linkit modal window

Choose one of the options, the Title attribute will prepopulate itself with the node title. Highlight another word or phrase and link it to some user. Save the node. Test now that your links point to the right node/user.

Summary

The Linkit module will save your editors time because they will no longer have to manually find content and the URL to link to. Linkit allows them to find and create links directly from the editor without having to leave the page.

Jorge Montoya

About Jorge Montoya

Jorge has been reading about Drupal and playing with it for almost 5 years. He likes it very much! He also likes to translate German and English documents into Spanish. He's lived in some places. Right now, he lives in the city of Medellín in his homeland Colombia with his wife and son.

Apr 17 2018
Apr 17

As a web developer, you probably build your sites first in a local environment (aka localhost), then you commit all your changes to a staging server (i.e. an online server to which only you or the development team has access) and if everything works fine in the staging server, you’ll commit these changes to a production or live server (that’s your online site).

However, you don’t have a way to differentiate between your local, your staging and your production environments apart from the address box of your browser, so it’s very easy to mix up everything and that could lead to complications. The worst case scenario is making changes directly to your live site without testing and breaking it. In order to prevent this, you can use the Environment Indicator module.

The Environment Indicator module adds a visual hint to your site, that way you’ll always be aware of the environment you’re working on. We’re going to cover installation and usage of this module in this tutorial.

Let’s start!

Getting Started

There are several ways to install the module. You can use composer for example:

Before we begin, go download Environment Indicator and install the “Environment Indicator UI” sub-module.

Using Composer:

composer require drupal/environment_indicator

Or, Drush:

drush dl environment_indicator -y

Once dowloaded, go to Extra and install both: “Environment Indicator” and “Environment Indicator UI”

Enabling the modules

Configuring Environment Indicator

To configure the module, go to Configuration, “Environment Indicator Settings” and you’ll see 3 tabs:

  • Settings
  • Environment Switcher
  • Current Environment (this tab is provided by the Environment Indicator UI module)

In the Settings tab you have two options:

  • Toolbar: integrates the environment indicator module with the toolbar. The toolbar will have a different color according to the environment you’re in.
  • Favicon: the module will add a favicon to your browser tab with the color of the corresponding environment and the first letter of it. If the name of your environment is Development, you’ll see a D with the colors you set up for that particular environment. This is practical if you have several tabs from different environments opened in your browser

Leave both options checked.

Environment Indicator Settings

In the Environment Switcher tab you can add buttons to switch between all your environments.

Click the “Add environment” button and give it a proper name, for example “Staging Server”. In the Hostname field insert the address of your staging server, configure the colors according to your preferences.

Environment Switcher Configuration

You could for example use the colors from a traffic light to indicate the different environments as defined in table 1-0.

Table 1-0. Example list of environments and their colors

Environment

Foreground (text) color

Background color

Development White Green Staging Black Yellow Production White Red

Click the Save button and repeat the procedure for each of your servers.

You should see all the different environments and their colors on the “Environment Switcher” page.

Environment Switcher

Finally click the “Current Environment” tab. This is the place where you configure the colors of the toolbar for the environment you’re working in at this very moment. I’m working on my localhost, so I will call this environment Development.

The foreground (text) color will be white and the background color will be a shade of green (lighter than the color of the switcher), you can choose the same color or another. I’ve chosen a different color just for demonstration purposes.

Click “Save Configuration”.

Notice that you’ll have to repeat this process in each one of your environments.

Current Environment Configuration

Click Configuration, Performance and clear the site cache.

Cache rebuild

Test Environment Indicator

You’ll see immediately that the toolbar changes its color to the color you selected in the “Current Environment” tab and also a new menu link in the Toolbar called Development.

Click this menu link. There are the environment switcher buttons, each one of them will take you to the URL you entered in the “Environment Switcher” tab. Notice also the favicon in the browser tab with a white “D” (Development) over a green background.

Envirionment Indicator Toolbar and Switchers

Configure Environment Indicator via settings.php

As already mentioned, the “Current Environment” tab is provided by the Environment Indicator UI module. You can also “hardcode” these settings directly in the settings.php file of your installation without the need of enabling the module.

Environmet Indicator Settings

Use a text editor to open the settings.php file of your Drupal installation and type this code at the end of the file:

$config['environment_indicator.indicator']['bg_color'] = '#333333';
$config['environment_indicator.indicator']['fg_color'] = '#DDDDDD';
$config['environment_indicator.indicator']['name'] = 'Dev. Environment';

settings.php file

You’ll see the change in the toolbar after rebuilding the site cache.

Toolbar with Environment Indicator

This won’t work if you already have configured the Current Environment in the UI. The first configuration has always precedence.

Summary

The Environment Indicator module is a valuable tool when developing your Drupal site. It helps you keep everything in order in your development process and prevents from inadvertently making changes in your live environment without proper testing.

Jorge Montoya

About Jorge Montoya

Jorge has been reading about Drupal and playing with it for almost 5 years. He likes it very much! He also likes to translate German and English documents into Spanish. He's lived in some places. Right now, he lives in the city of Medellín in his homeland Colombia with his wife and son.

Mar 28 2018
Mar 28

In our Drupal 7 site we have an enhanced textfield that autocompletes already stored values. When migrating to Drupal 8 we could normalize this by using a vocabulary and terms instead.

So in our current Drupal 7 setup we have something like:

Content type: Employee

nid name field_role 1 John Web developer 2 Emil Web developer 3 Henrik Web designer 4 Karl Web designer 5 David Sales 6 John Sales

And in Drupal 8 we want this instead:

Vocabulary: Role

tid title 1 Web developer 2 Web designer 3 Sales

Content type: Employee

nid name field_role 1 John 1 2 Emil 1 3 Henrik 2 4 Karl 2 5 David 3 6 John 3

Using entity_generate process plugin

The first approach we can take is to use the entity_generate plugin provided by Migrate Plus module.

In our migration file:

process:
  field_role:

    # Plugin to use
    plugin: entity_generate

    # Field from source configuration
    source: field_role

    # Value to compare in the bundle
    value_key: name

    # Bundle key value
    # If you get errors consider using only bundle
    bundle_key: vid

    # Bundle machine name
    bundle: role

    # Type of entity
    entity_type: taxonomy_term

    # Set to true to ignore case on lookup
    ignore_case: true

Running this migration will take the value of the textfield and try to lookup the term by name on the destination and if it does not exist the term will be created.

In the second part of this article (coming soon) we will take a look at how we can deal with translations.

Mar 16 2018
Mar 16

In our first post that announced the new Mediacurrent redesign, we looked at the evolution of Mediacurrent.com over the years and talked through the over goals of the relaunch. Now let’s take a look under the hood to see some of the cool stuff we did and discuss what our development team learned along the way.

Let’s talk architecture

Now for the fun part, the technical architecture of the new website. First, the backend was upgraded from Drupal 7 to Drupal 8 - that will probably not be a huge shock to anyone. The more interesting aspect of this build is that we have now implemented a fully decoupled frontend. We accomplished this using a static generator called Jekyll which has been integrated with the Drupal backend. More on that in a bit. First let’s answer the question, “Why decoupled?â€

Why decoupled?

A decoupled architecture provides flexibility for constant evolution, opening the door to a variety of potential programming languages and design philosophies to accomplish your website goals. There are any number of articles that discuss the benefits of moving to a decoupled approach. For this post, I want to focus specifically on the points that were deciding factors for our team.

Security

While we do have full confidence in the security features that Drupal offers, we have to acknowledge that a static public site does offer some advantages that make securing the application easier. First of all, we have the option to make the backend CMS completely walled off from the public site. It’s not a hard requirement that the Drupal admin is made publicly accessible. Second, there are simply fewer vulnerabilities that a static frontend will be susceptible to in comparison to a full PHP application. For example, it’s harder to DDOS a site serving only HTML/CSS/JS and there is no server side code running that could be hijacked by an SQL injection attack.

Performance

Decoupled sites often have a performance boost over a fully Drupal-rendered site because the frontend is more custom and lightweight. This is certainly true in our case. The static frontend requires no processing at the time of request so the page is served up immediately with no server-side execution required.

Hosting

One of the things we liked about this particular solution was that it made the hosting architecture pretty simple and inexpensive. With only editors logging into the CMS and the static site being served by Gitlab, we were able to have a fast, reliable stack up and running relatively easily. Up-time is great in that you aren’t as vulnerable to a production error or traffic spike bringing the site down. That being said, all platforms are subject to downtime each year.

Eating our own dog food

As many other agencies will attest to, when you work on your own website it’s a good chance to try something different! We looked at what some competitors had done and we wanted to try an approach that would be a good fit for our needs without overcomplicating the end solution. This endeavor was a way to take some risks and learn along the way.

Dividing the work

The great thing about decoupling is that you break apart the work that needs to get done. The frontend team can focus on the frontend stuff without being tied too much to the backend work (although there will always be some overlap). Our agency spends a lot of our day delivering solutions to our clients so being able to break apart some of the work streams was an advantage. We like that in the future we don’t necessarily need to do a big redesign and Drupal upgrade at the same time. With a decoupled approach, we have the flexibility to tackle each separately.

Technical Overview

Now that you have seen the “Why†behind this approach, let’s look at the “How.†We have kept our Drupal CMS in Bitbucket, which gets deployed to a Pantheon server. That piece is still the same as its been for many years. The new wrinkle is that the public frontend is served on GitLab Pages. If you haven’t heard of Github Pages (which run on Jekyll), Github, GitLab and many other services allow you host Jekyll source files which they can auto-compile into HTML pages and host for you for free or cheap. Pretty neat huh? We ended up going with GitLab Pages because GitLab allows you to add more build customizations than Github. We have also looked at potentially using Netlify in the future as the host for our Jekyll files.

The question you might be asking is how does Drupal content make its way to GitLab? Put simply, we translate node content to markdown and push to the GitLab API on every node save. For user files, we actually still use Drupal uploads and reference the path within Markdown files. If you are familiar with Markdown files, these are the “content†files that Jekyll compiles into pages. The diagram below illustrates the basic flow.

Illustration of how Drupal content makes its way to GitLab

The concept is pretty simple: have Drupal manage your content, write to Jekyll markdown files and deploy those files to a static host.

Why not [Insert favorite Node framework here]?

You might be saying, that's all well and good but why go with a static generator over a server-rendered JavaScript framework like Next.js or Nuxt.js?

The short answer is that we reviewed several options and concluded there wasn’t a framework we felt was a fit at the time we were planning development (around mid-late 2016). Going with a JavaScript-only framework was ruled out for SEO reasons and the options for Isomorphic (server + client side js) frameworks weren’t as stable as we would have liked. Jekyll was the most popular static framework (and still is) with a variety of plugins we could utilize. After doing some POC’s we opted for Jekyll in order to keep the page rendering lean, simple and speedy. The overall complexity of the integration was also a deciding factor in choosing Jekyll over other options.

Trade-offs

One of the fun challenges with a static only site is that you need to learn how to get around the lack of server side rendering. The files are of course static, thus if you want anything dynamic looking you are limited to mostly JavaScript-based solutions. A couple quick examples are forms and the site search. For forms, we were already using Pardot hosted forms for marketing automation, so that wasn’t a big tradeoff. For site search, we went with a pretty cool solution that leverages https://lunrjs.com/ to handle searching the site. Essentially we have Drupal push an index file that Lunr.js can search against using only Javascript in the browser. For RSS, we still generate the RSS feed from Drupal and push to GitLab.

Lessons learned and looking forward

Now that we have the shiny new website up and running, it’s time to look ahead. Would we recommend taking this exact approach in 2018? I would say not exactly. While we are still intrigued by the power of static generators serving as a front end we think something like Gatsby.js (Node/React-based) might have more upside than Jekyll. Further, we aren’t sold on this type of static-only being able to scale in comparison to Node-hosted solutions. The options for server-rendered JavaScript frameworks increase by the day and many have matured over the last few years. Mediacurrent.com will continue to be our place to try new approaches and share with you everything we’ve learned along the way. Thanks for joining us in this journey and enjoy the new site!

Additional Resources
The 3 C’s and 1 D of Drupal: Why Decoupled Matters | Mediacurrent Blog
Relearning Accessibility for a Decoupled Front End | Mediacurrent Blog
4 Benefits of Decoupled Architecture for Enterprise Marketers | Mediacurrent Blog

Mar 15 2018
Mar 15

Since joining Mediacurrent in 2009, I've seen firsthand how our company has grown and evolved, and how our website has mirrored those changes. Today, I am thrilled to announce the launch of our newly redesigned website, mediacurrent.com.

We’ve come a long way since 2007!

screenshots of the Mediacurrent.com homepage from 2007 to present

2017 marked the 10 year anniversary of Mediacurrent. It’s been an amazing journey from Drupal firm to a full-service digital agency. Along with this journey, we’ve expanded and redefined our services to meet the changing needs of our clients.

Explore the site to learn more about Mediacurrent’s development, design, and strategy services.

Built on Drupal 8 with a decoupled approach, the new site reflects a clear vision of our core focus:

Open source development, design, and strategy that grows your digital ROI

Celebrating Our Success

I am incredibly proud of the Mediacurrent team for the persistence and hard work that went into building our new website. It’s this teamwork that has fueled award-winning sites for weather.com, travelport.com, careaction.org, and many others. See how we do it.

group shot of the Mediacurrent team

Some of the MC team at a recent code sprint retreat in Los Angeles

Sharing Our Resources

In our very first company blog post circa 2009, we shared an ambitious goal — we hope the Mediacurrent staff can share pertinent content, and become a trusted advisor when it comes to your web related issues —and we got there! From blog posts and tutorials to videos and podcasts, our new site makes it easier to navigate a great depth of thought leadership content by the Mediacurrent team.

Expressing Our Culture

Mediacurrent is in an exciting period of growth, and we're evolving our team to keep pace with all of the work that lies ahead.

Our culture is at the core of everything we do and one of the biggest drivers behind our redesign was to tell that story. There are lots of ways to do this on the new Mediacurrent.com: meet our team, see how we give back to the Drupal community, and explore career opportunities

Enjoy the new Mediacurrent.com site, and we welcome your feedback!

Mar 05 2018
Mar 05

In this short article we will learn how to create image styles and how to add image effects programmatically.

Creating image styles

Image styles are stored as configuration entities in Drupal 8. So to create a new image style we simply need to create a ImageStyle instance and save it.

<?php

use Drupal\image\Entity\ImageStyle;

$imageStyle = ImageStyle::create([
  'name' => 'machine_name',
  'label' => 'Label',
])->save();

Add image effects

We will use the ImageStyle::addImageEffect method to do this.

$imageStyle->addImageEffect([
  'id' => 'image_scale_and_crop',
  'weight' => 0,
  'data' => [
    'width' => 500,
    'height' => 500,
]);
$imageStyle->save();

Each image effect is a plugin of the ImageEffect plugin type. You can find the core's image effect plugins under the Drupal\image\Plugin\ImageEffect namespace in the image module.

Feb 27 2018
Feb 27

The ability to create and maintain redirects on a website is vital for long-term success.

Once your site has a lot of content, you may need to do a content audit. This will require merging or deleting pages which are no longer important. To maintain the traffic from these deleted or merged pages, you’ll need to create URL redirects. Now I understand this isn’t the most exciting part of site building but it’s important to get it right.

The module which will handle all of this is perfectly named; Redirect.

The Redirect module lets you create and manage redirects using a simple user interface. Just define a source and destination and you’re good to go. You can also track 404 errors, using a sub-module, so if you have a page indexed in Google with a broken path then it’ll be logged and a redirect can be easily created.

In this tutorial, you’ll learn how to:

  1. Create a redirect
  2. Track broken paths
  3. Add domain level redirections
  4. Import redirects via a CSV

Learn how to automate the generation of URL aliases using Pathauto in Drupal 8.

Getting Started

Before we begin, make sure you download and install the Redirect module.

If you use Composer, run the following:

$ composer require drupal/redirect

Or Drush,

$ drush dl redirect

Create a Redirect

Once you have installed the module creating a redirect is very easy.

1. Go to Configuration and click on “URL redirects”.

2. Click on “Add redirect”, enter in a Path (old path) and then select a To (new path).

The To field is an autocomplete field which you can use to lookup content on the site. But you can also add an external URL into the field.

3. From the “Redirect status” drop-down box, you can select which status will be used. Most of the time it’ll be a 301 or 302. Once you’re finished click on Save.

4. You’ll be redirected back to the Redirect page where you can manage existing redirects and create new ones.

Track Broken Paths

The Redirect module ships with a handy sub-module called Redirect 404.

The sub-module logs all the 404s and displays them all with a count in the “Fix 404 pages” page in “URL redirects”. When dealing with redirects, half the battle is figuring out which paths need to be redirected. This sub-module will log all 404s and add a button called “Add redirect” which you can use to create the redirect.

This sub-module could cause performance issues if your site gets a lot of traffic so test accordingly.

Domain Level Redirects

Another sub-module, which Redirect comes with is called “Redirect Domain”, it allows you to create domain level redirects.

A good use of this is when you want to redirect a whole domain, i.e., old-domain.com to new-domain.com. You can create a rule which will redirect anything from old-domain.com/* to new-domain.com, without manually creating redirects.

Import Redirects

Creating redirects manually won’t cut it if you need to add more than 20. If you have a spreadsheet full of redirects then it’s best to look at using Path redirect import.

The module lets you import redirects using a CSV file. All you need to do is prepare everything in a spreadsheet then once you’re ready, export the sheet as a CSV and import it into Drupal using the module.

The format of the CSV is pretty simple:

From,To,Redirect,Language
hello-world,node/1,301,en

From and To are the only required columns. Redirect and Language are optional.

Run CSV Import

Once you’ve installed Path redirect import, go to Configuration, “URL Redirects” and click on the Import tab.

Select a file using the CSV File upload field and click on Import. Once the import is complete you’ll see a message telling you which redirects imported and which didn’t.

The reason the redirects in the above image were bypassed (not imported) is because the page did not exist. When preparing your CSV file make sure the paths and nodes actually exist. For example, if you’re creating a redirect to node/123 and that page doesn’t exist then the module won’t import it in.

Update Existing Redirects

When importing, if you want to update existing redirects then all you need to do is check the “Override existing sources” checkbox when importing.

Global Redirect merged into Redirect Module

The functionality of the Global Redirect module has been merged into Redirect for Drupal 8.  This is great because it means there’s one less module to install.

The Global Redirect settings can be configured by going to the Settings page with “URL redirects”.

Summary

Redirect is one of the must-have modules which is installed on most Drupal sites. But you can use it for more than just managing historical links. Another good use-case is to create vanity URLs. For example, you could create a redirect path domain.com/d8, that’ll redirect to another page within your site. This is especially useful when adding URLs into a presentation, the shorter the URL the more memorable it is.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Feb 19 2018
Feb 19

In Drupal 7 we could use Views Selective Filters module to have an exposed filter only show options that belong to result set. The module has not been ported to Drupal 8 yet. So what do we do?

Here is our current view:

article-view

As we can see there is no node with the JavaScript tag and therefore we would like to remove that option from the exposed filter.

Ideally we would like a setting that we can configure on the filter, however I did not have the time to implement that. Instead we will make a trade off and alter in the exposed form directly.

First we need somewhere to put the code, so we'll create a custom_views module. I'll use Drupal Console to scaffold this out quickly. In custom_views.module we'll add our code, I've added additional comments to explain what we do.

<?php

/**
 * @file
 * Contains custom_views.module.
 */

use Drupal\Core\Database\Database;

/**
 * Implements hook_form_FORM_ID_form_alter().
 */
function custom_views_form_views_exposed_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state)
{
  // If the exposed filter does not exist on this form, there's nothing we can do here.
  if (!array_key_exists('field_tags_target_id', $form)) {
    return;
  }

  // Options are tag entity id => title.
  $options = $form['field_tags_target_id']['#options'];

  // We are querying for tags belonging to at least one node.
  // We group by tag id so we don't get a result for each
  // node the tag is referred by.
  // We also set a condition on the bundle, as we could have
  // other bundles using same field.
  $connection = Database::getConnection();
  $sth = $connection->select('node__field_tags', 'tags');
  $sth->addField('tags', 'field_tags_target_id');
  $sth->condition('bundle', 'article');
  $sth->groupBy('tags.field_tags_target_id');

  $data = $sth->execute();
  // Flip the result set so the array key is the tag entity id.
  $results = array_flip($data->fetchAll(\PDO::FETCH_COLUMN, 'field_tags_target_id'));

  // Intersects the arrays, giving us back an "filtered" array.
  $options = array_intersect_key($options, $results);

  // Replace the options.
  $form['field_tags_target_id']['#options'] = $options;
}

article-view-filtered

That's it! We could definitely improve this by breaking out the logic so we can reuse for additional field or even better, integrate this functionality better in Views (for instance by porting Views Selective Filter to D8 https://www.drupal.org/project/views_selective_filters/issues/2660844)

To quote my colleagues:

Good Enough for Now and Safe Enough to Try

Feb 13 2018
Feb 13

On a recent project, I had to create a custom page which displays content by the logged in user, think of it as a “My articles” or “My blogs” page. I knew how to do it by writing code but I thought I’d try it with Views and see how far I could get without writing any custom code. Long story short, I was able to do it all by using just the Views module.

In this tutorial, you’ll learn how to create a page which will appear as a tab (local task) on the user profile page.

Getting Started

For once there are no extra modules to download and install. In Drupal 8, Views ships with core and will be automatically installed if you installed Drupal using the Standard installation profile.

If it’s not already installed, go to Extend and install Views and “Views UI”.

Create User Profile Page

The first bit of work we need to do is create an actual Views page.

This page will display a table of articles which is owned by the user, we’ll call it “My articles” and the URI to the page will be /user/%user/my-articles the %user argument will be the user ID which will be used by the contextual filter.

The owner of the content is defined by the user added to the “Authored by” field on the content edit page.

1. Go to Structure, Views and click on “Add view”.

2. Fill in the “Add view” form with the values defined in Table 1.0.

Table 1.0: Add view

Option Value View name My Articles Machine name my_articles Show Content (default) Of Type Article Create a page Checked Page title My Articles Path user/%user/my-articles Display format Table

3. If you go to /user/%user/my-articles replace %user with any number, it should return a table of articles.

Create Contextual Filter

The %user argument getting passed through the URI is not being used at this point. Let’s now add a contextual filter which will use the argument and only display articles which are authored by the user ID.

1. While on the Views edit page, click on Advanced then Add next to Contextual filters.

2. Search for “Authored by” in the Content category and click on Add.

3. Select the “Provide default value” radio button and choose “User Id from route context”

4. Further down the page:

  1. Check “Specify validation criteria”.
  2. Select “User ID” from Validator.
  3. Check “Validate user has access to the User”.
  4. Under “Access operation to check” select Edit.

5. Click on Apply to save the contextual filter, then click on Save to save the view.

Now if you go to the page, /user/%user/my-articles make sure you change %user with an actual user ID, you should only see their articles.

Page Access Control

Please make sure you’ve checked “Validate user has access to the User” and have chosen Edit under “Access operation to check”.

This means that only users who have edit access can access the page. This would be users accessing their own accounts or site administrators who can edit other user accounts.

If you do not then any user who knows the URI /user/%user/my-articles could go directly to it and see which articles are owned by the user.

Display Page as Tab (Local Task)

At this point, we’ve created the page and added a contextual filter to only display articles owned by the user.

Now let’s create a menu for the page so it’s accessible via a tab on the user profile page.

1. While on the views edit page, click on “No menu” link in the “Page settings” section.

2. In the “Page: Menu item entry” window, complete the following:

  1. Select “Menu tab” from Type.
  2. Add “My articles” to “Menu link title”.
  3. Select “<User account menu>” from Parent. This is important if you don’t do this then the tab won’t appear.
  4. Add 5 to weight.

3. Now if you go to the “My articles” page it should go from this:

To this:

If you can’t see the tabs but have configured it properly then try rebuilding the site cache. Go to Configuration, Performance and click on “Clear all caches”.

Summary

The ability to create these types of pages is where Views really shines. Often a client will ask for a content specific page such as a “My blog” or “My articles” page and Views makes it very easy to create these types of pages.

FAQs

Q: “My Articles” tab is not appearing.

First, make sure you’ve chosen “<User account menu>” from the Parent drop-down. Second, try rebuilding the site cache (go to Configuration, Performance and click on “Clear all caches”) and see if that fixes it.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Feb 06 2018
Feb 06

Webform allows you to create powerful forms in Drupal without writing any custom code. One feature I want to show you today is predefined options.

If this is the first time you’ve heard of the module and want to learn more check out our two part series on using Webform.

Predefined options ease the creation of forms by offering common lists such as days, months, time zones, titles, etc…

For example, if you want to add a select list where users choose a country, instead of manually entering in all countries yourself, use the predefined one that comes with the module.

Webform comes with around 30 predefined lists which can be added to radio buttons, checkboxes, select list and menus. You can also create your own.

If you have a website that will use the same set of options on multiple forms, look at creating a predefined options list to save time.

In this tutorial, you’ll learn how to create and use predefined options.

Getting Started

This is part of the Webform module, so I’m going to assume you have it installed.

If you’ve never installed it check out our “Getting Started with Webform in Drupal 8” tutorial.

Use a Predefined Option

Let’s now look at using one of the predefined options in a select element. Let’s assume you need to create a select element with days as the options, Monday, Tuesday, etc…

1. From the Webforms page, go to the Build page of any form by clicking on Build.

2. Then click on “Add element”, search for “select” and click on “Add element” on the Select row.

2018-02-02_22-14-10.png

3. Enter in a title for the element, you could call it Day.

4. From the Options drop-down, select Days and then Save.

2018-02-02_22-16-21

5. If you view the form, you should see a drop-down called Day with days as the options.

2018-02-02_22-19-10.png

You just saved yourself the effort of manually filling out the days.

Manage Predefined Options

To manage all the predefined options go to Structure, Webforms, Configuration and then click on Options.

2018-02-02_14-16-48.png

From this page, you can view all the options create custom options and modify existing ones.

Let’s now modify the Days options so that Monday is the first day.

1. Click on Edit on the Days row.

2. Reorder the options so that Sunday is at the bottom then click on Save.

2018-02-02_22-29-27.png

3. Now if you view the form, Monday should be the first option.

Take note, if you’re using the same predefined option on multiple elements and change the options (like we just did), then the change will appear on all elements.

Create Predefined Options

Creating your own predefined options is very easy.

1. While on the Options page, click on “Add options”.

2. Give your options a label and some values, then click on Save at the bottom of the page.

3. Select your predefined options from the Options drop-down on a select or checkbox element and you’re done.

Summary

The editors and marketers who use Webform to create custom forms will love this functionality. Out-of-the-box it comes with a bunch of common options such as days, months, “time zones”, “country codes” and more. On top of that you can create your own custom options which’ll save you a lot of time in the long run.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Jan 30 2018
Jan 30

If you’re planning to use Bootstrap on your Drupal 8 site, the first obvious thing to do is download and set up the Bootstrap theme. Then, during the site building process, there will come the point where you need to create a few layouts. These layouts could be used for content types with Display Suite, or for custom pages using Panels.

Implementing layouts using the Bootstrap grid system is simple thanks to the Bootstrap Layouts module.

Read our “Getting Started with Bootstrap in Drupal 8” tutorial to learn how to use Bootstrap in Drupal 8.

Bootstrap Layouts is a module that ships a bunch of prebuilt layouts using the grid system in Bootstrap. Best of all, these layouts can be used between Display Suite and Panels, or any module which supports the Layout Discovery module.

The layouts are configurable through Drupal’s administrative UI. For example, you can adjust the width of a two column layout by choosing grid CSS classes from a multi-select field.

All this can be done without overriding a template or writing a custom preprocess hook.

In this tutorial, you’ll learn how to use Bootstrap Layouts with two popular modules: Display Suite and Panels.

Getting Started

Before we begin, go download and install the Bootstrap Layouts theme.

If you use Composer, run the following:

$ composer require drupal/bootstrap_layouts

Or Drush,

$ drush dl bootstrap_layouts

Other Requirements

First, make sure you’re using Bootstrap on your Drupal site. This could be via the Bootstrap theme or another theme which implements Bootstrap.

Read our “Getting Started with Bootstrap in Drupal 8” tutorial to learn how to use Bootstrap in Drupal 8.

Second, you’ll need to use it via a module which implements layouts using the Layout Discovery module. So far, two popular modules which use it are Display Suite and Panels.

Implement Layout using Display Suite

To implement this using Display Suite go ahead and install the module.

If you’re new to Display Suite then read our “Using Display Suite in Drupal 8: How to Customize Content Pages” tutorial with a video.

1. Go to the “Manage display” page of a content type. You can access it by going to Structure, “Content types” and click on “Manage display” from the Operations drop-down.

2. From the “Layout for article in default” tab, select a layout under Bootstrap.

3. Once selected you should see a preview of the layout.

Don’t forget to click on Save.

Change Column Width

One thing I love about Bootstrap Layouts is that you can change the column width without overriding the template.

The preview of layout shows it as having two columns, each 50% wide. Or in “Bootstrap speak”, 6 columns wide. You can change the width of the column by choosing a different class from the Classes select box from a region tab.

Implement Layout using Panels

Now that you know how to configure Bootstrap layouts using Display Suite, let’s look at using it with Panels and Page Manager.

If you’ve never used Panels or Page Manager, then check out our tutorial “How to Build Custom Pages Using Page Manager and Panels in Drupal 8“.

I’m going to assume you’ve already created a Panels page and will be switching the layout to one of Bootstrap Layouts’.

1. Go to Structure, Pages and edit an existing page.

2. Go to the Layouts section in the variant.

3. From the Layout drop-down select a Bootstrap layout, then click on “Change Layout”.

4. After clicking on “Change Layout” another link will appear below Layout on the left called “Layout Regions”. From this page you must add the existing blocks into the new regions. Once completed click on “Update and save”.

I must admit, it’s easy to miss this new page, but it’s a required step to changing layouts.

5. Now you should see a new link below Layout called “Layout Settings”.

From this page you can change the layout settings such as the grid CSS classes for each region. Same as when using Display Suite.

Summary

Bootstrap Layouts makes it really easy to build layouts because it comes with many prebuilt. But the most impressive feature is the ability to change the grid classes from the interface without having to override a template.

FAQs

Q: After clicking on “Change Layout” in Panels for a second time I get an error “You must select a different layout if you wish to change layouts.”. But nothing changes.

After clicking on “Change Layout” a new link will appear called “Layout Regions” below Layouts. You must reassign blocks into the new layout regions. This new link can easily be missed after clicking “Change Layout.

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Jan 23 2018
Jan 23

Bootstrap is a front-end framework for building websites. It ships prebuilt CSS and JavaScript components that make building sites fast. It comes with all sorts of common components that every website needs such as a grid system, buttons, drop-down, responsive form elements, carousel (of course) and so much more. As a developer I don’t want to spend time styling yet another button. I just want to know which CSS class to add to an <a> tag so it looks like a button and I’m good to go.

One complaint about Bootstrap is you can spot it a mile away because a lot of developers use the default look-and-feel. When you see the famous Jumbotron you know it’s a Bootstrap site. But with a little bit of effort you can make your site look unique.

Now, another good reason to use Bootstrap is when you’re working with an agency who will design the site but has no Drupal expertize. If they can supply HTML wireframes using Bootstrap then it’s much easier to implement them into Drupal. Yes, in the perfect world it’ll be great to receive a fully built Drupal theme which can be enabled and that’s it. But in reality, most design agencies, unless they specialize in Drupal, don’t know it and this is where Bootstrap can help.

By using Bootstrap, it’ll be easier to work with designers and design agencies because you can discuss things using Bootstrap terminology. For example, a designer may ask, “can we have a sidebar on the right which is 3 columns wide”? Or, they may ask, “make this button on the blog listing page small”.

If you have experience using Bootstrap you should know what I mentioned above.

Luckily for us building a Drupal site using Bootstrap is easy thanks to the Bootstrap theme.

In this tutorial, you learn the following:

  1. Bootstrap theme configuration
  2. Create a Bootstrap sub-theme
  3. Compile Bootstrap locally using Sass

What About Bootstrap 4?

The Bootstrap theme as of this writing only supports Bootstrap 3. For details look at #2554199 in the issue queue.

Other Bootstrap Based Themes

Another theme you should evaluate is the Radix theme. If you know of any other Bootstrap based themes please leave a comment.

Getting Started

Before we can begin, go download the Bootstrap theme from drupal.org.

If you use Composer, run the following:

$ composer require drupal/bootstrap

Or Drush,

$ drush dl bootstrap

Configuring Bootstrap Theme

Before we jump into the advanced topic of sub-theming or compiling Sass. Let’s first look at what the theme offers out-of-the-box. In this section, we’ll look at some of the theme options.

The level of configuration in a Drupal theme varies a lot, however, the Bootstrap theme has a healthy amount of options. Enough to give you flexibility but not too much that you’re overwhelmed or confused.

Once you’ve downloaded the theme go to Appearance and click on “Install and set as default” on the Bootstrap theme.

Hover over 'Install and set as default'

Once installed click on “Settings”.

Bootstrap Settings

From the “Override Global Settings” you can configure common Drupal options such as the logo or favicon. If you’ve configured a Drupal theme in the past this should look familiar.

Override Global Settings options in Bootstrap

The vertical tabs under “Bootstrap Settings” is where you can configure specific Bootstrap options, let’s look at a few.

Fluid container

The “Fluid container” option,  which can be accessed by going to General then Container, this option adds the container-fluid class which’ll display the main region at full width. By default, the main region has a width of 1200px.

For more details check out the Containers section.

Images

While still under General, click on the Images field-set. This option let’s you configure how images will be handled.

Leave “Responsive Images” checked. This adds an img-responsive class to images making them responsive. It adds a max-width:100% so images are resized when viewed on mobile.

The “Default image shape” drop-down allows you to choose a different style for images which is done via CSS. I always leave this set to “None”.

For more details check out the Images section.

Advanced

Let’s now jump down to the “Advanced” section.

In this section, you can configure if Bootstrap will be served from a CDN and which version you want to use.

Theme

From the “Theme” drop-down you can choose a different theme. Most of the options come from Bootswatch.

So your Bootstrap site could go from this:

To this:

Tip: After I selected a theme I had to rebuild the site cache. Go to Configuration, Performance and click on “Clear all caches”. By the way, I chose Slate if you’re wondering.

Loading Bootstrap via a CDN is quick to setup, but hard to customize. If you’re planning to use a CSS processor such as Sass or Less then you’ll need to store Bootstrap locally and compile it.

We didn’t cover every option under “Bootstrap settings”, just the main ones. The rest you can figure out on your own.

Create Bootstrap Sub-theme

So far we’ve looked at changing options within the theme which is great for testing, but if you’re going to use Bootstrap on a proper project then it’s recommended you create a sub-theme.

Why Sub-theme?

When you create a sub-theme, the Bootstrap theme will be the “base theme” which means your sub-theme will automatically inherit all templates and assets such as the CSS and JavaScript.

Your sub-theme will automatically use any template from the base theme unless it’s overridden. If you want to modify the page.html.twig then simply copy it from Bootstrap into your sub-theme templates directory and customize. Drupal will automatically pickup the page.html.twig in your sub-theme instead of the one in Bootstrap.

You should never modify the Bootstrap theme. This way you can keep the Bootstrap theme up-to-date.

If you want to learn more about sub-themes in general. Check out the Creating a Drupal 8 sub-theme, or sub-theme of sub-theme documentation page.

Bootstrap Sub-themes

The way you create a sub-theme can vary. Some themes offer a Drush command to create a sub-theme while others offer starter kits which you just copy and change a few file names.

Bootstrap comes with three starter kits: CDN, Less and Sass. You can see them by going into the starterkits folder in the theme.

If you’re happy with Bootstrap being served over a CDN then choose the CDN kit. If you want to compile Bootstrap using Less or Sass (CSS pre-processors) then choose the corresponding starter kit.

There’s a lot of benefits in compiling Bootstrap CSS locally. You can customize it further and modify the variables which lets you change the colors, headings, fonts and more.

Step 1: Create Sub-theme

Let’s now create a sub-theme using the Sass starter kit. Why Sass? Well that’s my CSS pre-processor of choice. But the following steps can be applied to the other starter kits.

1. Go into the Bootstrap theme and copy the sass folder from starterkits and then paste the folder into /themes/custom.

2. Rename the folder from sass to bootstrap_sass (you can rename it to whatever you want). Once copied and renamed, the path to the sub-theme should be /themes/custom/bootstrap_sass.

Are you required to add sub-themes into a custom folder? No, it’s just best practice when it comes to managing sub-themes.

Your themes folder should look something like this:

3. In the bootstrap_sass sub-theme, replace all instances of THEMENAME in the file name to bootstrap_sass.

Look for the following files:

  • THEMENAME.libraries.yml   => bootstrap_sass.libraries.yml
  • THEMENAME.starterkit.yml => bootstrap_sass.info.yml (NOTE: make sure you change starterkit to info)
  • THEMENAME.theme            => bootstrap_sass.theme
  • /config/install/THEMENAME.settings.yml => bootstrap_sass.settings.yml
  • /config/schema/THEMENAME.schema.yml => bootstrap_sass.schema.yml

4. Now we need to open up a few files and perform a find and replace on the string THEMENAME.

Open the following files:

  • bootstrap_sass.info.yml: Give your sub-theme a name such as “Bootstrap Sass” and find all THEMENAME and replace them with bootstrap_sass.
  • /config/schema/bootstrap_sass.schema.yml: Find all instances of THEMENAME and replace with bootstrap_sass.

Step 2: Download Bootstrap

In the above section we just copied a folder and did a find and replace in the THEMENAME string. Now we need to download the actual Bootstrap library and compile the Sass.

1. Go to the Bootstrap Getting Started page and download the Sass version.

2. Extract the downloaded file into bootstrap_sass, once extracted the path should be bootstrap_sass/bootstrap. You may have to rename the folder after it’s extracted.

3. Now we need to copy over the variables in Bootstrap into our Sass files. This will allow us to override the variables without having to modify the actual Bootstrap library.

Go to bootstrap_sass/bootstrap/assets/stylesheets/bootstrap/_variables.scss and copy the variables into bootstrap_sass/scss/_default-variables.scss. Paste them just below the $icon-font-path variable.

Sass allows you to override variables. When we compile our Sass files, it’ll use the variables in _default-variables.scss instead of the default _variables.scss that ships with the library.

Step 3: Compile Sass using laravel-mix

The final piece to complete this sub-theme is to compile Sass. I won’t go into details on how to install and configure Sass, that’ll be a whole tutorial or two on it’s own. Instead, I’ll show you one of many ways to compile Sass. We’ll use laravel-mix which is a wrapper on top of webpack. Before you begin make sure you download and install Node.js.

Want to use Yarn instead? Look at this issue on GitHub.

1. Go into the sub-theme directory and create a package.json, you can do this by running the following command:

$ npm init

Just follow the prompts. Once complete you should see /themes/custom/bootstrap_sass/package.json.

2. Then install laravel-mix with the following command:

$ npm install laravel-mix

3. In the sub-theme create another file called webpack.mix.js and add the following to it:

let mix = require('laravel-mix');

mix.sass('scss/style.scss', 'css/');
mix.options({
  processCssUrls: false
});

The code above is pretty straightforward, it’ll tell laravel-mix to compile scss/styles.scss into the css directory. Once compiled there will be a single file styles.css and the path will be css/styles.css.

4. Open up package.json and replace the scripts section with the one below:

"scripts": {
  "dev": "NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
  "watch": "NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
  "hot": "NODE_ENV=development webpack-dev-server --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
  "production": "NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},

This adds NPM scripts which you’ll need to use to compile the Sass. You’ll need to run these commands from within the sub-theme.

The two important scripts are:

$ npm run watch

Use this script to automatically compile when a file is changed. This should be used locally while developing a site.

$ npm run production

This script should be run when you’re ready to deploy to production. It’ll uglify the CSS and JavaScript file to reduce the size.

Summary

I hope now you have a better understanding on how to use Bootstrap in your next Drupal project. From a developer’s perspective, it’s great because you can focus on building the site without dealing with small design issues such styling a button. The Bootstrap theme has a good mix of configuration and flexibility. You can use the theme to quickly spin up a website, however, it’ll look very Bootstrappy. Or you can still use it if you want to fully customize the look-and-feel by compiling it yourself.

Other Bootstrap Modules for Drupal 8

There’s a whole bunch of modules which help you use Bootstrap in other parts of Drupal. There’s a page on drupal.org called Bootstrap related modules which lists them out. The two modules I always use with Bootstrap in Drupal 8 is Bootstrap Layouts and Bootstrap Paragraphs.

Bootstrap Layouts lets you configure a layout using its grid system in Panels and Display Suite. Bootstrap Paragraphs ships a bunch of pre-built paragraph types for Bootstrap.

We’ll cover both modules in more detail in future tutorials.

FAQs

Q: I created a sub-theme but I can’t see it on the Appearance page?

Make sure you change THEMENAME.starterkit.yml to bootstrap_sass.info.yml, replace starterkit with info.

Q: I enabled my sub-theme but the site looks broken.

This can happen when you enable a sub-theme before the parent. The simple workaround is to uninstall your sub-theme and then install the Bootstrap theme first, then install the sub-theme.

Q: None of the JavaScript files are getting loaded?

First, make sure you renamed THEMENAME.libraries.yml to bootstrap_sass.libraries.yml. Then in bootstrap_sass.info.yml make sure you update the libraries sections.

From this:

libraries:
 - 'THEMENAME/global-styling'
 - 'THEMENAME/bootstrap-scripts'

To this:

libraries:
 - 'bootstrap_sass/global-styling'
 - 'bootstrap_sass/bootstrap-scripts'
Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

Aug 15 2017
Aug 15

In part two of our Webform tutorial, we’ll show you how to create multipage forms, apply conditional logic, create layouts and much more!

We’ll take the simple newsletter signup form created in part one of this tutorial and add additional pages. Then we’ll demonstrate how to show or hide an element depending on the selection made on another element. We’ll also look at layouts and then finish off with an overview of some of the other great features Webform has to offer.

Multipage Forms

For forms with many elements, it’s best to spread them across two or more pages. In this section, we’ll take the form we created in part one and move some of the elements to make a two page form. We’ll also add a preview page and make changes to the confirmation screen.

1. Starting from the Edit tab of the Webform created in part one, click on “Add page”.

Screenshot highlighting the Add page button which is above the first element.

2. Give the first page a title of “Your details”.

3. If you want to change the default “Previous page” and “Next page” text then you can do this in the “Page settings” section. We’ll stick with the defaults.

4. Click on Save to create the page.

5. Repeat the process to create a page called Feedback.

Screenshot showing the two new pages added at the bottom of the Webform elements.

6. On the Edit tab, drag the “Your details” page to the top.

7. Drag the “First name” and Email elements to the right a little so they are indented as shown below.

8. Drag the Feedback page above the checkboxes.

9. Drag the checkboxes and radio buttons to the right so they are also indented.

10. Click on “Save elements”.

Screenshot showing the pages moved to the correct places, as discussed in the text.

Clicking on the View tab will reveal a multipage form. You’ll see the page names on the progress bar at the top of the form. You can remove the progress bar in the form settings if you prefer.

Screenshot of the multipage Webform.

Preview and Submission Complete Pages

For long forms, it can be useful for users to preview the information before submitting it. Also, the default message a user receives after clicking on submit is “New submission added to Newsletter signup” or similar and so changing the message is normally a good idea. We can make both of these changes from the Settings tab for our Webform.

1. From the Edit tab of our form, click on the Settings sub-tab.

Screenshot highlighting the Settings sub-tab with is under the main Edit tab for a Webform.

2. Scroll down to the “Preview settings” section. The Optional radio button will allow users to skip the preview screen. We’ll select Required, so that users will always preview the information before submitting it. You can alter various aspects of the preview page but we’ll stick with the defaults.

3. Scroll further down the page to the “Confirmation settings” section.

4. It’s worth reading through the options under “Confirmation type”. We’ll stick with the default of Page as this will work well for our simple example.

5. For “Confirmation title”, enter “Newsletter signup successful”.

6. Enter “Thank you, [webform-submission:values:first_name]. You have signed up to our newsletter.” for the “Confirmation message”.

Screenshot of confirmation settings

7. Scroll down to the bottom of the page and click on Save.

When the “First name” element was initially added to the Webform, a key of first_name was created and we’ve used this in our confirmation message. You could also use the information from other form elements by replacing first_name with the appropriate key.

You can find the key listed on the Edit tab of the form, under the Key column, although that column may be hidden on smaller screens. Also, if you edit an element, you’ll see the key shown in small text to the right of the title.

Now if you click on the View tab and fill in the information on the form, you’ll have a preview screen. After signup, you’ll also have a personalized message.

Screenshot showing that the first name entered on the form becomes part of the confirmation message.

Conditional Logic

On page two of our wizard, we have a question asking about interests and then another specifically about JavaScript. Ideally, we only want to show the JavaScript question if the user has expressed an interest in it. This is where conditional logic helps. We can set the second question to respond to the results of the first.

1. From the Edit tab of our form, click on the Edit button for “Which JavaScript framework are you most interested in?”.

2. Scroll down to the “Conditional logic” section.

3. Change the State to Visible.

4. Select “JavaScript [Checkboxes]” under “Element/Selector”.

5. Under Trigger, select Checked.

Screenshot showing conditional logic being added.

6. Click on Save.

Now if you view the second page of the form, you won’t see the question about JavaScript frameworks unless you have selected the JavaScript checkbox.

Screenshot showing that the JavaScript library question only appears if the JavaScript checkbox in the first question is checked.

Conditional logic can be used to show or hide elements, disable them or make them required, depending on the state of other elements. It’s always worth testing that the logic performs as you expect it to, especially for complex forms.

Displaying Webforms

In this section, we’ll show how to change the default URL. We’ll also demonstrate how to attach a Webform to a node and how to display it in a block.

Changing the URL

By default, Webforms have a URL of “/form/name-of-form”, so in our case it’s “/form/newsletter-signup”. You can change the form part of the URL to another word for all forms within the global Webform Settings tab (administrative toolbar, Structure, Webforms). Instead of doing that, we’ll add an alias for our form.

1. From the Edit screen of the form, click on the Settings sub-tab.

2. Scroll down to the “URL path settings” section.

3. Here you can add URL aliases. We’re going to use “/signup” for the first box and “/signup-complete” for the second.

Screenshot showing the URL path settings being added.

4. Click on Save.

Now, both the form and the confirmation page will have a shorter URL.

Attaching a Webform to a Node

Webform also allows you to attach a form to a node. In this example, we’ll attach a node to the “Basic page” content type.

1. From the administrative toolbar, click on Structure and then “Content types”.

2. Click on “Manage fields” for “Basic page”.

3. Click on “Add field”.

4. Under “Add a new field”, select Webform.

5. Give the field a label, such as “Newsletter signup”.

6. Click on “Save and continue” and then “Save field settings” on the next screen.

7. You should now be on the Edit tab for the new field. In the “Default value” section, select “Newsletter signup” from the list.

8. Click on “Save settings”.

As with any field, you’ll be able to adjust its position relative to other fields, so you can move the Webform to any part of the node.

Now when you create new content using the “Basic page” content type, you’ll have the newsletter signup Webform attached.

Screenshot showing a node with a Webform attached.

Displaying a Webform in a Block

Another option for displaying a Webform is to create a block. This offers flexibility on where the Webform can be placed on the page.

1. Navigate to Structure on the administrative toolbar, and then “Block layout”.

2. Next to the appropriate region of your theme, click on “Place block”. We’re going to add the block to “Sidebar second” for our Bartik theme.

3. Find Webform in the list and click on “Place block” next to it.

4. Change the title to “Newsletter signup”.

5. Under Webform, type News and select “Newsletter signup” when it appears.

6. In the Visibility section, adjust the settings as you would with any block. We’re going to enter “/signup*” for “Hide for the listed pages” on the Pages tab, so the block will be hidden on the “/signup” and “/signup-confirmation pages”.

7. Click on “Save block” to complete the process.

The signup form now appears in a block on the right of our screen for most pages.

Screenshot of the Webform in a block.

Note that Webform adds another tab to the Visibility section for block configuration. This allows you to select which Webforms the block should be displayed on.

Creating Layouts

To simplify laying out elements on a page, Webform includes a variety of containers including divs, expandable details and fieldsets. If you add a new element, you’ll see all the containers listed together.

Screenshot listing the containers - Container, Details, Fieldset, Flexbox layout, Item, Label.

In this section, we’ll look at the Flexbox container and use it on the second page of the form. This will allow the two questions to sit side-by-side on a large screen, but they’ll automatically be vertically stacked on smaller screens.

1. From the Edit screen of our Webform, click on “Add element”.

2. Find “Flexbox layout” in the list and click on “Add element” on the same line.

3. Give the element a key, such as newsletter_interests.

4. The defaults will work fine for this example, so click Save to create the container.

5. Drag the new Flexbox layout element, so that it’s just below Feedback and make sure it’s indented.

6. The checkboxes and radio buttons should now be below the Flexbox layout element. Move them both to the right so they are further indented.

Screenshot showing flexbox layout with indented questions underneath.

7. Click on “Save elements” to complete the process.

Now when you fill in page two of the form, when the JavaScript box is ticked, the second question will appear to the right on large screens. If there isn’t enough room for the questions to be side-by-side then the second question will drop down below the first.

Screenshot showing two questions side-by-side.

This is just a simple example of what’s possible with layouts. Later in this tutorial, we’ll install the Webform Examples module and the “Example: Layout: Flexbox” form shows how many different elements can be displayed across a page.

Even More Features

We could carry on writing about the Webform module for weeks as it includes so many great features. In this section, we’ll give a brief overview of some other features that are definitely worth looking at.

Reducing SPAM

Any form on the internet will be a target for spammers so it’s essential to have systems in place to reduce this to a minimum. Webform works with the spam protection modules Antibot, CAPTCHA and Honeypot and using a combination of these should help cut down on unwanted messages.

Head to Structure on the administrative toolbar and then Webforms. Click on the Add-ons tab and then scroll down to the “Spam protection” section and find the links to each of the modules.

Once installed, to configure Antibot and Honeypot, click on Webform’s global Settings tab. Then expand “Third party settings” within the “Webform settings” section. For CAPTCHA, there is an element that can be added to any Webform.

YAML

The Edit tab on a Webform has a “Source (YAML)” sub-tab which exposes the underlying YAML markup. This allows you to copy code to another form, add more elements and make changes to forms. For forms that use a lot of similar elements, copying and pasting with the appropriate changes can be a lot quicker than manually adding each element.

In the code below, which is the first section of our YAML markup, we’ve added a Surname text field by copying the markup for first_name and editing it. We’ve also changed the title for the second page from Feedback to Interests.

your_details:
  '#type': wizard_page
  '#title': 'Your details'
  first_name:
    '#type': textfield
    '#title': 'First name'
    '#required': true
  surname:
    '#type': textfield
    '#title': 'Surname'
    '#required': true
  email:
    '#type': email
    '#title': Email
    '#required': true
feedback:
  '#type': wizard_page
  '#title': Interests

Saving the form and clicking on the View tab shows the new form element in place and the new name for the second page of our form.

Screenshot showing that Surname has been added and the second page is now called Interests.

If you’ve not used YAML before then be very careful with spaces. When items are nested then always use two spaces to indent. Thankfully the interface will point out any lines that have been incorrectly formatted. The screenshot below shows what happens when you add an extra space.

Screenshot showing that there is an indentation issue near surname in the YAML file.

Note that some changes made to the YAML markup will require you to remove data first. For example, if we had also changed the key for the Feedback page, which is shown as feedback: in the YAML code above, then we would have needed to clear submissions or delete the page in the UI and then re-create it.

You can find out more about exporting and importing Webforms using YAML in this video.

Debugging

To help track down issues, you can enable debugging for a form. Start off at the Edit tab of the form and click on the “Emails / Handlers” sub-tab. Then you just need to click on “Add handler” and follow through the screens to add a Debug handler. The screenshot below shows the type of information that will be displayed as you move through a form.

Screenshot showing debugging output with keys and values entered for each element.

The Examples Module

To get an idea of the capabilities of Webform, it’s a good idea to look at the Webform Examples module. You can enable this from the Extend tab of the administrative toolbar or by using Drush with the following command:

drush en webform_examples

This will install many Webforms that demonstrate different aspects of the module.

Screenshot listing the nine example Webforms available.

The “Example: Style Guide” is a good starting point as it shows all the different elements and also has some photos of cute kittens.

Settings, Modules and Add-ons

If you have been following along with this tutorial, you will have seen a huge array of settings. It’s worth spending some time looking through all the global settings available for Webform as well as the settings for individual Webforms and for different elements. These are just some of the settings available:

Screenshot listing some of the many Webform settings.

Webform includes a number of modules including starter templates and dev tools and you can view these on the Extend tab of the administrative toolbar by filtering using the word Webform. If you need to extend the functionality of Webform further, then the first place to look is the Add-ons tab.

Summary

In this part of the tutorial, we’ve looked at multipage forms and shown how to display Webforms in a variety of ways. We’ve used conditional logic to show or hide an element depending on the state of another element. We’ve also given an overview of some of the other great features included in the Webform module.

FAQ

Q: Is there an online demo of Webform?
You can test the features of Webform on simplytest.me.

Aug 01 2017
Aug 01

The Webform module in Drupal 8 makes it easy to create complex forms in no time. The basics are easy to learn but when you start digging below the surface, the huge power of the Webform module starts to reveal itself.

While the Contact module in Drupal 8 core does allow you to create simple personal and site-wide contact forms, functionality is limited. This is where Webform module steps in.

In the first part of this tutorial, we’ll use some Webform elements to create a simple but fully functioning form. We’ll show what you can do with the results of submissions and then add some additional elements. We’ll also demonstrate how one of the built-in JavaScript libraries can improve the look of form elements.

In part two, we’ll add additional pages to our Webform, apply conditional logic, show how to create great layouts and much more!

Getting Started

The simplest way to install Webform is to use Drush. The following three commands download and enable the Webform module and then download all the required libraries. This tutorial is based on Webform version 8.x-5.0-beta15.

drush dl webform
drush en webform webform_ui
drush webform-libraries-download

If you get “Unable to unzip” errors then install a command line tool capable of unzipping files and try again. On our minimal CentOS setup, we needed to install the unzip package.

Alternatively, you can download the module. Note, although it’s better to download the libraries, Webform will use a CDN if any libraries are not available locally.

To see what libraries are used and to check the status of each, from the administrative toolbar, click on Reports, then “Status Report” and look for the entries that start with “Webform library”. Specific libraries can be disabled within Webform’s Settings tab if required.

Screenshot showing the status of Webform libraries.

If you plan to allow users to upload files using a Webform then please read the note below about setting up private files. Incorrect configuration could be a significant security risk.

A Quick Tour of the Webform Interface

To add and manage Webforms, click on Structure on the administrative toolbar, then Webforms. You should see a screen similar to the one below.

Screenshot highlighting six different areas in the Webform interface.

Some of the points of interest are:

1. The tabs along the top are self-explanatory and we’ll look at these throughout the tutorials.

2. You’ll see “Watch video” buttons in various places in the Webform module. These short videos are a great way to learn about Webform features.

3. Add a new Webform.

4. The “How can we help you?” button is a quick way to find out more about the module, report issues and become involved with the Drupal community.

5. “Filter webforms” is useful if you have a large number of Webforms.

6. Buttons for each Webform allow you to download submissions and edit forms.

Creating a Simple Form

In this tutorial, we’ll start off with a very simple newsletter signup form and then later we’ll add more complex elements. We’ll initially create two elements – first name and email address. One way to do this would be to duplicate the existing Contact form by clicking on the down arrow on the Edit button and then selecting Duplicate. We could then edit the form. The other way is to create the form from scratch and we’ll show you how to do that here.

1. Click on the “Add webform” button.

2. Give the form a name such as “Newsletter signup” and an appropriate description if you want.

3. If you plan to have a lot of forms then adding categories can be useful. We’ll add a Newsletter category by selecting the “Other…” option.

4. Click on Save to create the form.

Screenshot of the add Webfrom screen.

On the next screen, we can start adding elements to our form.

1. Click on the “Add element” button.

2. Use the “Filter by element name” box at the top to find “Text field” and click on “Add element” next to that.

Screenshot of the text filter helping to find the text field element.

3. Enter “First name” for the title.

4. We’ll leave all the other settings as they are. Click on Save to finish.

5. Click on “Add element” again.

6. This time, find the Email element and click on “Add element”.

7. Enter a title of Email and click on Save.

Note, if you wanted to make sure the user entered their email address correctly then you can use the “Email confirm” element which adds an additional confirmation box. Many of the other elements also have variations, so it’s worth reviewing these to make sure you’ve picked the best element for your needs.

You should now have a screen that’s like this.

Screenshot of the edit tab after two elements have been added.

Click on the View tab to see the form.

Screenshot of the view tab after two elements have been added.

To keep matters simple to start with, we’ve used a lot of the default settings for the elements. While these settings work well for a lot of cases, it’s worth looking through the different options for each element type as there are a number of ways to customize the way an element appears.

There are a couple of things that would be good to change at this stage. Firstly, it may be better to change the Submit button text to Signup. Also, currently neither of the fields are required, so a user could submit a blank form.

1. Click on the Edit tab.

2. To the right of “Submit button(s)”, click on Customize.

Screenshot showing that the customize button is below the other elements.

3. Enter Signup under “Submit button label”. We’ve also changed the Title to Signup.

4. Click on Save.

5. Tick Required for both the first name and email elements and then “Save elements”.

Screenshot highlighting the required checkboxes next to each element.

Note, with a customized submit button you can adjust the position of it relative to other elements if you wanted to.

Now, when you click on the View tab, you should see that both fields are required and the submit button is labelled Signup.

Screenshot showing that the elements are now required and the submit button is now labelled Signup.

Testing the Form and Viewing Results

To test the form, you can either manually add data and submit it, or you can use the Test tab to generate data. If the Devel generate module is installed then you can use that to automatically generate Webform submissions.

Screenshot of the test tab with information automatically added.

Signup to the newsletter a few times and then click on the Results tab. You can click on the Customize button to change the columns shown if required. You can also mark particular submissions with a star or add administrative notes and we’ve done both for submission number 5. Looking at our list of automatically generated submissions, it appears that the Webform maintainers like The Beatles!

Screenshot of the webform submissions with first names of John, Paul, George and Ringo.

The Edit button next to each submission has a number of options including editing, viewing and deleting submissions. The Download sub-tab allows you to export the submissions in a variety of ways and you can filter based on date or submission ID, so you don’t need to download all the data in one go.

Screenshot of the download sub-tab.

Emailing Results

While the Results tab allows you to review submissions, it can also be useful for results to be emailed once submitted.

1. Head to the Edit tab and click on the “Emails / Handlers” sub-tab.

2. Click on “Add email”.

3. In the “Send to” and “Send from” sections, we’ll just use the default settings. This will use the email address and name that are configured for the site (administrative toolbar – Configuration, “Basic site settings”).

4. The email that’s sent can be customized in the Message section if required but we’ll just stick with the default message.

5. Click on Save to create the email handler.

Screenshot after the email handler added.

You can also add handlers to post submissions to a remote URL and enable debugging.

Adding Checkboxes and Radio Buttons

In this section, we’ll add checkboxes and radio buttons and enhance their appearance using the jQuery iCheck library. If you filter elements using the word “checkbox” then you’ll see five different options.

  • Checkbox – a single checkbox.
  • Checkboxes – a group of checkboxes using a custom or pre-defined lists.
  • Checkboxes other – a group of checkboxes with an “Other …” option to allow the user to enter their own information.
  • Entity checkboxes – checkboxes using entity references.
  • Term checkboxes – checkboxes using taxonomy terms.

We’re going to create checkboxes asking the user about their interests.

1. From the Edit tab, click on the Elements sub-tab, then “Add element”.

2. Use the filter to help find “Checkboxes other” and select “Add element”.

3. Give it a title of “What are your main interests?”.

4. With long titles, it’s often worth shortening the associated key as this will be used for CSS classes and referred to in other areas of Webform. Click on the Edit link to the right of the title and change the key to main_interests.

Screenshot of title and key for checkboxes other element.

5. Scroll down to the “Elements options” section.

6. The Options list allows you to select from pre-defined lists such as days of the week. For our example, we want to enter values, so leave Options set to “Custom…”.

7. For each option, we need to add a value and text pair. The “Option value” is used internally and you’ll see the values in the markup and CSS IDs and classes that are added. The “Option text” will appear to the user on the form. We’ve created three options with pairs html / HTML, css / CSS and js / JavaScript, as shown below.

Screenshot of value - text pairs.

8. Scroll down to the “Other option settings” section. These settings apply to an additional item that will be added to the end of the list of checkboxes. The default settings create an “Other…” checkbox with an additional textfield if other is selected. The settings are customizable but for our example we’ll stick with the defaults.

9. Click on Save to complete the process.

Note, in the “Elements options” section, the number of columns that checkboxes are displayed in can be adjusted. We’ve stuck with a single column as we’ll be showing how to layout two elements next to each other in part two of this tutorial.

Using a similar procedure, other elements such as radio buttons and select lists can be created. We’re going to add radio buttons.

1. Add the Radios element.

2. Give it a title of “Which JavaScript library are you most interested in?”.

3. Change the key to js_library by clicking on Edit to the right of the title.

4. Add options for jQuery, AngularJS and React as shown below.

5. Leave all the other options at their default settings and click on Save.

Screenshot of value - text pairs.

In part two of this tutorial, we’ll use conditional logic to show how you can hide this question unless someone selects the JavaScript checkbox under “What are your main interests?”.

Clicking on the View tab should reveal elements that look similar to this.

Screenshot of Webform view tab with checkboxes and radio buttons.

Enhancing Checkboxes and Radio Buttons

As you can see from the screenshot above, checkboxes and radio buttons have the default look. Thankfully, Webform includes the jQuery iCheck library and this makes it simple to enhance these elements. You can enable iCheck for individual elements by editing their properties and scrolling down to “Enhance using iCheck”, but we’ll show how to enable it globally.

1. From the administrative toolbar, click on Structure, Webforms and then the Settings tab.

2. Expand the “Element Settings” section.

3. Within that section you’ll find “Checkbox/radio settings”. Expand this also.

4. Select an option from “Enhance checkboxes/radio buttons using iCheck” that works with your theme (it’s worth trying the different styles). In our screenshots, we’ve used the “Square: Blue” option.

5. Scroll to the bottom of the screen and click on “Save configuration”.

Now our Webform has greatly enhanced checkboxes and radio buttons.

Screenshot with enhanced checkboxes and radio buttons.

Other Elements

We’ve only discussed a few elements from a very long list. It’s worth spending time looking through the full range of Webform elements and trying them out.

If you add any of the file elements then please ensure that you’ve followed advice about file uploads. Allowing non-trusted users to upload files to public folders can be a huge security risk. When using a private folder make sure you’ve secured the folder correctly and ideally, only allow trusted users to upload files. It’s worth reading “Drupal file upload by anonymous or untrusted users into public file systems – PSA-2016-003” before implementing file elements on a production server.

Summary

In this part of the tutorial, we’ve shown how to create a simple form and view submissions. We’ve discussed Webform elements and looked at how to enhance checkboxes and radio buttons.

In part two, we’ll show you how to create multi-page forms, use conditional logic to show and hide elements, create layouts and much more!

FAQs

Q: How can I get more information about Webform?
The Webform documentation page has lots of useful information and videos.

Q: What are the differences between Webform and the Contact module?
You can find a detailed comparison here.

Jul 11 2017
Jul 11

The ability to create a form quickly and easily is a vital piece of functionality in any content management system. A content editor needs the capacity to create a form and add or remove fields.

The days of asking a developer to create a custom form are long gone. An editor should be able to spin up a form for whatever they need.

Luckily Drupal 8 has two good options for building forms: Contact and Webform.

Contact

The Contact module in Drupal 7 and below has always been the go-to module for basic forms as long as you’re happy with the hard-coded fields. If you need an extra field, you would have to write custom code to add it.

Now in Drupal 8, you’re no longer stuck with a single form. Instead, you can create different fieldable contact form types. You can create different contact forms and attach fields to them, the same way as you do on content types.

The Contact module won’t keep any form submissions in Drupal. It’ll send them to a designated email address. To store submissions use Contact Storage module.

Webform

Webform is the original form builder for Drupal. If content editors needed the ability to create forms then this is the module they would use. The module is suited for basic contact forms as well as long multi-page forms.

The 8.x-5.x version of Webform started out as YAML Form and it was decided to make YAML Form the Drupal 8 version of Webform.

Webinar on Contact and Webform

I recently recorded a webinar on Contact and Webform, where I cover both modules and show you how to create a form in each one.

Watch the webinar above or directly on YouTube.

Or jump to a specific section using the links below.

Contact Module

  • What’s new in Contact module (02:03)
  • Manage contact form types (04:34)
  • Create Contact form type (05:05)
  • Default fields on Contact form (06:37)
  • Add field to contact form (07:44)
  • View submission (09:01)
  • Configure “Manage display” (09:57)

Webform Module

  • What’s new in Webform module (12:25)
  • Install Webform (14:11)
  • Overview of Webform admin page (16:31)
  • Create form using Webform (18:33)
  • Adding elements to forms (18:56)
  • Add pages to forms (21:17)
  • Conditionally display fields (22:20)
  • Form settings (28:58)
  • Add email notification to form (31:15)

Webform Integration with Google Sheet

  • Introduction to Zapier (36:30)
  • Create zap in Zapier (38:40)
  • Configure Webform to send post to webhook (39:36)
  • Test integration with Google Sheets (43:27)

Questions

  • Question: how to prevent spam (46:08)
  • Question: Views integration with Webform (55:25)

Mentioned Resources

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 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 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.

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.

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.

Feb 21 2017
Feb 21

My last post talked about how Docker microcontainers speed up the software development workflow. Now it’s time to dive into how all this applies to Drupal.

I created a collection of Docker configuration files and scripts to make it easy to run Drupal. If you want to try it out, follow the steps in the README file.

The repository is designed using the microcontainers concept, so that each Drupal site will end up with 3 containers of it’s own (Apache, MySQL and Drush containers), which are linked together, to run our application. If you want to serve a new site, you need to create separate containers.

In theory you could re-use containers for different web applications. However, in practice, Docker containers are resource-cheap and easy to spin up. So it’s less work to run separate containers for separate applications than it is to configure each application to play nice with the other applications running on the same container (e.g.: configuring VirtualHosts and port mappings). Or at least this is what my colleague M Parker believes.

Plus, configuring applications to play nice with each other in the same container kind of violates the “create once, run anywhere” nature of Docker.

How it works

My repository uses the docker-compose program. Docker-compose is controlled with the docker-compose.yml file, which tells Docker which containers to start, how to network them together so they serve Drupal, and how to connect them to the host machine. This means serving the Drupal repository filesystem and mapping a port on the host machine to one of the ports in one of the containers.

A useful tip to remember is that docker-compose ps will tell you the port mappings as shown in the screenshot below. This is useful if you don’t map them explicitly to ports on the host machine.

Docker terminal

Networking

If you’ve ever tried setting up a bunch of containers manually (without docker-compose), it is worth noting (and not very well documented in the Docker docs, unfortunately) that you don’t need to explicitly map port 3306:3306 for the mysql container, because docker-compose sets up a miniature network for containers run from the same docker-compose.yml. It also sets up hostnames between each container in the same docker-compose.yml. This means that the web container can refer to the mysql-server machine with the hostname mysql-server, and, even if you implicitly map 3306 to some random port on the​ host machine, web can talk to mysql-server on port 3306.

Note in this case that the container running MySQL is named db, so, when you’re installing Drupal, on step 4 (“Database configuration”) of the Drupal 7 install script, you have to expand “Advanced options”, and change “Database host” from localhost to db!

Filesystem

It is possible to put the Drupal filesystem into a container (which you might want to do if you wanted to deploy a container to a public server). However, it doesn’t really make sense for development, because most of the time, you’re changing the files quite frequently.

To get around this requirement for a development environment, we mount the current folder (often referred to as ‘.’) to /var/www/html in the container, which matches where the current directory is mounted in all three containers. This is done with the ‘volumes’ directive in the docker-compose.yml file. The ’working_dir’ directive says “when you run the Drush command in the Drush container, pretend it’s running from /var/www/html”, which is the equivalent of ‘cd /var/ww/html’ before you run a drush command.

So when you run the Drush command in the Drush container, it sees that it’s currently in a Drupal directory and proceeds to load the database connection information from sites/default/settings.php which tells it how to connect to the mysql on the db container with the correct credentials. (recall the links directive makes sure that the drush container can access the db container so it can connect to it on port 3306).

The Drush container

The drush container is a bit special because it runs a single command, and is re-created every time a Drush command is used.

If you look at the step 9 of my Docker configuration files you’ll see it says…

  • Run Drush commands with:
USER_ID=$(id -u) docker-compose run --rm drush $rest_of_drush_command

… i.e.: docker-compose run --rm drush i.e. start the container named drush, pass it $rest_of_drush_command

Docker terminal containers

If you look at the Dockerfile for Mparker’s Dockerfile, you’ll see it contains a line saying ‘ENTRYPOINT [“drush”]’. ENTRYPOINT is a variant of the CMD command which passes all the rest of the ‘docker run’ parameters to the command specified by the ENTRYPOINT line.

So what happens when you run that ‘docker-compose run’ line is that it creates a new container from the ‘mparker17/mush’ image, with all the configuration from the ‘docker-compose.yml’ file. When that container runs, it automatically runs the ‘drush’ command, and docker-compose passes ‘$rest_of_drush_command’ to the ‘drush’ command. When the ‘drush’ command is finished, the container stops, and the ‘–rm’ thing we specified deletes the container afterwards

Running USER_ID=$(id -u) before a command sets an environment variable that persists for that command; i.e.: when docker-compose runs, an environment variable $USER_ID exists; but $USER_ID goes away when docker-compose is finished running. You can leave out the USER_ID=$(id -u) if you add that line to your shell’s configuration. Essentially what that environment variable does is set the user account that the Drush command runs as. If you don’t specify the user account, then Docker defaults to root.

The main reason why I do this is so that if I ask Drush to make changes to the filesystem (e.g.: download a module, run drush make, etc.) that the files are owned by me, not root (i.e.: so I don’t have to go around changing ownership permissions after I run the drush command)

It may only be necessary on Windows/Macintosh, because the virtual machine that Docker runs in on Win/Mac has different user IDs — I think if you run a Docker command from a Linux machine, your user id is already correct; but because a Docker command on a Mac/Win is run with your Mac/Win user ID (e.g.: 501) but gets passed to the docker VM’s ‘docker’ user (which runs as user 1000), some problems arise unless you’re explicit about it.

Acknowledgements Lastly, I would like to thank Matt Parker here, who has been mentoring me since the beginning of setting up docker and telling me better ways to do it. He also recommends reading the Docker book if you want to explore this further.

Feb 20 2017
Feb 20

I’ve been building websites for the last 10 years. Design fads come and go but image galleries have stood the test of time and every client I’ve had has asked for one.

There are a lot of image gallery libraries out there, but today I want to show you how to use Juicebox.

Juicebox is an HTML5 responsive image gallery and it integrates with Drupal using the Juicebox module.

Juicebox is not open source, instead it offers a free version which is fully useable but you are limited to 50 images per gallery. The pro version allows for unlimited images and more features.

If you’re looking for an alternative solution look at Slick, which is open source, and it integrates with Drupal via the Slick module. I will cover this module in a future tutorial.

In this tutorial, you’ll learn how to display an image gallery from an image field and how to display a gallery using Views.

Getting Started

First, go download and install the Juicebox module.

Using Drush:

$ drush dl juicebox
$ drush en juicebox

Download Juicebox Library

Go to the Juicebox download page and download the free version.

Extract the downloaded file and copy the jbcore folder within the zip file into /libraries and rename the jbcore directory to juicebox.

Once everything has been copied and renamed, the path to juicebox.js should be /libraries/juicebox/juicebox.js.

Create a Gallery Using Fields

We’ll first look at how to create a gallery using just an image field. To do this, we’ll create an image field called “Image gallery” and this field will be used to store the images.

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

2. Click on “Add field” and select Image from “Add a new field”.

3. Enter “Image gallery” into Label and click on “Save and continue”.

4. Change “Allowed number of values” to Unlimited and click on “Save field settings”.

You’ll need to do this if you want to store multiple images.

5. On the Edit page leave it as is and click on “Save settings”.

Configure Juicebox Gallery Formatter

Now that we’ve created the image fields, let’s configure the actual Juicebox gallery through the field formatter.

1. Click “Manage display”, and select “Juicebox Gallery” from the Format drop-down on the “Image gallery” field.

2. Click on the cogwheel to configure the gallery. Now there a lot of options but the only change we’ll make is to set the image alt text as the caption.

3. Click on the “Lite config” field-set and change the height to 500px.

4. Reorder the field so it’s below Body.

5. Click on Save at the bottom of the page.

Now if you go and create a test article and add images into the gallery you should see them below the Body field.

Create a Gallery Using Views

You’ve seen how to create a gallery using just the Juicebox gallery formatter, let’s now look at using Views to create a gallery.

We’ll create a single gallery that’ll use the first image of every gallery on the Article content type.

1. Go to Structure, Views and click on “Add view”.

2. Fill in the “Add new view” form with the values defined in Table 1-0.

Table 1-0. Create a new view

Option Value View name Article gallery Machine name article_gallery Show Content type of Article sorted by Newest first Create a page Unchecked Create a block Unchecked


3. Click on Add in the Fields section.

4. Search for the “Image gallery” field and add it to the view.

5. Change the Format from “Unformatted list” to “Juicebox Gallery” and click on Apply.

6. On the “Page: Style options” select the image field in “Image Source” and “Thumbnail Source”. The one you added to the View earlier.

You can configure the look and feel by expanding the “Lite config” field-set. You can change the width and height, text color and more.

7. Click on Apply.

8.Click on Add next to Master and select Page from the drop-down.

9. Make sure you set a path in the “Page settings” section. Add something like /gallery.

10. Do not forget to save the View by clicking on Save.

11. Make sure you have some test articles and go to /gallery. You should see a gallery made up of the first image from each gallery.

Summary

The reason I like Juicebox in Drupal is because it’s easy to set up. With little effort you can get a nice responsive image gallery from a field or a view. The only downside I can see is that it’s not open source.

FAQ

Q: I get the following error message: “The Juicebox Javascript library does not appear to be installed. Please download and install the most recent version of the Juicebox library.”

This means Drupal can’t detect the Juicebox library in the /libraries directory. Refer to the “Getting started” section.

Feb 13 2017
Feb 13

The definition of “what a search page is” varies from project to project. Some clients are happy with the core Search module, others want a full blown search engine.

Drupal offers a wide range of options when it comes to building custom search pages. You can create a basic search page using the core Search module or if you’re looking for something advanced you could use Search API.

In the recorded webinar I cover the following:

  • Search in Drupal 7 (1:24)
  • What’s new in Drupal 8 (8:37)
  • Create Search Page using Core Search (15:24)
  • Create Search Page using Views (19:20)
  • How to Modify Search Results (22:34)
  • Introduction to Search API (25:28)
  • How to Create Facets (38:20)

Modules Mentioned in Webinar

Extra Resources

Feb 11 2017
Feb 11

Docker, a container-based technology which I just came across, is great for setting up environments. It was first introduced to the world by Solomon Hykes, founder and CEO of dotCloud at Python Developers Conference in Santa Clara, California, in March 2013. The project was quickly open-sourced and made available on GitHub, where anyone can download and contribute to it.

Containers vs. Virtual Machines

You might be wondering, “What is the difference between Containers (like Docker) and Virtual Machines”?

Well, virtual machines (VM) work by creating a virtual copy of a computer’s hardware, and running a full operating-system on that virtual hardware. Each new VM that you create results in a new copy of that virtual hardware, which is computationally expensive. Many people use VMs because they allow you to run an application in a separate environment which can have it’s own versions of software and settings, which are different from the host machine.

On the other hand, container technologies like Docker, isolate the container’s environment, software, and settings, in a sandbox; but all sandboxes share the same operating-system kernel and hardware as the host computer. Each new container results in a new sandbox. This enables us to pack a lot more applications into a single physical server as compared to a virtual machine.

Docker containers are isolated enough that the root process in a container cannot see the host machine’s processes or filesystem. However, it may still be able to make certain system calls to the kernel that a regular user would not, because in Docker, the kernel is shared with the host machine. This is also why Docker containers are not virtual machines and thus a lot faster.

Note, however, that Docker relies on a technology which is only available in the Linux kernel. When you run Docker on a Windows or Macintosh host machine, Docker and all it’s containers run in a virtual machine.

That said, there are two projects trying to bring Docker-style containers natively to OS/X , Dlite and Xhyve. But last I heard, these projects were still very experimental. So consider yourself warned.

When you are done with a container, on a Mac host machine, it’s probably good to suspend the containers, because they run in a virtual machine and that has a lot of overhead. But on a Linux host machine, there would be no need to suspend them because they would not create (much) additional overhead (no more than, say, MAMP).

Docker is a tool that promises to scale into any environment, streamlining the workflow and responsiveness of agile software organizations.

Docker’s Architecture

This is a diagram explaining the basic client-server architecture which docker uses. Docker architecture

Image source: http://www.docker.com

Important Terminology

  • Docker daemon: A Docker engine which runs on the host machine as shown in the image above.
  • Docker client: A Docker cli which is used to interact with the daemon.

Workflow components

  • Docker image: A read-only disk image in which environment & your application resides.
  • Docker container: A read/writeable instance of an image, which you can start, stop, move, and delete.
  • Docker registry: A public or private repository to store images.
  • Dockerfile: A Dockerfile is instructions for how to build a single image. You can think of a Dockerfile as kind of Vagrantfile, or a single Chef cookbook, or an Ansible script, or a Puppet script.

Microservices

Because Docker allows you to run so many containers at the same time, it has popularized the idea of microservices: a collection of containers, each of which contain a single program, all of which work together to run a complex application (e.g. Drupal).

Taking Drupal as an example, every Drupal site has at least two dependencies: an HTTP server (Apache, Nginx, etc.) running PHP; and MySQL. The idea of microservices would involve packaging Apache+PHP separately from MySQL; as opposed to most Drupal virtual machine images which bundle them together into the same VM. For more complicated setups, you could add another container for Solr, another container for LDAP, etc.

For me, the main advantage of using microservices is that it’s easier to update or swap one dependency of an application without affecting the rest of it. Another way of looking at this is that microcontainers make it easier to modify one piece without waiting a long time for the virtual machine to rebuild.

When I was using a virtual machine on a particularly complex project, if I needed to make a change to a setting, I had to make that change in the Puppet config, then run vagrant destroy && vagrant up and wait two hours for it to tell me that the new configuration wasn’t compatible with some other piece of the system. At which point I had to repeat the two hour process, which wasted a lot of time.

If I had been using Docker (properly), then I could have just changed the setting for that one program, rebuild that program’s container (5 seconds), and not have to worry that one piece of the machine needed at least Java 6 and the other piece of the machine could not work without Java 5.

Now that you know the possibilities with Docker, watch this space to find out how all this applies to Drupal.

Jan 26 2017
Jan 26

If you ever need to modify content pages, Display Suite is a good choice. It offers a lot of flexibility without learning a brand new interface. You just use the standard “Manage display” page to select a layout and move fields into regions.

Yesterday, I presented a webinar on how to use Display Suite in Drupal 8. The webinar went for around 50 minutes and I covered the following:

  • What’s new in Drupal 8 (2:20)
  • How to set up a Display Suite layout on a view mode (8:20)
  • How to change the wrapper elements (11:03)
  • How to add custom CSS classes (14:15)
  • How to use Display Suite fields (16:10)
  • How to use the “Display Suite Switch View Mode” sub-module (29:00)
  • And finally, how to override a layout (35:47)

Extra Resources

  1. Using Display Suite in Drupal 8: How to Customize Content Pages
  2. Using Display Suite in Drupal 8: How to Use Display Suite Fields
  3. Using Display Suite in Drupal 8: How to Use Switch View Mode Sub-module
Share
Jan 21 2017
Jan 21

Everyone loves a fast website. It’s one of the critical goals for every web developer to build a site that’s twice as fast than a previous one. And ‘BigPiping’ a website makes it super fast. BigPipe is a fundamental redesign of the dynamic web page serving system.

Typically, 80-90% of the loading time is spent on front end which is very huge.

A few important metrics to observe are:

  • Time To First Byte (TTBF): Time taken between requesting html page and starting to receive the first byte of response. In this time, the client and the browser can’t do anything.
  • Time To Interact (TTI): Completely dependent on use case, but it’s what really matters.
  • Page load time: Total load time until loading is complete.

Facebook’s story on Bigpipe

The basic idea here is to break the webpage into small chunks called pagelets and pipelining them to go through several execution. You know why Facebook loads very fast? That’s because Facebook uses Bigpipe for loading content. Facebook loads the structure of the page in chunks and the elements which are difficult to load come afterwards.

It seems like Facebook loads very fast, but it actually takes 5-6 seconds for everything to load. What happens is that it loads the unchangeable parts first and personalised parts later, like friends list, groups, pages etc.

Facebook’s homepage performance after using Bigpipe caching

Bigpipe rating chart

Source: Facebook

The graph shows that BigPipe reduces user perceived latency by half in most browsers.

Bigpipe in Drupal 8

In Drupal 7 or any other CMS, the webpage gets comparatively slower ones we add customisations or personalisation it. After Using Bigpipe in Drupal 8, this is no longer an issue.

This technology was only available to Linkedin and FB. But now it’s available as a module in Drupal 8.

Facebook did the streaming of content in so called pagelets. Wim & Fabian (Author of Drupal 8 Bigpipe module) named it “auto-placeholdering” for Drupal 8 which differentiates the static sections of the page from dynamic ones.

Here is the example of comparison between standard drupal caching & bigpipe: Here is the link to video

Source: Drie Buytaert/ Youtube

If we provide the correct cacheability metadata, Drupal will be able to automatically deliver personalised parts of the page later, without you having to write a single line of code.

Wim Leers, the author of this module explained it as follows:

“The problem we were solving: Drupal 7 can’t really cache its output because it lacks metadata for caching. It generates just about everything you see on the page on every page load, even things that don’t change. Drupal 8 does far less work when serving a page, because we brought cacheability metadata to everything. BigPipe is the most powerful way to put that cacheability metadata to use and it delivers the most significant performance improvements.

BigPipe was not possible before Drupal 8. So, no, it’s the other way around: BigPipe has changed Drupal 8 and made it the fastest Drupal yet.” - Wim Leers.

How BigPipe works

To exploit the parallelism between web server and browser, BigPipe first breaks web pages into multiple chunks called pagelets. Just as a pipelining microprocessor divides an instruction’s life cycle into multiple stages (such as “instruction fetch”, “instruction decode”, “execution”, “register write back” etc.), BigPipe breaks the page generation process into several stages:

  • Request parsing: Web server parses and sanity checks the HTTP request.
  • Data fetching: Web server fetches data from storage tier.
  • Markup generation: Web server generates HTML markup for the response.
  • Network transport: The response is transferred from web server to browser.
  • CSS downloading: Browser downloads CSS required by the page.
  • DOM tree construction and CSS styling: Browser constructs DOM tree of the document, and then applies CSS rules on it.
  • JavaScript downloading: Browser downloads JavaScript resources referenced by the page.
  • JavaScript execution: Browser executes JavaScript code of the page.

(From the Facebook Engineering blog)

I’d really like to thank Wim Leers & Fabian and others who worked really hard on bringing this caching strategy to Drupal 8 core with 8.1 release.

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