AMP integration to Drupal. Advanced level

Profile picture for user Andriy Dmytriv

For both average users and developers, mobile web can be slow and really frustrating experience. Since most of us are surfing Internet using our mobile devices, it became inevitable for companies to improve their websites’ load speed. AMP ( Accelerated Mobile Pages ) is an open source project which, as the name implies, aims to improve the mobile web experience by providing clean and instant-loading content. This can make a massive impact on a site’s SEO.

There is enough basic information on how to install AMP in the Internet, but I would like to share my own tips and experience for advanced level developers. Note: I'm describing my experience struggling with AMP for Drupal 7. Drupal 8 follows similar approach, but hook names might differ a bit.

First, you need to follow basic steps to enable basic AMP support.

    1. The first step is to install the contributed module AMP. The config of this module can be changed here: admin/config/content/amp.
    2. I recommend to read file README.txt and follow the instructions to install this module, including all components and libraries.
    3. Enable theme BaseAMP. We inherited this theme and created our own subtheme. We may use ampsubtheme_example and rename it in the code for correct performance.
    4. See more information about drupal subtheme [here].

To make the subtheme work, you need to add the following code (change subtheme to the desired name):

/**
* Preprocess all templates.
*/
function subtheme_amp_preprocess(&$vars, $hook) {
  $vars['ampsubtheme_path_file'] = DRUPAL_ROOT . '/' . drupal_get_path('theme',     'subtheme');
}

After our own theme was enabled, we can use hook_is_amp_request_alter() and add your custom conditions. This way we can enable AMP on certain pages only. For example, by default, AMP is not working on pages created by views module.

For example my hook_is_amp_request_alter:

/**
 * Implements hook_is_amp_request_alter().
 */
function amp_front_page_is_amp_request_alter(&$result) {
  $result = FALSE;
  $page_pattern = variable_get('amp_front_page_pattern', '<front>');
  $page_pattern_disable = variable_get('amp_front_page_pattern_disable');
  $request_path = request_path();
  $current_path = current_path();
  $is_valid_pages = explode(', ', $page_pattern);
  $is_disable_pages = explode(', ', $page_pattern_disable);
  foreach ($is_valid_pages as $page) {
    if ($page and drupal_match_path($current_path, $page) || $page == $request_path) {
      // Add amphtml.
      $uri['options']['query']['amp'] = NULL;
      $uri['options']['absolute'] = TRUE;
      drupal_add_html_head_link(array('rel' => 'amphtml', 'href' => url($request_path, $uri['options'])), TRUE);
      $result = TRUE;
      break;
    }
  }
  if (!isset($_GET['amp'])) {
    $result = FALSE;
  }
  foreach ($is_disable_pages as $page) {
    if ($page and drupal_match_path($current_path, $page) || $page == $request_path) {
      $result = FALSE;
      break;
    }
  }
  // Avoid load node.
  if (!$result) {
    return;
  }
  $node = menu_get_object();
  if ($node && in_array($node->type, amp_get_enabled_types())) {
    $display_config = field_bundle_settings('node', $node->type);
    // Check if enable amp display.
    if (!isset($display_config['view_modes']['amp']) && !$display_config['view_modes']['amp']['custom_settings']) {
      $result = FALSE;
    }
  }
}

For the correct work of amp module with an image, it's width and height should be involved in the attribute. But these attributes might be missing if the image was added using a WYSIWYG text editor.

For blocks this problem can be solved by altering the blocks and parsing the output. See the following example:

function mymodule_block_view_alter(&$data, $block) {
  $blocks_to_parse = array(10, 11, 12);
  if (in_array($block->delta, $blocks_to_parse) and amp_is_amp_request()) {
    $markup = $data['content'];
    $new = str_replace('<img', '<img height="1.1" width="1" layout="responsive" ', $markup);
    $result = _amp_convert_markup_to_amp($new);
    $data['content'] = $result['amp_markup'];
  }
}

Note: the mymodule needs to be changed to your module name.

In this case, I have used str_replace to parse the HTML string. Approach for node is quite similar.
To simply convert HTML string to AMP HTML you can to use function _amp_convert_markup_to_amp($markup);

If for any reason,  you need to extend or alter the converting logic, you can use _amp_create_amp_converter() function to retrieve the converter object. After that you can change the Lullabot\AMP\AMP object to whatever state you need.

Sometimes, you might need to use an AMP Components components which is not included in the default set.

_amp_get_amp_js_list() function returns an array of all JS libraries available.

As an example, here I added AMP bind and AMP form components.

function mymodule_form_FORMID_alter(&$form, &$form_state) {
  customamp_add_amp_script_to_head(array('amp-bind', 'amp-form'));
}

To make my life easier, I've defined my own helper function which adds required libraries to the head of the page:

/**
 * Add amp component to drupal html <head>.
 *
 * @param array $amp_attribute_names
 */
function customamp_add_amp_script_to_head_add_amp_script_to_head(array $amp_attribute_names) {
  $js_item = _amp_get_amp_js_list();
  foreach ($amp_attribute_names as $component_name) {
    if (isset($js_item[$component_name]) && $component_name == 'amp-mustache') {
      $header_element = array(
        '#tag' => 'script',
        '#type' => 'html_tag',
        '#attributes' => array(
          'src' => $js_item[$component_name],
          'async' => "",
          'custom-template' => $component_name
        ),
      );
      drupal_add_html_head($header_element, $component_name);
      continue;
    }
    if (isset($js_item[$component_name])) {
      $header_element = array(
        '#tag' => 'script',
        '#type' => 'html_tag',
        '#attributes' => array(
          'src' => $js_item[$component_name],
          'async' => "",
          'custom-element' => $component_name
        ),
      );
      drupal_add_html_head($header_element, $component_name);
    }
  }
}

Since we have both HTML and AMP versions of the website, we need to let Google know that the AMP version is available. You can read more about it here: How make your pages discoverable.

Below is the code snippet, I've used to add required element:

drupal_add_html_head_link(array('rel' => 'amphtml', 'href' => url($request_path, $uri['options'])),

Following is an advice that can save your time and nerve cells:

You might notice that Drupal is trimming added attributes, for example [hidden] attribute. In this case AMP Bindings won't work.

To avoid it, you need to disable all components that modify HTML code. In my case, it was caused by advagg module. Just disable this variable:

variable_set('advagg_mod_wrap_inline_js_xpath', FALSE);

I hope the article helped you with the difficulties you might have or answered some of your questions. If you have any  additional questions, suggestions or just want to share your experience using AMP on Drupal, feel free to contact us via social networks or email.

P.S. You can download the source of exampel amp_front_page module here https://github.com/bear926/amp_front_page/archive/master.zip. And for drupal 8 https://github.com/bear926/amp_front_page_d8/archive/master.zip.

Happy AMP-ing!