Viewport is one of the modules I maintain for Drupal 7. It’s a very small and simple module that does a very specific thing: it allows site admins to specify the values they want for the HTML meta tag that specifies the viewport settings for browsers. It also allows admins to choose the pages in which they want this custom viewport applied.
Back in the day a client required this functionality to embed some browser games that required very specific settings. Nowadays it’s not really common for a site admin to set those values, and developers don’t really require any UI to set them.
Nevertheless, in the last year I’ve been a bit out of the loop of Drupal 8, and I haven’t had the chance to work on a Drupal 8 project for a client yet, so I’ve decided it’s about time to port my modules to Drupal 8 and catch up with all the changes that it incorporates for developers.
The Viewport module will be the first one: it’s simple, but it has just enough components to get me to reconcile with Drupal 8, and catch up with the massive amount of changes that have taken place since the last time I worked with it. In fact, I already ported this module more than 1 year ago, and it was in good working status but most APIs changed after that, making it completely unusable.
This article is not a tutorial on upgrading modules from Drupal 7 to Drupal 8. It’s done purely to document the new things or problems I’ve come across when getting up to grips with D8 while porting the module. So…
Getting Drupal to run
To install Drupal, this time is not enough with simply running “drush si”. Before that, “composer install” has to be run from the web root, otherwise drush might welcome you with a not so pleasant message:
PHP Fatal error: require(): Failed opening required
/var/www/drupal8/autoload.php on line 14
On top of that, I also came across a different error. Even a more beautiful one:
Drush command terminated abnormally due to an unrecoverable error. [error]
Error: Call to undefined function conf_path() in
The problem turned out to be related to composer as well, and I was able to fix it running “composer global update”, as pointed out in one of the Drush issues on github.
“drush cc all” is no more. For Drupal 8, this is replaced with “drush cache-rebuild” (drush cr).
Also, the concept of disabling a module doesn’t exist anymore. As of Drupal 8, modules are either installed or uninstalled. For uninstalling, the good old “drush pmu -y” still works.
The refactoring starts
One of the simplest things to refactor has been the hook_permission implementation to add a custom permission for the module. In Drupal 8, like many other hooks, this one has disappeared, and now permissions are declared in a module.permissions.yml file. The change record explains how it has to be used, and also how to define dynamic permissions from now on.
Surprisingly, hook_help is still supported in Drupal 8. Refactoring this is trivial, as it mostly has text explaining what the module can do. However, digging a bit on the Standards, security and best practices section of the developer handbook, I came across two pages I hadn’t seen before, that helped me to improve a bit the way I had previously done the help page for the module. They were the Help text standard page, and the Interface text page.
As I was linking to several other sections (or external pages) from the help page, I came across a few of the API changes as well:
- l() has been removed. Now, the Link class should be used instead:
- For absolute urls, Link::fromTextAndUrl() can be used.
- For internal routes, Link::createFromRoute() is the way to go (it also accepts route_parameters for routes that need them. This is a big win, because a url change in a route, won’t imply a lot of refactoring in all the places linking to that route, unless the “id” of the route has changed too!
- url() has been removed in favor of the Url class, which has several helpful methods. For example, to create a Url object from an uri, Url::fromUri() is available.
- t() hasn’t been removed, as it’s a very convenient function, but now its behavior is done through a new class called TranslatableMarkup.
- theme() has become drupal_render(). However, it is already deprecated and it will be removed before Drupal 9, so the way to process a render array so that it’s transformed in markup, becomes this:
One of the issues that led to that final change can be found here.
This one required a bit of searching. The Viewport module used this hook in Drupal 7 inject the custom meta tag for the viewport in the HTML head. However, in Drupal 8 this hook has disappearead, and hook_page_attachments() or hook_page_attachments_alter() has to be used instead.
The key difference between the new and the old hook, is that the new one receives more contents, and not only the html head, so we have to be more careful with what is altered.
Some more information of how core attachments are done by core, can be found in the system_page_attachments function. Also, the HtmlResponseAttachmentsProcessor is used to process all the attachments later on, before the response is returned to the browser.
For this hook, it makes sense that the EventDispatcher is not used, since technically speaking this is not an event, and Drupal just wants to expose these contents to developers so that they can be altered.
The logic of this function is rather simple. First, if a path is not passed as an argument, it finds the current path. Then, it matches the path with the list of configured paths for which to include the custom viewport, to see if the custom tag needs inclusion or not.
To get the current path, another new Service has to be used, instead of the old current_path() function:
To compare it with the list of selected paths, drupal_match_path() was available in Drupal 7. In Drupal 8 there’s a new Service class for it called PatchMatcher, which contains some other methods to get the front page path, and check whether the current path is the front page.
In particular, to match a path against a list paths:
And that wraps it all up for this article. I’ve covered the main API changes I found when refactoring the contents of the viewport.module file. In follow-up posts I’ll document some of the changes I came across when working with the settings form and the testing framework (Simpletest and PHPUnit).