amp_dfp.module in Accelerated Mobile Pages (AMP) 7
AMP integration for the DFP module.
File
modules/amp_dfp/amp_dfp.moduleView source
<?php
/**
* @file
* AMP integration for the DFP module.
*/
/**
* Implements hook_block_view_alter().
*/
function amp_dfp_block_view_alter(&$data, $block) {
if ($block->module == 'dfp') {
if (amp_is_amp_request()) {
$data['content']['dfp_wrapper']['tag']['#theme'] = 'amp_dfp_tag';
}
}
}
/**
* Implements hook_theme().
*/
function amp_dfp_theme($existing, $type, $theme, $path) {
$theme = array(
'amp_dfp_tag' => array(
'variables' => array(
'tag' => NULL,
'width' => NULL,
'height' => NULL,
'layout' => NULL,
'amp_ad_json' => NULL,
'sticky' => NULL,
),
'template' => 'amp-dfp-tag',
),
);
return $theme;
}
/**
* A callback to render amp_ad with js library added to head.
*
* @params
* $variables The theme variables.
* @return
* The passed-in element with the js library necessary for the amp-ad
* element added to head.
*/
function _amp_dfp_headers($variables) {
if (!empty($variables['sticky'])) {
$head_js = array(
'#tag' => 'script',
'#type' => 'html_tag',
'#attributes' => array(
'src' => 'https://cdn.ampproject.org/v0/amp-sticky-ad-1.0.js',
'async' => NULL,
'custom-element' => 'amp-sticky-ad',
),
);
drupal_add_html_head($head_js, 'amp-sticky-ad');
}
else {
$head_js = array(
'#tag' => 'script',
'#type' => 'html_tag',
'#attributes' => array(
'src' => 'https://cdn.ampproject.org/v0/amp-ad-0.1.js',
'async' => NULL,
'custom-element' => 'amp-ad',
),
);
drupal_add_html_head($head_js, 'amp-ad');
}
}
/**
* Implements hook_preprocess_amp_dfp_tag().
*/
function amp_dfp_preprocess_amp_dfp_tag(&$variables) {
if (!amp_is_amp_request()) {
return;
}
$variables['layout'] = $variables['tag']->settings['amp_layout'];
$size = _amp_dfp_get_amp_size($variables['tag']);
if (empty($size)) {
// Provide a default if there is a problem, so validation does not break.
$variables['width'] = $variables['height'] = 1;
}
else {
$variables['width'] = $size['width'];
$variables['height'] = $size['height'];
}
$tag = _dfp_prepare_adunit($variables['tag']);
$tag->adunit = dfp_token_replace('[dfp_tag:network_id]/' . $tag->adunit, $tag, array(
'sanitize' => TRUE,
'clear' => TRUE,
));
// We add global targeting to each dfp tag to simplify implementation.
$global_targeting = variable_get('dfp_targeting', array());
drupal_alter('dfp_global_targeting', $global_targeting);
$tag->global_targeting = $global_targeting;
$variables['slot'] = $tag->adunit;
if (!empty($variables['tag']->settings['amp_sticky'])) {
$variables = array_merge($variables, get_amp_sticky_ad($tag));
}
// Prepare targeting arrays for JSON formatting
$targeting = array();
foreach ($tag->targeting as $target) {
$targeting[$target['target']] = dfp_token_replace($target['value'], $tag, array(
'sanitize' => TRUE,
'clear' => TRUE,
));
}
$global_targeting = array();
foreach ($tag->global_targeting as $target) {
$global_targeting[$target['target']] = dfp_token_replace($target['value'], $tag, array(
'sanitize' => TRUE,
'clear' => TRUE,
));
}
// Give preference to tag specific targeting values
$targeting = $targeting + $global_targeting;
$to_json = array(
'targeting' => $targeting,
);
$variables['amp_ad_json'] = drupal_json_encode($to_json);
_amp_dfp_headers($variables);
}
/**
* Implements hook_form_FORM_ID_alter().
* Add an AMP Size option to the DFP tag configuration page.
*/
function amp_dfp_form_ctools_export_ui_edit_item_form_alter(&$form, &$form_state) {
// Create a default tag object.
if ($form_state['plugin']['schema'] == 'dfp_tags') {
$tag = $form_state['op'] == 'add' ? $form_state['item'] : $form_state['item']->raw;
$options = array(
'fill',
'fixed',
'fixed-height',
'flex-item',
'nodisplay',
'responsive',
);
$form['tag_settings']['amp_layout'] = array(
'#type' => 'select',
'#title' => t('AMP Layout'),
'#description' => t('Select the layout.'),
'#options' => drupal_map_assoc($options),
'#weight' => 0,
'#default_value' => isset($tag->settings['amp_layout']) ? $tag->settings['amp_layout'] : 'responsive',
);
$form['tag_settings']['amp_size'] = array(
'#type' => 'textfield',
'#title' => t('AMP Size'),
'#description' => t('Enter the size for this tag when displayed on AMP pages. If not entered, the smallest size will be used. Example: 300x250'),
'#weight' => 0,
'#default_value' => isset($tag->settings['amp_size']) ? $tag->settings['amp_size'] : '',
);
$form['tag_settings']['amp_sticky'] = array(
'#type' => 'checkbox',
'#title' => t('AMP Sticky'),
'#description' => t('Makes the ad slot sticky. One one sticky ad slot is allowed per page.'),
'#weight' => 0,
'#default_value' => isset($tag->settings['amp_sticky']) ? $tag->settings['amp_sticky'] : 0,
);
$form['#validate'][] = 'amp_dfp_ctools_export_ui_edit_item_form_validate';
array_unshift($form['#submit'], 'amp_dfp_ctools_export_ui_edit_item_form_submit');
}
}
/**
* Validation handler to check the format of anything entered into the AMP Size
* field.
*/
function amp_dfp_ctools_export_ui_edit_item_form_validate(&$form, &$form_state) {
if (!empty($form_state['values']['amp_size'])) {
$pixels = _amp_dfp_get_pixel_sizes($form_state['values']['amp_size']);
if (empty($pixels)) {
form_set_error('amp_size', 'AMP Size is in an invalid format.');
}
}
}
/**
* Submit handler so the amp size gets saved to the database.
*/
function amp_dfp_ctools_export_ui_edit_item_form_submit(&$form, &$form_state) {
if (isset($form_state['values']['amp_size'])) {
$form_state['values']['settings']['amp_layout'] = $form_state['values']['amp_layout'];
$form_state['values']['settings']['amp_size'] = $form_state['values']['amp_size'];
$form_state['values']['settings']['amp_sticky'] = $form_state['values']['amp_sticky'];
if ($form_state['values']['amp_sticky'] == 1) {
$form_state['values']['settings']['amp_layout'] = 'nodisplay';
}
}
}
/**
* Extract the height and width given a resolution string in the form of 300x250.
*
* @param $size
* A string to test.
*
* @return array|bool
* An array keyed by 'width' and 'height', FALSE if there is no match.
*/
function _amp_dfp_get_pixel_sizes($size) {
preg_match('/^(\\d+)x(\\d+)$/i', $size, $match);
if (!empty($match)) {
return array(
'width' => $match[1],
'height' => $match[2],
);
}
return FALSE;
}
/**
* Given a DFP tag object, return the size that should be used for an amp-ad element.
*/
function _amp_dfp_get_amp_size($tag) {
if (!empty($tag->settings['amp_size'])) {
return _amp_dfp_get_pixel_sizes($tag->settings['amp_size']);
}
else {
// Grab the lowest resolution from the DFP size property
$lowest = _amp_dfp_get_lowest_resolution($tag->size);
return _amp_dfp_get_pixel_sizes($lowest);
}
}
/**
* @param $sizes
* A string of comma-separated resolution sizes. Ex. 300x250
*
* @return mixed
* A string of lowest sized resolution.
*/
function _amp_dfp_get_lowest_resolution($sizes) {
$resolutions = explode(',', $sizes);
return array_reduce($resolutions, '_amp_dfp_size_reduce_callback');
}
/**
* Callback for the array_reduced used to determine lowest resolution.
* @see _amp_dfp_get_lowest_resolution().
*/
function _amp_dfp_size_reduce_callback($carry, $item) {
if ($carry == NULL) {
return $item;
}
$carry_sizes = _amp_dfp_get_pixel_sizes($carry);
$item_sizes = _amp_dfp_get_pixel_sizes($item);
$carry_total = $carry_sizes['width'] * $carry_sizes['height'];
$item_total = $item_sizes['width'] * $item_sizes['height'];
return $carry_total > $item_total ? $item : $carry;
}
/**
* AMP wrapper for the dfp_tag function, setting the #theme key to amp_dfp_tag.
*/
function amp_dfp_tag($machinename) {
$render_array = dfp_tag($machinename);
$render_array['dfp_wrapper']['tag']['#theme'] = 'amp_dfp_tag';
return $render_array;
}
/**
* Returns the sticky ad element.
* Ensures that only one sticky ad is placed on the page to avoid validation
* errors.
*
* @params $tag
* The ad tag.
*
* @return $sticky_ad
* The sticky ad.
*/
function get_amp_sticky_ad($tag) {
static $count;
$sticky_ad = array();
if (is_null($count)) {
$sticky_ad['layout'] = NULL;
$sticky_ad['sticky'] = TRUE;
$sticky_ad['slot'] = '/' . trim($tag->adunit, '/') . '/sticky';
$count++;
}
else {
watchdog('amp_dfp', 'DFP tag %machinename is being added to the page more than once.', array(
'%machinename' => $tag->machinename,
), WATCHDOG_WARNING);
}
return $sticky_ad;
}
Functions
Name | Description |
---|---|
amp_dfp_block_view_alter | Implements hook_block_view_alter(). |
amp_dfp_ctools_export_ui_edit_item_form_submit | Submit handler so the amp size gets saved to the database. |
amp_dfp_ctools_export_ui_edit_item_form_validate | Validation handler to check the format of anything entered into the AMP Size field. |
amp_dfp_form_ctools_export_ui_edit_item_form_alter | Implements hook_form_FORM_ID_alter(). Add an AMP Size option to the DFP tag configuration page. |
amp_dfp_preprocess_amp_dfp_tag | Implements hook_preprocess_amp_dfp_tag(). |
amp_dfp_tag | AMP wrapper for the dfp_tag function, setting the #theme key to amp_dfp_tag. |
amp_dfp_theme | Implements hook_theme(). |
get_amp_sticky_ad | Returns the sticky ad element. Ensures that only one sticky ad is placed on the page to avoid validation errors. |
_amp_dfp_get_amp_size | Given a DFP tag object, return the size that should be used for an amp-ad element. |
_amp_dfp_get_lowest_resolution | |
_amp_dfp_get_pixel_sizes | Extract the height and width given a resolution string in the form of 300x250. |
_amp_dfp_headers | A callback to render amp_ad with js library added to head. |
_amp_dfp_size_reduce_callback | Callback for the array_reduced used to determine lowest resolution. |