You are here

caption_filter.module in Caption Filter 6

Same filename and directory in other branches
  1. 7.2 caption_filter.module
  2. 7 caption_filter.module

caption_filter.module

Provides a very basic caption filter for input formats

File

caption_filter.module
View source
<?php

/**
 * @file
 * caption_filter.module
 *
 * Provides a very basic caption filter for input formats
 */

/**
 * Implementation of hook_init().
 */
function caption_filter_init() {
  $path = drupal_get_path('module', 'caption_filter');
  drupal_add_css($path . '/caption-filter.css');
  drupal_add_js($path . '/js/caption-filter.js');
}

/**
 * Implementation of hook_filter().
 */
function caption_filter_filter($op, $delta = 0, $format = -1, $text = '') {
  switch ($op) {
    case 'list':
      return array(
        0 => t('Caption filter'),
      );
    case 'description':
      return t('Converts [caption] tags and allows image alignment.');
    case 'process':
      return caption_filter_process_filter($text, $format);
    default:
      return $text;
  }
}

/**
 * Implementation of hook_filter_tips().
 */
function caption_filter_filter_tips($delta, $format, $long = FALSE) {
  if ($long) {
    return t('
      <p><strong>Caption Filter</strong></p>
      <p>You may wrap images or embeds with a caption using the code <code>[caption]IMAGE caption[/caption]</code>.</p>
      <p>Examples:</p>
      <ul>
        <li>Single Image:<br /><code>[caption]<img src="" />This is a caption[/caption]</code></li>
        <li>Align the video:<br /><code>[caption align=right]<img src="" />This is another caption[/caption]</code></li>
      </ul>');
  }
  else {
    return check_plain(t('Captions may be specified with [caption]<img src="example.png">Image caption[/caption]. Items can be aligned with [caption align=left].'));
  }
}

/**
 * Filter process callback. Replace the [caption] tags with HTML.
 */
function caption_filter_process_filter($text, $format = -1) {

  // Prevent useless processing if there are no caption tags at all.
  if (stristr($text, '[caption') !== FALSE) {
    $pattern = "/(\\[caption([^\\]]*)\\])(.*?)(\\[\\/caption\\])/";
    $text = preg_replace_callback($pattern, '_caption_filter_replace', $text);
  }
  return $text;
}

/**
 * Callback for preg_replace_callback() in caption_filter_process_filter().
 *
 * This function actually does the work of replacing each [caption] tag with
 * HTML output.
 */
function _caption_filter_replace($matches) {
  $caption_attributes = _caption_filter_tag_attributes($matches[2]);
  $item = $matches[3];
  $width = _caption_filter_get_width($item);
  $align = isset($caption_attributes['align']) ? $caption_attributes['align'] : 'center';
  $caption = isset($caption_attributes['caption']) ? $caption_attributes['caption'] : '';

  // Remove "align" from the start of the alignment if needed. WordPress
  // commonly uses align="alignright" for example.
  if (strpos($align, 'align') === 0) {
    $align = substr($align, 5);
  }
  return '<div class="caption caption-' . $align . '"><div class="caption-width-container" style="width: ' . $width . ';"><div class="caption-inner">' . $item . $caption . '</div></div></div>';
}

/**
 * Determine the width of the img/object that is being captioned.
 */
function _caption_filter_get_width($item) {

  // In case we cannot determine the width, just set it to the default css value
  $width = 'auto';

  // find an img or object tag within the [caption] to determine the width
  if (preg_match_all('/<(img|object)[^>]+>/i', $item, $internal_result)) {
    foreach ($internal_result['0'] as $tag) {

      // If the width is defined, dump that into the $widths array.
      if (preg_match_all('/width="([0-9]*)"/i', $tag, $width_result)) {
        $width = $width_result[1][0];
      }
      else {
        preg_match('/src="([^"]*)"/i', $tag, $src_result);

        // if there is a src tag
        if (!empty($src_result)) {
          $src = $src_result['1'];
          $parts = parse_url($src);
          if (empty($parts['scheme'])) {

            // getcwd() is based off index.php's location, but we'll filter
            // out the caption_filter path just in case
            $drupal_root = str_replace(drupal_get_path('module', 'caption_filter'), '', getcwd());

            // Remove any query string attached to the images, as query strings
            // break getimagesize()
            if (!empty($parts['query'])) {
              $local_path = $drupal_root . str_replace('?' . $parts['query'], '', $src);
            }
            else {
              $local_path = $drupal_root . $src;
            }
            list($width) = getimagesize($local_path);
          }
          else {
            list($width) = getimagesize($src_result[1]);
          }
        }
      }
    }
  }

  // Free up memory.
  unset($src_result, $width_result, $internal_result, $tag, $item);

  // We need to append the 'px' to any numeric widths.
  if (is_numeric($width)) {
    $width = $width . 'px';
  }
  return $width;
}

/**
 * Retrieve all attributes from a caption "shortcode" tag.
 *
 * This code is based upon the WordPress function shortcode_parse_atts().
 *
 * @see http://core.svn.wordpress.org/branches/3.2/wp-includes/shortcodes.php
 *
 * @param $text
 *   The shortcode tag attributes to be parsed. This should not include the
 *   brackets, the tag itself, or any of the contents within the tag. It should
 *   be a string of attributes, such as:
 *
 *   @code
 *     attr1="value 1" attr2=value2 value3
 *   @endcode
 *
 *   Note that these tags may or may not use quotes, either single or double.
 * @return
 *   An array of attributes and their value.
 */
function _caption_filter_tag_attributes($text) {
  $atts = array();
  $pattern = '/(\\w+)\\s*=\\s*"([^"]*)"(?:\\s|$)|(\\w+)\\s*=\\s*\'([^\']*)\'(?:\\s|$)|(\\w+)\\s*=\\s*([^\\s\'"]+)(?:\\s|$)|"([^"]*)"(?:\\s|$)|(\\S+)(?:\\s|$)/';
  $text = preg_replace("/[\\x{00a0}\\x{200b}]+/u", " ", $text);
  if (preg_match_all($pattern, $text, $match, PREG_SET_ORDER)) {
    foreach ($match as $m) {
      if (!empty($m[1])) {
        $atts[strtolower($m[1])] = stripcslashes($m[2]);
      }
      elseif (!empty($m[3])) {
        $atts[strtolower($m[3])] = stripcslashes($m[4]);
      }
      elseif (!empty($m[5])) {
        $atts[strtolower($m[5])] = stripcslashes($m[6]);
      }
      elseif (isset($m[7]) and strlen($m[7])) {
        $atts[strtolower($m[7])] = TRUE;
      }
      elseif (isset($m[8])) {
        $atts[strtolower($m[8])] = TRUE;
      }
    }
  }
  else {
    $atts = ltrim($text);
  }
  return $atts;
}

/**
 * Implements hook_wysiwyg_plugin().
 *
 * This hook returns a list of plugins written directly against certain WYSIWYG
 * editors.
 */
function caption_filter_wysiwyg_plugin($editor, $version) {
  $plugins = array();
  if ($editor == 'tinymce') {
    $plugins['captionfilter'] = array(
      'url' => 'http://drupal.org/project/caption_filter',
      'path' => drupal_get_path('module', 'caption_filter') . '/js',
      'filename' => 'caption-filter-tinymce.js',
      // Caption Filter doesn't actually provide a button, but this code is
      // needed to make it so that WYSIWYG module will load our plugin.
      'buttons' => array(
        'captionfilter' => t('Caption Filter'),
      ),
      'options' => array(
        'captionfilter_css' => base_path() . drupal_get_path('module', 'caption_filter') . '/caption-filter.css',
      ),
      'load' => TRUE,
    );
  }
  return $plugins;
}

/**
 * Implements hook_element_info().
 */
function caption_filter_elements() {

  // We only enhance widgets if the Insert module is available.
  if (!function_exists('insert_widgets')) {
    return;
  }
  $extra = array(
    '#after_build' => array(
      'caption_filter_element_process',
    ),
  );
  $elements = array();
  foreach (insert_widgets() as $widget_type => $widget) {
    if (isset($widget['fields']['title'])) {
      $element_type = isset($widget['element_type']) ? $widget['element_type'] : $widget_type;
      $elements[$element_type] = $extra;
    }
  }
  return $elements;
}

/**
 * Form API #process function for elements.
 */
function caption_filter_element_process($element) {
  static $js_added;
  $field = content_fields($element['#field_name'], $element['#type_name']);
  $widget_settings = $field['widget'];
  $widget_type = $field['widget']['type'];
  if (isset($element['data']['title'])) {
    $element['data']['title']['#title'] = t('Caption');
    $element['data']['title']['#description'] = NULL;

    // Add settings for this widget only once.
    if (!isset($js_added[$widget_type])) {
      $js_added[$widget_type] = TRUE;
      $caption_settings = array(
        'captionFromTitle' => $widget_settings['caption_from_title'],
      );
      drupal_add_js(array(
        'captionFilter' => array(
          'widgets' => array(
            $widget_type => $caption_settings,
          ),
        ),
      ), 'setting');
    }
  }
  return $element;
}

/**
 * Implementation of hook_widget_settings_alter().
 */
function caption_filter_widget_settings_alter(&$settings, $op, $widget) {

  // Only support modules that implement hook_insert_widgets().
  $widget_type = isset($widget['widget_type']) ? $widget['widget_type'] : $widget['type'];
  if (!function_exists('insert_widgets') || !in_array($widget_type, array_keys(insert_widgets()))) {
    return;
  }

  // Add our new options to the list of settings to be saved.
  if ($op == 'save') {
    $settings = array_merge($settings, caption_filter_widget_settings());
  }

  // Add the additional settings to the form.
  if ($op == 'form' && isset($settings['title_settings']['custom_title'])) {
    $settings['insert'] = array_merge($settings['insert'], caption_filter_widget_form($widget));
  }
}

/**
 * A list of settings needed by Caption Filter module on widgets.
 */
function caption_filter_widget_settings() {
  return array(
    'caption_from_title',
  );
}

/**
 * Configuration form for editing insert settings for a field instance.
 */
function caption_filter_widget_form($widget) {
  $settings = $widget['settings'];
  $form['caption_from_title'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use the <em>Title</em> field as a caption'),
    '#default_value' => isset($settings['caption_from_title']) ? $settings['caption_from_title'] : 1,
    '#element_validate' => array(
      'caption_filter_field_widget_caption_validate',
    ),
    '#description' => t('This feature requires that the <em>Title</em> field be enabled above, and that the "Caption filter" be enabled in the <a href="!formats">input formats configuration</a>.', array(
      '!formats' => url('admin/settings/filters'),
    )),
    '#weight' => -9,
  );
  return $form;
}

/**
 * An #element_validate function for the "caption_from_title" field.
 */
function caption_filter_field_widget_caption_validate($element, &$form_state) {
  if ($element['#value'] && empty($form_state['values']['custom_title'])) {
    form_error($element, t('The <em>Title</em> field must be enabled to use it as a caption.'));
  }
}

Functions

Namesort descending Description
caption_filter_elements Implements hook_element_info().
caption_filter_element_process Form API #process function for elements.
caption_filter_field_widget_caption_validate An #element_validate function for the "caption_from_title" field.
caption_filter_filter Implementation of hook_filter().
caption_filter_filter_tips Implementation of hook_filter_tips().
caption_filter_init Implementation of hook_init().
caption_filter_process_filter Filter process callback. Replace the [caption] tags with HTML.
caption_filter_widget_form Configuration form for editing insert settings for a field instance.
caption_filter_widget_settings A list of settings needed by Caption Filter module on widgets.
caption_filter_widget_settings_alter Implementation of hook_widget_settings_alter().
caption_filter_wysiwyg_plugin Implements hook_wysiwyg_plugin().
_caption_filter_get_width Determine the width of the img/object that is being captioned.
_caption_filter_replace Callback for preg_replace_callback() in caption_filter_process_filter().
_caption_filter_tag_attributes Retrieve all attributes from a caption "shortcode" tag.