Dec 18 2015
Dec 18

Code reviews are a regular part of our project process and give us the opportunity to catch bugs and standardize code before work is tested by our project leads or clients. You can read more about our code review philosophy in our last post.

This post aims to give an overview of some of the code review tools we use for PHP code reviews.

Tool Time

Git history

For smaller reviews, using Git history to look at a code change is all you need. We use a post-commit Git hook that posts commit hashes to their related tickets in our project management software, so when you’re assigned a ticket to review, you can easily see the commit IDs and run “git show [the hash]” to see the change. With some other ticket management tools you may even be able to see the code changes right along with the ticket comments.

Git diff showing a code change.

Looks good, Oliver! ????


CodeSniffer

The PHP CodeSniffer (PHPCS) utility reviews PHP code for adherence to a given code standard. For Drupal projects, we can check code against Drupal’s standards. There are a few ways to run this, but first, you’ll need to install a few things.

How to install PHP CodeSniffer

  1. Download the PHPCS package using Composer.
    • For Drupal 7 projects:
      composer global require squizlabs/PHP_CodeSniffer:\<2
    • For Drupal 8 projects:
      composer global require squizlabs/PHP_CodeSniffer:\>=2
    • Or, you can install PHPCS with Drush.
  2. Download the Drupal Coder module (7.x-2.x branch – this part is important, don’t choose the 1.x branch). Move this to your central Drush directory ($HOME/.drush) – that allows it to be used on all your Drupal projects.
  3. Configure PHPCS to use Drupal standards:
    phpcs --config-set installed_paths $HOME/.drush/coder/coder_sniffer
    phpcs --config-set default_standard Drupal

Run PHP CodeSniffer in phpStorm IDE

If you use an IDE, there’s probably a plugin for running PHPCS. I set it up in phpStorm like this:

  1. Follow the directions above to install CodeSniffer with the Drupal standards.
  2. Set the path to your CodeSniffer installation in phpStorm (Preferences > Languages & Frameworks > PHP > CodeSniffer). Click the Validate button there to make sure it works.
  3. Enable CodeSniffer (Preferences > Editor > Inspections): Select “PHP CodeSniffer validation”, then select Drupal as the standard to use.
PHPCS settings in phpStorm

PHPCS settings in phpStorm.

Once that’s hooked up, you’ll start to see inline alerts of your rule breaking. You can also run PHPCS against a whole file, directory or project (Code > Run inspection by name > PHPCS). This will give you a list of all the issues PHPCS finds, with a synopsis of the problem and how to fix it.

PHPCS error in phpStorm.

Oooh, busted! ????

There are a lot more Drupal-specific features in phpStorm that are worth trying out, especially in Drupal 8 – check out the JetBrains site for more information.

Run CodeSniffer on the command line

If you don’t use an IDE or just prefer a CLI, you can run PHPCS with terminal commands. You can do this with Drush, like this: drush drupalcs path/to/your/file

Or, without Drush, like this: phpcs --standard=Drupal path/to/your/file

PHPCS command line output.

PHPCS command line output.

The command will return a list of errors and the line numbers where they occur.

Drupal Coder Review module

If you prefer a UI, you can still make use of the Coder module by way of the accompanying Coder Review module.

Coder Review module UI.

Coder Review module provides user interface.

  1. Download the Coder module to your site’s module directory and enable coder and coder_review.
  2. Browse to admin/config/development/coder/settings.
  3. Choose which modules or themes to review.
  4. Review your results, and if needed, make the suggested changes to your code.

Further Reading

Best practices for Drupal code are well-documented on Drupal.org:

These are some other blog posts on the topic:

Do you use any other code review tools?
How do you use code review tools in your project process?

Nov 17 2015
Nov 17

We just launched our first Drupal 8 website for the Northwest Atlantic Marine Alliance (NAMA). During our project retrospective, a few of us brought up how nice it was that so many contrib modules that were part of the D6 site weren’t necessary in Drupal 8 – this was a major factor in making it possible to launch this project before the actual D8 release.

The graphic below compares contrib modules that were running on NAMA’s Drupal 6 site compared to the modules needed to achieve the same functionality in Drupal 8.

