You are here

fb_instant_articles_display.module in Facebook Instant Articles 7.2

Hook implementations for Facebook Instant Articles Display module.

File

modules/fb_instant_articles_display/fb_instant_articles_display.module
View source
<?php

/**
 * @file
 * Hook implementations for Facebook Instant Articles Display module.
 */

/**
 * Load all Field module hooks for Facebook Instant Articles Display module.
 */
require_once __DIR__ . '/includes/field.inc';

/**
 * Implements hook_entity_load().
 */
function fb_instant_articles_display_entity_load($entities, $type) {
  $enabled_entity_types = fb_instant_articles_display_get_article_entity_types();
  foreach ($entities as $entity) {

    // Only proceed if the entity is of the configured entity type and bundle.
    list($id, $vid, $bundle) = entity_extract_ids($type, $entity);
    if (!in_array($type, array_keys($enabled_entity_types)) || !in_array($bundle, array_keys($enabled_entity_types[$type]))) {
      return FALSE;
    }

    // We add Entity-specific information when constructing the ArticleWrapper
    // object, so that other modules can make use of this when implementing
    // hook_fb_instant_articles_article_alter(). We then attach the
    // InstantArticle object directly onto the Entity so that it will be
    // available during hook_field_formatter_view() for mapping field data onto
    // the InstantArticle object.
    $context = array(
      'entity_type' => $type,
      'entity' => $entity,
    );
    $entity->fb_instant_article = \Drupal\fb_instant_articles\ArticleWrapper::create($context)
      ->getArticle();
  }
}

/**
 * Implements hook_fb_instant_articles_article_alter().
 *
 * @see fb_instant_articles_display_module_implements_alter()
 */
function fb_instant_articles_display_fb_instant_articles_article_alter($instantArticle, $context) {
  if (!isset($context['entity_type']) || !isset($context['entity'])) {
    return FALSE;
  }

  // We map the Entity properties here, so we can ensure it runs only once per
  // Entity (during load). If other modules wish to alter this, they should
  // implement hook_fb_instant_articles_article_alter().
  \Drupal\fb_instant_articles_display\EntityPropertyMapper::create($context['entity_type'], $context['entity'], $instantArticle)
    ->map();
}

/**
 * Implements hook_module_implements_alter().
 *
 * @see fb_instant_articles_display_declare_entity_preprocess_hooks()
 */
function fb_instant_articles_display_module_implements_alter(&$implementations, $hook) {

  // Ensure hook_fb_instant_articles_article_alter() runs first, so that other
  // modules can override mappings made by this module by implementing
  // hook_fb_instant_articles_article_alter().
  if ($hook != 'fb_instant_articles_article_alter') {
    return;
  }
  $module = 'fb_instant_articles_display';
  $group = array(
    $module => $implementations[$module],
  );
  unset($implementations[$module]);
  $implementations = $group + $implementations;
}

/**
 * Implements hook_preprocess().
 *
 * Add template suggestions for an entity based on view modes if they do not
 * already exist.
 */
function fb_instant_articles_display_preprocess(&$variables, $hook) {
  $entities = array_keys(entity_get_info());
  if (isset($variables['elements']['#entity_type']) && in_array($variables['elements']['#entity_type'], $entities, TRUE)) {

    // Sometimes a rendered entity is passed into another theme function, in
    // which case do not process. Also account that #theme may be a hook
    // suggestion itself. For example. #theme = 'comment__node_type' and
    // $hook = 'comment'.
    $theme_keys = is_string($variables['elements']['#theme']) ? array(
      $variables['elements']['#theme'],
    ) : $variables['elements']['#theme'];
    foreach ($theme_keys as $theme) {
      if ($theme != $hook && strpos($theme, $hook . '__') !== 0) {
        return;
      }
    }
    if (isset($variables['view_mode']) && $variables['view_mode'] === 'fb_instant_article') {
      $suggestions =& $variables['theme_hook_suggestions'];
      array_push($suggestions, 'entity__fb_instant_article');
    }
  }
}

/**
 * Implements hook_theme().
 *
 * @see fb_instant_articles_display_declare_entity_preprocess_hooks()
 * @see theme_entity__fb_instant_article()
 */
function fb_instant_articles_display_theme() {
  $theme_path = drupal_get_path('module', 'fb_instant_articles_display') . '/theme';
  return array(
    'entity__fb_instant_article' => array(
      'variables' => array(),
    ),
    'field__fb_instant_article' => array(
      'template' => 'field--fb-instant-article',
      'path' => $theme_path,
      'render element' => 'element',
    ),
  );
}

/**
 * Theme function for the fb_instant_article Entity view mode.
 *
 * @see fb_instant_articles_display_preprocess_entity()
 *
 * @ingroup themeable
 */
function theme_entity__fb_instant_article($variables) {
  if (!isset($variables['fb_instant_article'])) {
    return FALSE;
  }
  return $variables['fb_instant_article']
    ->render('<!doctype html>', TRUE);
}

