You are here

phantomjs_capture_field.module in PhantomJS Capture 7

Implements a new field that takes usages of the PhantomJS capture module to allow users to enter an URL and display the screen shot captured as an image on the entity.

The module have been extended with a option to capture the node the field is located on using another view mode and a menu callback. The default view mode is PhantomJS. View modes are used to ensure that the previous capture is not displayed in the capture (image in image in img...).

File

phantomjs_capture_field/phantomjs_capture_field.module
View source
<?php

/**
 * @file
 * Implements a new field that takes usages of the PhantomJS capture module to
 * allow users to enter an URL and display the screen shot captured as an image
 * on the entity.
 *
 * The module have been extended with a option to capture the node the field is
 * located on using another view mode and a menu callback. The default view mode
 * is PhantomJS. View modes are used to ensure that the previous capture is not
 * displayed in the capture (image in image in img...).
 */

/**
 * Implements hook_permission().
 *
 * The self entity capture formatter/widget needs to have anonymous access to a
 * view mode menu callback to capture the rendered entity. So this permission
 * have to be set to allow anonymouse access, so it's here to prevent access to
 * content when not using the modules self capture capabilities.
 */
function phantomjs_capture_field_permission() {
  return array(
    'access phantomjs view mode' => array(
      'title' => t('PhantomJS view modw'),
      'description' => t('Allow access to use the PhantomJS view mode (anonymous need access for the view mode capture to work correctly).'),
    ),
  );
}

/**
 * Implements hook_menu().
 *
 * Adds menu callback to render the node in a given view mode which is used when
 * capturing the entity self.
 */
function phantomjs_capture_field_menu() {
  $items = array();

  // @TODO: change to take entity id and node autoload node (%entity)?
  $items['phantomjs/%node/%'] = array(
    'title callback' => 'node_page_title',
    'title arguments' => array(
      1,
    ),
    'page callback' => 'phantomjs_capture_field_view_mode',
    'page arguments' => array(
      1,
      2,
    ),
    'access arguments' => array(
      'access phantomjs view mode',
    ),
  );
  return $items;
}

/**
 * Implements hook_field_info().
 *
 * Adds a new field that can be used to show image screen shots of a given URL.
 */
function phantomjs_capture_field_field_info() {
  return array(
    'phantomjs_capture_field' => array(
      'label' => t('PhantomJS'),
      'description' => t('Field to use PhantomJS Screen Capture.'),
      'settings' => array(
        'uri_scheme' => variable_get('file_default_scheme', 'public'),
      ),
      'instance_settings' => array(
        'file_directory' => '',
      ),
      'default_widget' => 'phantomjs_capture_field_widget',
      'default_formatter' => 'phantomjs_capture_field_formatter',
    ),
  );
}

/**
 * Implements hook_field_validate().
 */
function phantomjs_capture_field_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
}

/**
 * Implements hook_field_is_empty().
 */
function phantomjs_capture_field_field_is_empty($item, $field) {
  return empty($item['url']);
}

/**
 * Implements hook_field_widget_info().
 */
function phantomjs_capture_field_field_widget_info() {
  return array(
    'phantomjs_capture_field_widget' => array(
      'label' => t('Simple URL'),
      'field types' => array(
        'phantomjs_capture_field',
      ),
    ),
    'phantomjs_capture_field_hidden_widget' => array(
      'label' => t('View mode (hidden)'),
      'field types' => array(
        'phantomjs_capture_field',
      ),
    ),
  );
}

/**
 * Implements hook_field_settings_form().
 */
function phantomjs_capture_field_field_settings_form($field, $instance, $has_data) {
  $form = array();
  $defaults = field_info_field_settings($field['type']);
  $settings = array_merge($defaults, $field['settings']);
  $scheme_options = array();
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $stream_wrapper) {
    $scheme_options[$scheme] = $stream_wrapper['name'];
  }
  $form['uri_scheme'] = array(
    '#type' => 'radios',
    '#title' => t('Upload destination'),
    '#options' => $scheme_options,
    '#default_value' => $settings['uri_scheme'],
    '#description' => t('Select where the final files should be stored. Private file storage has significantly more overhead than public files, but allows restricted access to files within this field.'),
  );
  return $form;
}

/**
 * Implements hook_field_instance_settings_form().
 */
