Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
May 19 2020
May 19

Web accessibility helps to make the world wide web usable for everyone. Many of the most common accessibility issues making sites difficult or impossible to use in a non-traditional way can be easily fixed. 

The following hook modifies any exposed view filter to have aria labels on each form element as well as inserts the view name into the form element, form element label, form actions wrapper, and form submit button ID. (E.g. edit-submit-[VIEW_NAME]-[VIEW_DISPLAY])

Developers need to ensure that page markup does not contain duplicate IDs because many screen readers and assistive technologies use IDs within the page as landmarks for the user to easily parse the content.

The addition of aria-labels acts as an agnostic accessibility feature to inform the user the purpose of the element:

/**
* Implements hook_form_alter().
*/
function MYMODULE_form_alter(&$form, FormStateInterface &$form_state, $form_id) {
 // Add aria-labels to views exposed filters.
 if ($form_id === 'views_exposed_form' && strpos($form['#id'], '-block-') !== FALSE) {
   // Assumed structure (e.g. views_exposed_form_[view_name]_[block_name]).
   $exploded_form_id = explode('views_exposed_form_', str_replace('-', '_', $form['#id']));

   if (isset($exploded_form_id[1]) && !empty($exploded_form_id[1])) {
     // Get the view id.
     $view = explode('_block_', $exploded_form_id[1]);
     $view = isset($view[0]) ? $view[0] : '';

     if (!empty($view)) {
       // Get the view display id.
       $view_display = explode($view . '_', $exploded_form_id[1]);
       $view_display = $view_display[1];
       // Get the view display title.
       $view = Views::getView($view);
       $view->setDisplay($view_display);
       $clean_view_display_title = Html::getClass($view->getTitle());

       foreach ($form['#info'] as $info) {
         if (isset($form[$info['value']])) {
           $clean_info_value = Html::getClass($info['value']);

           // Add an aria-label to the form element.
           $form[$info['value']]['#attributes']['aria-label'] = $view->getTitle() . ' ' . trim($info['label'], ':');
           // Update the id on the form element and label.
           $form[$info['value']]['#id'] = 'edit-' . $clean_view_display_title . '-' . $clean_info_value;
         }
       }

       if (isset($form['actions'])) {
         $form['actions']['#id'] = 'edit-actions-' . $clean_view_display_title;

         // Update the id on the submit button.
         if (isset($form['actions']['submit'])) {
           $form['actions']['submit']['#id'] .= '-' . $clean_view_display_title;
         }
       }
     }
   }
 }
}

This second hook modifies any Table view display to prepend the view name and view display name to the table header ID attribute and corresponding table row header attribute (e.g. [VIEW_NAME]--[VIEW_DISPLAY]--[EXISITNG_ID]):

/**
* Implements template_preprocess_views_view_table().
*/
function MYMODULE_preprocess_views_view_table(&$variables) {
 $view_name = Html::getClass($variables['view']->id());
 $view_display = Html::getClass($variables['view']->getDisplay()->display['id']);
 $id_prefix = $view_name . '--' . $view_display . '--';

 // Update each table header 'id' to be
 // prepended with [VIEW_NAME]--[VIEW_DISPLAY]--.
 foreach ($variables['header'] as $header_key => $header) {
   if (
     isset($header['attributes']) &&
     isset($header['attributes']->storage()['id'])
   ) {
     $variables['header'][$header_key]['attributes']->setAttribute('id', $id_prefix . $header['attributes']->storage()['id']);
   }
 }

 // Update each row 'headers' to be
 // prepended with [VIEW_NAME]--[VIEW_DISPLAY]--.
 foreach ($variables['rows'] as $row_key => $row) {
   if (isset($row['columns']) && !empty($row['columns'])) {
     foreach ($row['columns'] as $column_key => $column) {
       if (
         isset($column['attributes']) &&
         isset($column['attributes']->storage()['headers'])
       ) {
         $variables['rows'][$row_key]['columns'][$column_key]['attributes']->setAttribute('headers', $id_prefix . $column['attributes']->storage()['headers']);
       }
     }
   }
 }
}

Comment below for other ideas, or if you need help using these. 
 

May 15 2020
May 15

There is a lot of excitement in the Drupal community about the pending release of Drupal 9. In particular, one of the most appealing elements is how the transition to Drupal 9 promises to be the easiest major upgrade in more than a decade.

In reminiscing with other community members about some painful upgrades from long ago, it got me thinking about how the nature of the majority of Drupal modules has changed.

Early Days - Modules as Solutions

My first big Drupal project was using the 4.6 version of core. One of the things I remember was the way many of the modules available tried to anticipate a particular use case and provide a “solution” -- essentially an attempt at a fully-formed (if configurable) way to make Drupal meet a set of needs. 