/**
 * Implements hook_preprocess_HOOK().
 */
function fb_instant_articles_display_preprocess_field(&$vars) {
  $element = $vars['element'];
  if ($element['#view_mode'] == 'fb_instant_article') {
    $vars['theme_hook_suggestions'][] = 'field__fb_instant_article';
  }
}

/**
 * Implements hook_permission().
 */
function fb_instant_articles_display_permission() {
  $permissions = array();
  $permissions['administer fb_instant_articles_display'] = array(
    'title' => t('Administer Facebook Instant Articles Display'),
  );
  return $permissions;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * @see fb_instant_articles_display_node_type_form_submit()
 *
 * @todo Decouple from alter hook to more easily support other entity types.
 */
function fb_instant_articles_display_form_node_type_form_alter(&$form, &$form_state) {

  // Add a vertical tab to the node type form.
  if (user_access('administer fb_instant_articles_display')) {

    // Build fieldset for vertical tab section.
    $fieldset = array(
      '#type' => 'fieldset',
      '#title' => t('Facebook Instant Articles'),
      '#description' => t('Configure content type for Facebook Instant Article mappings.'),
      '#group' => 'additional_settings',
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );

    // Has the section already been created (perhaps by a sub module)?
    if (isset($form['fb_instant_articles_display'])) {

      // Add additional fieldset data.
      $form['fb_instant_articles_display'] += $fieldset;
    }
    else {
      $form['fb_instant_articles_display'] = $fieldset;
    }

    // Is an Article type?
    $type = $form['#node_type']->type;
    $fb_instant_enabled = fb_instant_articles_display_is_article_type('node', $type);
    $previously_checked = isset($form_state['values']) && $form_state['values']['fb_instant_articles_display']['fb_instant_enabled'];

    // Build the product checkbox.
    $form['fb_instant_articles_display']['fb_instant_enabled'] = array(
      '#type' => 'checkbox',
      '#title' => t('Include Content Type in Facebook Instant Articles feed.'),
      '#description' => t('Enable content of this type to be included in the Facebook Instant Articles feed.'),
      '#weight' => 0,
      '#default_value' => $previously_checked || $fb_instant_enabled ? TRUE : FALSE,
    );

    // Add custom submit.
    $form['#submit'][] = 'fb_instant_articles_display_node_type_form_submit';
  }
}

/**
 * Submit callback for node type form.
 *
 * @see fb_instant_articles_display_form_node_type_form_alter()
 */
function fb_instant_articles_display_node_type_form_submit($form, &$form_state) {
  $fb_instant_enabled = fb_instant_articles_display_is_article_type('node', $form['#node_type']->type);
  if (!$fb_instant_enabled && $form_state['values']['fb_instant_enabled']) {

    // Save the new article type.
    fb_instant_articles_display_set_entity_type('node', $form['#node_type']->type);
    ctools_include('export');
    ctools_export_crud_enable('fb_instant_articles_display_layout_settings', 'node|article|fb_instant_article');
  }
  elseif (!$form_state['values']['fb_instant_enabled']) {
    fb_instant_articles_display_delete_entity_type('node', $form['#node_type']->type);
  }
}

/**
 * Checks if an entity type and bundle are a Facebook Instant Article type.
 *
 * @param string $entity_type
 *   The entity type name.
 * @param string $bundle
 *   The entity bundle name.
 *
 * @return bool
 *   Boolean TRUE or FALSE.
 */
function fb_instant_articles_display_is_article_type($entity_type, $bundle) {
  $is_type = FALSE;
  if ($types = fb_instant_articles_display_get_article_entity_types()) {

    // See if this entity type and bundle exists.
    if (isset($types[$entity_type]) && isset($types[$entity_type][$bundle])) {
      $is_type = TRUE;
    }
  }

  // Allow other modules to alter.
  drupal_alter('fb_instant_articles_display_is_article_type', $is_type, $entity_type, $bundle);
  return $is_type;
}

/**
 * Gets entity types that are treated as Facebook Instant Articles.
 *
 * @return mixed
 *   Array of entity types and bundles.
 */
function fb_instant_articles_display_get_article_entity_types() {
  ctools_include('export');
  $fia_entity_types = ctools_export_crud_load_all('fb_instant_articles_display_entity_types');
  $entity_types = array();
  foreach ($fia_entity_types as $fia_entity_type) {
    $entity_types[$fia_entity_type->entity_type][$fia_entity_type->entity_bundle] = array(
      'type' => $fia_entity_type->entity_type,
      'bundle' => $fia_entity_type->entity_bundle,
    );
  }

  // Allow other modules to alter.
  drupal_alter('fb_instant_articles_display_entity_types', $entity_types);
  return $entity_types;
}

/**
 * Sets the entity type as an allowable Facebook Instant Article type.
 *
 * @param string $type
 *   The entity type.
 * @param string $bundle
 *   The entity bundle.
 */
function fb_instant_articles_display_set_entity_type($type, $bundle) {
  db_insert('fb_instant_articles_display_entity_types')
    ->fields(array(
    'id' => $type . '|' . $bundle,
    'entity_type' => $type,
    'entity_bundle' => $bundle,
  ))
    ->execute();

  // Allow other modules to perform actions.
  module_invoke_all('fb_instant_articles_display_set_type', $type, $bundle);

  // Clear entity info cache, since this setting influences it. Also clear the
  // menu cache.
  entity_info_cache_clear();
}

/**
 * Deletes the entity type as an allowable Facebook Instant Article type.
 *
 * @param string $type
 *   The entity type.
 * @param string $bundle
 *   The entity bundle.
 */
function fb_instant_articles_display_delete_entity_type($type, $bundle) {
  db_delete('fb_instant_articles_display_entity_types')
    ->condition('entity_type', $type)
    ->condition('entity_bundle', $bundle)
    ->execute();

  // Allow other modules to perform actions.
  module_invoke_all('fb_instant_articles_display_delete_type', $type, $bundle);
}

/**
 * Implements hook_entity_info_alter().
 */
function fb_instant_articles_display_entity_info_alter(&$entity_info) {
  $entity_types = fb_instant_articles_display_get_article_entity_types();
  foreach (array_keys($entity_types) as $entity_type) {
    $entity_info[$entity_type]['view modes']['fb_instant_article'] = array(
      'label' => t('Facebook Instant Articles'),
      'custom settings' => TRUE,
    );
  }
}

/**
 * Implements hook_form_alter().
 */
function fb_instant_articles_display_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'field_ui_display_overview_form') {
    if ($form['#view_mode'] == 'fb_instant_article') {
      form_load_include($form_state, 'inc', 'fb_instant_articles_display', 'includes/view_mode_layout');
      fb_instant_articles_display_layout_form($form, $form_state);
    }
    else {
      fb_instant_articles_display_cleanup_view_mode_formatters($form);
    }
  }
}