function phantomjs_capture_field_field_instance_settings_form($field, $instance) {
  $settings = $instance['settings'];
  $form = array();
  $form['file_directory'] = array(
    '#type' => 'textfield',
    '#title' => t('File directory'),
    '#default_value' => $settings['file_directory'],
    '#description' => t('Optional subdirectory within the upload destination where files will be stored. Do not include preceding or trailing slashes.'),
    '#element_validate' => array(
      '_file_generic_settings_file_directory_validate',
    ),
  );
  $form['file_type'] = array(
    '#type' => 'select',
    '#title' => t('File type'),
    '#options' => array(
      'jpg' => 'jpg',
      'png' => 'png',
      'pdf' => 'pdf',
    ),
    '#default_value' => isset($settings['file_type']) ? $settings['file_type'] : 'jpg',
    '#description' => t('Select the type of file to store the captured screen shot as in the folder above.'),
  );
  return $form;
}

/**
 * Implements hook_field_widget_form().
 */
function phantomjs_capture_field_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  switch ($instance['widget']['type']) {
    case 'phantomjs_capture_field_hidden_widget':
      $element = array();
      break;
    default:
      $element += array(
        'url' => array(
          '#type' => 'textfield',
          '#title' => check_plain($instance['label']),
          '#description' => check_plain($instance['description']),
          '#default_value' => isset($items[$delta]['url']) ? $items[$delta]['url'] : '',
          '#element_validate' => array(
            'phantomjs_capture_field_validate',
          ),
        ),
        'uri' => array(
          '#type' => 'hidden',
          '#default_value' => isset($items[$delta]['uri']) ? $items[$delta]['uri'] : '',
        ),
      );
      break;
  }
  return $element;
}

/**
 * Field validator that ensure that a url was given.
 */
function phantomjs_capture_field_validate($element, &$form_state) {
  if (!empty($element['#value']) && !valid_url($element['#value'])) {
    form_error($element, t("Needs to be a valid URL."));
  }
}

/**
 * Implements hook_field_formatter_info().
 */