Having so many of the modules that we always install built into core gave us more time to focus on ways to optimize the site for NAMA’s specific needs, and it should also save us time down the road when it’s time to handle maintenance tasks (like security updates) or add on new features.

What are you most excited about having in core?

Which modules are you waiting to see in D8 before upgrading?

Aug 26 2015
Aug 26

We’re working on our first Drupal 8 project here at Advomatic, and Jim and I have been tasked with implementing a content migration from the client’s existing Drupal 6 site.

My first assignment was to write a plugin which rewrites image assist tags in node body fields as regular HTML image tags. Fortunately, lots of smart people had already solved this problem for Drupal 6 to Drupal 7 migrations (I adapted my plugin from Olle Jonsson’s script on Github), so the biggest hurdle was learning how to implement this thing in Drupal 8.

This is the true story of how we made it work.

Note: You’ll need to use Drush 8 for working with Drupal 8. I’d recommend following Karen Stevenson’s great tutorial from the Lullabot blog to help set up multiple versions of Drush on your system.

Initial setup

As of this writing, you’ll need some very specific Git checkouts of Drupal core and the migration helper modules, or you’re going to immediately encounter a pile of fatal errors. These are working for us:

Enable those modules and their dependencies, then set up your own custom module for your plugin code. The very cool Drupal Console module is a quick way to generate the boilerplate files you’ll need.

Write some code

Migration template

Your migration is likely going to need to provide migration templates for various node types, as well as one that handles all nodes. This plugin for handling image assist tags needs to run on all imported nodes, so we start by copying /core/modules/node/migration_templates/d6_node.yml over to our module and adjusting it a little to instruct it to run the ImgAssist plugin (see line 36 here).

Migrate plugin

There are example process plugins in “process” folders around the installation, and looking at those was a great way to figure out how to write ours. Jim made note of these commands to use for finding example code:

find ./ -type d -name 'migration_templates'
find ./ -type d -name 'process'

Our ImgAssist migrate process plugin starts with the Drupal 6 node body and teaser values, and then it runs through a few steps to create their Drupal 8 counterparts:

Running a node migration, step-by-step

  • 1. Get the D6 site running locally.
  • 2. Install Drupal 8 dev at the commit noted above.
  • 3. Install migrate_plus and migrate_upgrade at the commits noted above, and enable your custom module.
  • 4. Add your D6 database connection information to settings.php (you can follow the Drupal 7 directions here).
  • 5. Run these Drush commands:
    • drush8 migrate-upgrade --legacy-db-url=mysql://dbusername:[email protected]/D6databasename --legacy-root=http://d6site.local --configure-only
    • drush8 migrate-status (just to make sure your custom migration template is registering)
    • drush8 migrate-import yourmodule_d6_node

You’ll probably get an error the first time running migrate-import since the node migration depends on a few others to run first, such as d6_user. Run the dependency migrations as needed, then try the custom node import again.

If you have a lot of nodes, the import process will take a few minutes. I actually wrote this entire blog post while waiting for imports to run. Go do something fun for a minute, you’ve earned it.

Eventually, migrate-import will finish running, and you’ll be all set! You can compare the node on your D8 site against the node on the D6 site and see that the tag has been replaced. Hooray!

If it didn’t work: read on. It’s totally fine, you’ve got this.

So what if you have to roll it back?

drush migrate-rollback hasn’t been implemented in D8 just yet (but it is getting close). A workaround is to use drush scr to run a script which deletes your newly-imported nodes. We’ve been using this: https://gist.github.com/sarahg/993b97d6733003814fda

Then, you’ll need to uninstall your custom module, remove all of its config entities from the database, and drop its database tables. You can do that with queries like these:

DELETE from config where name=“migrate.migration.yourmodule_d6_node”;
DROP table migrate_map_yourmodule_d6_node;
DROP table migrate_message_yourmodule_d6_node;

To make this a little easier, you could add these queries to a hook_uninstall function in your module. I’m not one for making things easy (I’m working on this migration before there’s even a Drupal 8 release candidate, after all), so I’ve just been using drush sql-cli.