/**
 * Get the layout settings for a specific bundle.
 *
 * @param string $entity_type
 * @param string $bundle_name
 *
 * @return stdClass
 *
 * @todo Add update hook to change export ID, and note this in the API
 *   changelog.
 */
function fb_instant_articles_display_get_layout_settings($entity_type, $bundle_name) {
  ctools_include('export');
  $export_id = $entity_type . '|' . $bundle_name . '|fb_instant_article';
  $layout_settings = ctools_export_crud_load_all('fb_instant_articles_display_layout_settings');
  $layout = new stdClass();
  if (isset($layout_settings[$export_id])) {
    $layout = $layout_settings[$export_id];
  }
  return $layout;
}

/**
 * Remove formatters from view modes which are not Facebook Instant Articles.
 */
function fb_instant_articles_display_cleanup_view_mode_formatters(&$form) {
  foreach ($form['fields'] as $field_key => $field) {
    if (strpos($field_key, '#') === FALSE) {
      foreach ($field['format']['type']['#options'] as $format_key => $format) {
        if (strpos($format_key, 'fbia') !== FALSE) {
          unset($form['fields'][$field_key]['format']['type']['#options'][$format_key]);
        }
      }
    }
  }
}

Functions

Namesort descending Description
fb_instant_articles_display_cleanup_view_mode_formatters Remove formatters from view modes which are not Facebook Instant Articles.
fb_instant_articles_display_delete_entity_type Deletes the entity type as an allowable Facebook Instant Article type.
fb_instant_articles_display_entity_info_alter Implements hook_entity_info_alter().
fb_instant_articles_display_entity_load Implements hook_entity_load().
fb_instant_articles_display_fb_instant_articles_article_alter Implements hook_fb_instant_articles_article_alter().
fb_instant_articles_display_form_alter Implements hook_form_alter().
fb_instant_articles_display_form_node_type_form_alter Implements hook_form_FORM_ID_alter().
fb_instant_articles_display_get_article_entity_types Gets entity types that are treated as Facebook Instant Articles.
fb_instant_articles_display_get_layout_settings Get the layout settings for a specific bundle.
fb_instant_articles_display_is_article_type Checks if an entity type and bundle are a Facebook Instant Article type.
fb_instant_articles_display_module_implements_alter Implements hook_module_implements_alter().
fb_instant_articles_display_node_type_form_submit Submit callback for node type form.
fb_instant_articles_display_permission Implements hook_permission().
fb_instant_articles_display_preprocess Implements hook_preprocess().
fb_instant_articles_display_preprocess_field Implements hook_preprocess_HOOK().
fb_instant_articles_display_set_entity_type Sets the entity type as an allowable Facebook Instant Article type.
fb_instant_articles_display_theme Implements hook_theme().
theme_entity__fb_instant_article Theme function for the fb_instant_article Entity view mode.