function phantomjs_capture_field_field_formatter_info() {
  return array(
    'phantomjs_capture_field_formatter' => array(
      'label' => t('Image'),
      'field types' => array(
        'phantomjs_capture_field',
      ),
      'settings' => array(
        'image_style' => '',
        'image_link' => '',
      ),
    ),
    'phantomjs_capture_field_view_mode_formatter' => array(
      'label' => t('Image (view mode capture)'),
      'field types' => array(
        'phantomjs_capture_field',
      ),
      'settings' => array(
        'image_style' => '',
        'image_link' => '',
        'view_mode' => '',
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function phantomjs_capture_field_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $element = array();
  switch ($display['type']) {
    case 'phantomjs_capture_field_view_mode_formatter':

      // Build list of available view modes.
      $entity_info = entity_get_info($instance['entity_type']);
      $view_modes = $entity_info['view modes'];
      $options = array();
      foreach ($view_modes as $name => $mode) {
        $options[$name] = $mode['label'];
      }
      $element['view_mode'] = array(
        '#title' => t('View mode'),
        '#type' => 'select',
        '#default_value' => $settings['view_mode'],
        '#empty_option' => t('None (PhantomJS)'),
        '#options' => $options,
      );

    // Fall through is on purpose.
    default:

      // Load image styles.
      $image_styles = image_style_options(FALSE);
      $element['image_style'] = array(
        '#title' => t('Image style'),
        '#type' => 'select',
        '#default_value' => $settings['image_style'],
        '#empty_option' => t('None (original image)'),
        '#options' => $image_styles,
      );
      $element['image_link'] = array(
        '#title' => t('Link image to'),
        '#type' => 'select',
        '#default_value' => $settings['image_link'],
        '#empty_option' => t('Nothing'),
        '#options' => array(
          'site' => t('Site'),
        ),
      );
      break;
  }
  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function phantomjs_capture_field_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $summary = array();
  switch ($display['type']) {
    case 'phantomjs_capture_field_view_mode_formatter':

      // Display the selected view mode.
      $mode = $settings['view_mode'];
      if (empty($mode)) {
        $mode = 'PhantomJS';
      }
      $summary[] = t('View mode: %mode', array(
        '%mode' => $mode,
      ));

    // Fall through is on purpose.
    default:

      // Get image style names.
      $image_styles = image_style_options(FALSE);

      // Unset possible 'No defined styles' option.
      unset($image_styles['']);

      // Styles could be lost because of enabled/disabled modules that defines
      // their styles in code.
      if (isset($image_styles[$settings['image_style']])) {
        $summary[] = t('Image style: @style', array(
          '@style' => $image_styles[$settings['image_style']],
        ));
      }
      else {
        $summary[] = t('Original image');
      }
      $link_types = array(
        'site' => t('Linked to site'),
      );

      // Display this setting only if image is linked.
      if (isset($link_types[$settings['image_link']])) {
        $summary[] = $link_types[$settings['image_link']];
      }
      break;
  }
  return implode('<br />', $summary);
}

/**
 * Implements hook_field_formatter_view().
 */
function phantomjs_capture_field_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $settings = $display['settings'];
  foreach ($items as $delta => $item) {

    // If this formatter uses view mode.
    if ($display['type'] == 'phantomjs_capture_field_view_mode_formatter') {

      // Check if uri exists.
      $file = drupal_realpath($item['uri']);
      if (!file_exists($file)) {

        // Build capture url.
        $entity_ids = entity_extract_ids($entity_type, $entity);
        $entity_id = array_shift($entity_ids);
        $url = url('phantomjs/' . $entity_id . '/' . $settings['view_mode'], array(
          'absolute' => TRUE,
        ));

        // Build destination.
        $dest = $field['settings']['uri_scheme'] . '://' . $instance['settings']['file_directory'];

        // Try to capture the entity.
        if (!phantomjs_capture_screen($url, $dest, basename($file))) {
          drupal_set_message(t('PhantomJS chould not create screenshot of the entity', 'error'));
        }
        else {
          return $element;
        }
      }
    }

    // Build return element.
    $element[$delta] = array(
      '#theme' => 'image_formatter',
      '#item' => array(
        'uri' => $item['uri'],
        'alt' => '',
        'title' => '',
      ),
      '#image_style' => isset($settings['image_style']) ? $settings['image_style'] : '',
      '#path' => '',
    );

    // Add link to the image, if selected.
    if (isset($settings['image_link'])) {
      switch ($settings['image_link']) {
        case 'site':
          $element[$delta]['#path'] = array(
            'path' => $item['url'],
            'options' => array(),
          );
          break;
      }
    }
  }
  return $element;
}

/**
 * Implements hook_field_insert().
 */
function phantomjs_capture_field_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
  $dest = $field['settings']['uri_scheme'] . '://' . $instance['settings']['file_directory'] . '/';
  switch ($instance['widget']['type']) {
    case 'phantomjs_capture_field_hidden_widget':

      // Generate the new file name.
      $file_type = isset($instance['settings']['file_type']) ? $instance['settings']['file_type'] : 'jpg';
      $file = md5($entity->nid . REQUEST_TIME) . '.' . $file_type;

      // Store the path (the capture is taken in field formatter).
      $items = array(
        array(
          'url' => 'self',
          'uri' => $dest . $file,
        ),
      );
      break;
    default:
      foreach ($items as $delta => &$item) {

        // Generate the new file name.
        $file_type = isset($instance['settings']['file_type']) ? $instance['settings']['file_type'] : 'jpg';
        $file = md5($entity->nid . REQUEST_TIME . $delta) . '.' . $file_type;

        // Try to capture the site.
        if (phantomjs_capture_screen($item['url'], $dest, $file)) {
          $item['uri'] = $dest . $file;
        }
        else {
          drupal_set_message(t('PhantomJS chould not create screenshot of !url', array(
            '!url' => l($item['url'], $item['url']),
          )), 'error');
        }
      }
      break;
  }
}

/**
 * Implements hook_field_update().
 */
function phantomjs_capture_field_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {

  // Build destination path.
  $dest = $field['settings']['uri_scheme'] . '://' . $instance['settings']['file_directory'] . '/';
  switch ($instance['widget']['type']) {
    case 'phantomjs_capture_field_hidden_widget':

      // Get the item.
      $item =& $items[0];

      // Build path and remove old screen shot.
      if (isset($item['uri'])) {
        $path = drupal_realpath($item['uri']);
        if (file_exists($path)) {
          unlink($path);
        }

        // Remove image style copies.
        image_path_flush($item['uri']);
      }

      // Generate the new file name.
      $file_type = isset($instance['settings']['file_type']) ? $instance['settings']['file_type'] : 'jpg';
      $file = md5($entity->nid . REQUEST_TIME) . '.' . $file_type;

      // Store the path (the capture is taken in field formatter).
      $items = array(
        array(
          'url' => 'self',
          'uri' => $dest . $file,
        ),
      );
      break;
    default:

      // Get old items values.
      $old_items = field_get_items($entity_type, $entity->original, $field['field_name']);

      // Loop over current items.
      foreach ($items as $delta => &$item) {

        // Check if value have been update as taking captures takes time is
        // expensive.
        if (!isset($old_items[$delta]['url']) || $old_items[$delta]['url'] != $item['url']) {
          if (isset($item['uri'])) {

            // Build path and remove old screen shot.
            $path = drupal_realpath($item['uri']);
            if (file_exists($path)) {
              unlink($path);
            }

            // Remove image style copies.
            image_path_flush($item['uri']);
          }
          $file_type = isset($instance['settings']['file_type']) ? $instance['settings']['file_type'] : 'jpg';
          $file = md5($entity->nid . REQUEST_TIME . $delta) . '.' . $file_type;

          // Try to capture the site.
          if (phantomjs_capture_screen($item['url'], $dest, $file)) {
            $item['uri'] = $dest . $file;
          }
          else {
            $item['uri'] = '';
            drupal_set_message(t('PhantomJS chould not create screenshot of !url', array(
              '!url' => l($item['url'], $item['url']),
            )), 'error');
          }
        }
      }
      break;
  }
}

/**
 * Implements hook_field_delete().
 */
function phantomjs_capture_field_field_delete($entity_type, $entities, $field, $instances, $langcode, &$items) {
  foreach ($items as $item) {
    $path = drupal_realpath($item['uri']);

    // Remove screen shot.
    if (file_exists($path)) {
      unlink($path);
    }

    // Remove image style copies.
    image_path_flush($item['uri']);
  }
}

/**
 * Implements hook_entity_info_alter().
 *
 * Adds new view mode to both user and node entities.
 */
function phantomjs_capture_field_entity_info_alter(&$entity_info) {

  // Define the view mode array.
  $view_mode = array(
    'label' => t('PhantomJS'),
    'custom settings' => TRUE,
  );

  // Add view mode.
  $entity_info['node']['view modes']['phantomjs'] = $view_mode;
}

/**
 * Helper function that renders a node in a given view mode.
 *
 * Used as a menu callback when the field is used to capture the node it self.
 *
 * @param entity $node
 *   A node entity.
 * @param string $view_mode
 *   The name of the view mode to render the node as.
 *
 * @return string
 *   The rendered node as HTML.
 */
function phantomjs_capture_field_view_mode($node, $view_mode) {
  $build = node_view($node, $view_mode);
  return drupal_render($build);
}

Functions

Namesort descending Description
phantomjs_capture_field_entity_info_alter Implements hook_entity_info_alter().
phantomjs_capture_field_field_delete Implements hook_field_delete().
phantomjs_capture_field_field_formatter_info Implements hook_field_formatter_info().
phantomjs_capture_field_field_formatter_settings_form Implements hook_field_formatter_settings_form().
phantomjs_capture_field_field_formatter_settings_summary Implements hook_field_formatter_settings_summary().
phantomjs_capture_field_field_formatter_view Implements hook_field_formatter_view().
phantomjs_capture_field_field_info Implements hook_field_info().
phantomjs_capture_field_field_insert Implements hook_field_insert().
phantomjs_capture_field_field_instance_settings_form Implements hook_field_instance_settings_form().
phantomjs_capture_field_field_is_empty Implements hook_field_is_empty().
phantomjs_capture_field_field_settings_form Implements hook_field_settings_form().
phantomjs_capture_field_field_update Implements hook_field_update().
phantomjs_capture_field_field_validate Implements hook_field_validate().
phantomjs_capture_field_field_widget_form Implements hook_field_widget_form().
phantomjs_capture_field_field_widget_info Implements hook_field_widget_info().
phantomjs_capture_field_menu Implements hook_menu().
phantomjs_capture_field_permission Implements hook_permission().
phantomjs_capture_field_validate Field validator that ensure that a url was given.
phantomjs_capture_field_view_mode Helper function that renders a node in a given view mode.