An example of this is the Job Search module I helped to maintain for a time. It had a preconfigured relationship between jobs and resumes, and permissions associated with different actions you would expect users to take: post jobs, apply for jobs, and so on.

There were frequent requests from users who wanted it to work just a little differently from how it was made. Sometimes these changes got incorporated back into the module, but sometimes they remained a special case - a customization that was really only useful in the context of their site. That started to change with the rise in popularity of toolsets and modules like CCK, which were more about making Drupal more flexible so it can be quickly customized to meet a very specific set of needs.

Rise of the Toolsets

What we’ve seen since that time is an increasingly powerful set of modules that extend the capabilities of Drupal, but leave it up to the site builder to decide how these capabilities should be applied. Image API, Responsive Images, Metatags, and many more are examples of modules that gave Drupal important new abilities, but without any recommended starting point on how they should be used.

Even a tool like Features was built to help make those configuration decisions portable between sites or environments on the same site. But increasingly all decisions on how these should be set up fell entirely on the site builder. Which was fine for those of us used to Drupal (or fortunate enough to work among an experienced team of Drupal experts) but more daunting for someone trying to put together a simple site.

In that time we’ve seen Drupal become the CMS of choice for governments, record labels, and major universities, but we’ve seen competitors slowly take over niches where Drupal used to be popular, such as startups and charities. Having to build from scratch can be less attractive for an organization with limited resources, so it’s understandable they’d be tempted to go an easier route, if available.

A Middle Way

Distributions have been one attempt at addressing this problem, such as the popular Commerce Kickstart, which helped to install a ready-to-use e-commerce site. The challenge we’ve seen in using distributions is that they’re complex to maintain, so often you’re not able to use the latest versions of core or popular contrib modules. Or when it comes time to upgrade, it has built-in assumptions about what’s installed, which can make it more complex to upgrade the component pieces. And finally, a distribution is typically only an option when you’re starting to build (or potentially re-build) a site, not for adding incremental functionality.

One of the exciting features Drupal introduced in version 8 was configuration management.  

In addition, Drupal Console gives us an easy way to export individual elements: a content type, a view, and related dependencies. At Digital Echidna we’ve been experimenting with using these to create modules that are effectively just sets of related configuration meant to be a starting point to help us quickly address a particular use case: a locations map, an events calendar, and yes, even a jobs board.

smart date logoSmart Date Module

Now, I’ve adapted this approach to help anyone interested in using (or even just trying out) the Smart Date module I’ve mentioned many times in this blog. It’s easy to install the Smart Date Starter Kit and it will give you a functional (if basic) Event content type and a view with displays to show upcoming and past events. 

It isn’t preconfigured for recurring events (since not every site needs that) but if you want to add that, it’s as simple as installing the Smart Date Recurring submodule and then updating the configuration of the “When” field to allow recurring events. That’s it! The view has already been set up to properly aggregate events with multiple events.

If you also need your events to show in a calendar, you can use the Smart Date Calendar Kit. Installing it via composer gives you all the dependencies (including Smart Date and Fullcalendar View) plus everything described above in the starter kit. The calendar is connected to the list views as navigation tabs, so it should be a robust starting point to manage events on your site.

Both of these are just an initial set of configurations so you can add as much additional complexity as necessary to suit the specific needs of your site. And we’ve tried to build in some admin experience best practices, such as built-in administrative links to add content, so it’s intuitive to maintain out of the box. 

I hope you’ll try them out and post an issue if you think there are ways these could be made even better.

Future Considerations

The recent Drupal business survey posted by Dries hints that there may be similar conversations already happening elsewhere in the community, so it will be interesting to see the results when they’re announced at DrupalCon Global in July. It’s yet another reason to be excited about Drupal 9, and the future direction for this platform. With all kinds of innovations happening each and every day, it is an exciting era in the history of Drupal. 

Digital Echidna Recognized as a Top Development Firm in Canada

Feb 01 2019
Feb 01

Join Us for Drupal Global Contribution Weekend

Jan 10 2019
Jan 10

Can We Talk? Planning the First Step Along the Path to Better

Oct 02 2018
Oct 02

Routine Maintenance Now Will Lead to a Smooth Path to Drupal 9

Sep 18 2018
Sep 18

Expressing Our Gratitude for All Who Have Helped Us Grow

Sep 13 2018
Sep 13

Putting Our Best Foot Forward Against Bullying on For Me Day

Dec 19 2017
Dec 19

When Custom Import is a Better Choice than Migrate -- Custom Code Support and Examples

Oct 27 2017
Oct 27
Sep 12 2017
Sep 12
Digital Echidna is a certified Thunder Integrator -- one of only a few in the world -- offering tailored CMS solutions to the publishing community.

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