Now you can adjust your code as needed, re-enable your module and give it another shot (you can just skip ahead to the “drush8 migrate-import yourmodule_d6_node” step at this point).

Further reading

It took a lot of research to figure out how to get migrate working in Drupal 8 this early in the game. These articles were immensely helpful (thanks bloggers and documenters!).

Aug 26 2015
Aug 26

We’re wrapping up our first Drupal 8 project here at Advomatic, and Jim and I have been tasked with implementing a content migration from the client’s existing Drupal 6 site.

My first migration job was to write a plugin which rewrites image assist tags in node body fields as regular HTML image tags. Fortunately, lots of smart people had already solved this problem for Drupal 6 to Drupal 7 migrations (I adapted my plugin from Olle Jonsson’s script on Github), so the biggest hurdle was learning how to implement this thing in Drupal 8.

This is the true story of how we made it work.

Note: You’ll need to use Drush 8 for working with Drupal 8. I’d recommend following Karen Stevenson’s great tutorial from the Lullabot blog to help set up multiple versions of Drush on your system.

Initial setup

When we first ran our migrations, a few months ago, we needed specific checkouts from dev branches of core and migrate modules. However, as of our final migration run yesterday (10/6/15), we were able to use:

Enable those modules and their dependencies, then set up your own custom module for your plugin code. The very cool Drupal Console module is a quick way to generate the boilerplate files you’ll need.

Write some code

Migration template

Your migration is likely going to need to provide migration templates for various node types, as well as one that handles all nodes. This plugin for handling image assist tags needs to run on all imported nodes, so we start by copying /core/modules/node/migration_templates/d6_node.yml over to our module and adjusting it a little to instruct it to run the ImgAssist plugin (see line 36 here).

Migrate plugin

There are example process plugins in “process” folders around the installation, and looking at those was a great way to figure out how to write ours. Jim made note of these commands to use for finding example code:

find ./ -type d -name 'migration_templates'
find ./ -type d -name 'process'

Our ImgAssist migrate process plugin starts with the Drupal 6 node body and teaser values, and then it runs through a few steps to create their Drupal 8 counterparts:

  • 1. Read through the body value and pick out [img_assist] tags.
  • 2. Split those tags into usable pieces.
  • 3. Build the HTML image tag.
  • 4. Replace the original content containing img_assist tags with the rewritten version, using the built-in transform function.

Running a node migration, step-by-step

  • 1. Get the D6 site and your D8 site running locally.
  • 3. Enable migrate_plus, migrate_upgrade and your custom module.
  • 4. Add your D6 database connection information to settings.php (you can follow the Drupal 7 directions here).
  • 5. Run these Drush commands:
    • drush8 migrate-upgrade --legacy-db-url=mysql://dbusername:[email protected]/D6databasename --legacy-root=http://d6site.local --configure-only
    • drush8 migrate-status (just to make sure your custom migration template is registering)
    • drush8 migrate-import yourmodule_d6_node

You’ll get a notice the first time running migrate-import since the node migration depends on a few others to run first, such as d6_user. Run the dependency migrations as needed, then try the custom node import again.

If you have a lot of nodes, the import process will take a few minutes. I actually wrote this entire blog post while waiting for imports to run. Go do something fun for a minute, you’ve earned it.

Eventually, migrate-import will finish running, and you’ll be all set! You can compare the node on your D8 site against the node on the D6 site and see that the tag has been replaced. Hooray!

If it didn’t work: read on. It’s totally fine, you’ve got this.

So what if you have to roll it back?

drush migrate-rollback hasn’t been implemented in D8 just yet (but it is getting close). A workaround is to use drush scr to run a script which deletes your newly-imported nodes. We’ve been using this: https://gist.github.com/sarahg/993b97d6733003814fda

Then, you’ll need to uninstall your custom module, remove all of its config entities from the database, and drop its database tables. You can do that with queries like these:

DELETE from config where name=“migrate.migration.yourmodule_d6_node”;
DROP table migrate_map_yourmodule_d6_node;
DROP table migrate_message_yourmodule_d6_node;

To make this a little easier, you could add these queries to a hook_uninstall function in your module. I’m not one for making things easy (I’m working on this migration before there’s even a Drupal 8 release candidate, after all), so I’ve just been using drush sql-cli.

Now you can adjust your code as needed, re-enable your module and give it another shot (you can just skip ahead to the “drush8 migrate-import yourmodule_d6_node” step at this point).

Further reading

It took a lot of research to figure out how to get migrate working in Drupal 8 this early in the game. These articles were immensely helpful (thanks bloggers and documenters!).

Jul 01 2015
Jul 01

Earlier this year, we launched a new site for the ACLU. The project required a migration from Drupal 6, building a library of interchangeable page components, complex responsive theming, and serious attention to accessibility, security and privacy. In this post, I’ll highlight some of the security and privacy-related features we implemented.

Privacy

As an organization, the ACLU has a strong commitment to protecting individual privacy, and we needed to translate their passion to this issue to their own website. This meant meeting their high standards at a technical level.

The ACLU ensures that technical innovation doesn’t compromise individual privacy, and most of our work around user privacy involved ensuring that third-party services like Facebook and YouTube weren’t accessing user data without their consent.

Social sharing

Facebook “Like” buttons on the site offer a quick way for visitors to follow ACLU content on their Facebook feed. But even if you don’t click to “Like” a page, the tracking scripts behind them – coming straight from Facebook – log your behavior and make note of your visit there. That data can be used for targeted advertising, and the data itself is also a sellable product. Because these buttons are all over the web, Facebook can piece together quite a bit of your browsing history, without your knowledge.

To prevent this from happening to ACLU site visitors, we hooked up a jQuery plugin called Social Share Privacy to make sure that visitors who want to “Like” ACLU can do so while others can avoid Facebook’s data tracking. The plugin initially loads a disabled (greyed-out) button for the Facebook “Like”. If the visitor wants to use the button, they click once to enable it, which then loads Facebook’s iframe, and then a second time to “Like” the page.

ACLU.org Facebook button

The Social Share Privacy jQuery plugin keeps Facebook from tracking you without your consent.

A nice side effect is a faster page load for everyone since we don’t have to load content from Facebook on the initial page render.

Video playback

YouTube and other video sharing sites present a similar privacy problem – the scripts used to embed videos can also dig into visitor data without their consent.

MyTube video embed

A MyTube video embed on ACLU.org.

MyTube is a script that was written by the Electronic Frontier Foundation as a Drupal 5 module, then updated as a Drupal 6 module by students in the Ohio State University Open Source Club. For the ACLU site, we worked on porting the module to Drupal 7 and updating various parts of the code to work with updated APIs (both on the Drupal end and the video host end).

Similar to the Social Share Privacy plugin, MyTube initially loads a static thumbnail image from the embedded video, and to play the video, the visitor gives it an extra click, opting-in to run code from the third-party site.

If you’re interested in helping complete Drupal 7 version of the module, take a look at some of the patches our team submitted, and test them out!

Security

Due to the ACLU’s high profile and involvement with controversial topics, security was a major concern throughout the development process. While we can’t go into too much detail here (because, well, security), here are a few steps we took to keep the site safe.

  1. Scrubbed database back-ups. When developers copy down database instances for local development, sensitive information – such as user emails and passwords – are removed. This is made pretty easy using Drush sql-sync-pipe.
  2. Secure hosting on Pantheon with controlled development environments. In addition, though our development team can make code changes, only a handful of authorized ACLU web managers can actually deploy change to their live site. 
  3. The site runs entirely over SSL via HSTS headers
  4. The previous version of the site had various “action” tools where visitors could log in to sign petitions and send letters to their elected representatives. During the redesign this functionality was moved to a separate site, on a separate codebase, running on separate infrastructure. Any site that allows anonymous visitors to create accounts is, by definition, more susceptible to attack. By splitting into two sites, a successful exploit of one web property will not affect the other.
  5. All custom code was reviewed by multiple members of the Advomatic team, and Tag1 Consulting performed an additional security review, in which our codebase received excellent marks and a glowing recommendation.

When your organization champions a cause, all your activities should embody it, including your website. The ACLU takes these standards very seriously, and so do we. Take a look at some of our other projects to see how we worked with nonprofits on sites that reflect their values. 

You should also check out

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