You are here

glossify.module in Glossify 7.4

Glossify module.

File

glossify.module
View source
<?php

/**
 * @file
 * Glossify module.
 */

/**
 * Implements hook_help().
 */
function glossify_help($path, $arg) {
  switch ($path) {
    case 'admin/help#glossify':
      $output = '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('The Glossify module provides filters that scan and parse content and replace terms in the text with links to their pages. It consists of 2 filters:') . '</p>';
      $items = array();
      $items[] = array(
        'data' => t('Glossify with taxonomy'),
        'children' => array(
          t('links taxonomy terms appearing in content to their taxonomy term page.'),
          t('select which taxonomy vocabularies to use as the source for the terms.'),
          t('indicate whether or not the match is case sensitive.'),
          t('indicate whether or not every match should be linked or just the first occurrence.'),
          t('display the term definition as a tooltip while hovering the glossified link.'),
        ),
      );
      $items[] = array(
        'data' => t('Glossify with content'),
        'children' => array(
          t('links node titles of content appearing in other content to their node page'),
          t('select which content types to use as the source for the terms.'),
          t('indicate whether or not matching is case sensitive.'),
          t('indicate whether or not every match should be linked or just the first occurrence.'),
          t('display the text from a selected field on the linked node as a tooltip while hovering the glossified link.'),
        ),
      );
      $output .= theme('item_list', array(
        'items' => $items,
        'type' => 'ul',
      ));
      $items = array();
      $items[] = t('Navigate to the <a href="!url">Text formats</a> configuration page. Click "configure" next to the text format you upon which you want to enable the Glossify filter.', array(
        '!url' => '/admin/config/content/formats',
      ));
      $items[] = t('Check the box for "Glossify with taxonomy" and/or "Glossify with content" under the list of filters in the "Enabled filters" section.');
      $items[] = t('Check the boxes for the desired taxonomy vocabularies and/or content types you wish to use the source of terms to link.');
      $items[] = t('Set any other options as desired.');
      $output .= '<h3>' . t('Configuration/Usage') . '</h3>';
      $output .= theme('item_list', array(
        'items' => $items,
        'type' => 'ol',
      ));
      $output .= '<h3>' . t('Theming') . '</h3>';
      $output .= '<p>' . t('The module provides a theme function (theme_glossify_links), some basic css, and 2 icons to format the links. Override and customize as desired. See <a href="!url">Beginners guide to overriding themable output</a> for more detailed instructions.', array(
        '!url' => 'https://drupal.org/node/457740',
      )) . '</p>';
      return $output;
  }
}

/**
 * Implements hook_filter_info().
 */
function glossify_filter_info() {
  $filters = array();
  if (module_exists('taxonomy')) {
    $filters['glossify_taxonomy'] = array(
      'title' => t('Glossify with taxonomy'),
      'description' => t('Links taxonomy terms appearing in content to their taxonomy term page.'),
      'process callback' => '_glossify_taxonomy_process',
      'settings callback' => '_glossify_taxonomy_settings',
      'default settings' => array(
        'glossify_taxonomy_vocabs' => array(),
        'glossify_taxonomy_case_sensitivity' => FALSE,
        'glossify_taxonomy_first_only' => TRUE,
        'glossify_taxonomy_tooltips' => FALSE,
      ),
    );
  }
  $filters['glossify_content'] = array(
    'title' => t('Glossify with content'),
    'description' => t('Links titles of content appearing in other content to their page.'),
    'process callback' => '_glossify_content_process',
    'settings callback' => '_glossify_content_settings',
    'default settings' => array(
      'glossify_content_types' => array(),
      'glossify_content_case_sensitivity' => FALSE,
      'glossify_content_first_only' => TRUE,
      'glossify_content_tooltips' => FALSE,
      'glossify_content_tooltip_field' => 'body',
    ),
  );
  if (defined('FILTER_TYPE_TRANSFORM_REVERSIBLE')) {
    $filters['glossify_taxonomy']['type'] = FILTER_TYPE_TRANSFORM_REVERSIBLE;
    $filters['glossify_content']['type'] = FILTER_TYPE_TRANSFORM_REVERSIBLE;
  }
  return $filters;
}

/**
 * The settings for configuring the glossify taxonomy filter.
 */
function _glossify_taxonomy_settings($form, $form_state, $filter, $format, $defaults) {
  $filter->settings += $defaults;
  $vocabs = array();
  foreach (taxonomy_get_vocabularies() as $vocab) {
    $vocabs[$vocab->vid] = $vocab->name;
  }
  $settings['glossify_taxonomy_case_sensitivity'] = array(
    '#type' => 'checkbox',
    '#title' => t('Case sensitive'),
    '#description' => t('Whether or not the match is case sensitive.'),
    '#default_value' => $filter->settings['glossify_taxonomy_case_sensitivity'],
  );
  $settings['glossify_taxonomy_first_only'] = array(
    '#type' => 'checkbox',
    '#title' => t('First match only'),
    '#description' => t('Match and link only the first occurance.'),
    '#default_value' => $filter->settings['glossify_taxonomy_first_only'],
  );
  $settings['glossify_taxonomy_tooltips'] = array(
    '#type' => 'checkbox',
    '#title' => t('Tool tips'),
    '#description' => t('Enable tooltips displaying the taxonomy term description when hovering the link.'),
    '#default_value' => $filter->settings['glossify_taxonomy_tooltips'],
  );
  $settings['glossify_taxonomy_vocabs'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Taxonomy vocabularies'),
    '#description' => t('Select the taxonomy vocabularies who\'s terms should be linked to their term page.'),
    '#options' => $vocabs,
    '#default_value' => $filter->settings['glossify_taxonomy_vocabs'],
  );
  return $settings;
}

/**
 * The settings for configuring the glossify content filter.
 */
function _glossify_content_settings($form, $form_state, $filter, $format, $defaults) {
  $filter->settings += $defaults;
  $types = array();
  $fields = array();

  // get all node types
  foreach (node_type_get_types() as $type) {
    $types[$type->type] = $type->name;
  }
  ksort($types);

  //get all fields that appear on node bundles
  foreach (field_info_fields() as $field) {
    if (isset($field['bundles']['node'])) {
      $fields[$field['field_name']] = $field['field_name'];
    }
  }
  ksort($fields);
  $settings['glossify_content_case_sensitivity'] = array(
    '#type' => 'checkbox',
    '#title' => t('Case sensitive'),
    '#description' => t('Whether or not the match is case sensitive.'),
    '#default_value' => $filter->settings['glossify_content_case_sensitivity'],
  );
  $settings['glossify_content_first_only'] = array(
    '#type' => 'checkbox',
    '#title' => t('First match only'),
    '#description' => t('Match and link only the first occurance.'),
    '#default_value' => $filter->settings['glossify_content_first_only'],
  );
  $settings['glossify_content_tooltips'] = array(
    '#type' => 'checkbox',
    '#title' => t('Tool tips'),
    '#description' => t('Enable tooltips displaying the node body when hovering the link.'),
    '#default_value' => $filter->settings['glossify_content_tooltips'],
  );
  $settings['glossify_content_tooltip_field'] = array(
    '#type' => 'select',
    '#title' => t('Tool tip field'),
    '#description' => t('Select the node field to be used for the tooltip. The field should appear on all selected content types. NOTE: multivalue fields will only use the first value (delta = 0).'),
    '#options' => $fields,
    '#default_value' => $filter->settings['glossify_content_tooltip_field'],
    '#states' => array(
      'invisible' => array(
        'input[name="filters[glossify_content][settings][glossify_content_tooltips]"]' => array(
          'checked' => FALSE,
        ),
      ),
    ),
  );
  $settings['glossify_content_types'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Content types'),
    '#description' => t('Select the content types who\'s titles should be linked to their content page.'),
    '#options' => $types,
    '#default_value' => $filter->settings['glossify_content_types'],
  );
  return $settings;
}

/**
 * Taxonomy filter process callback for the glossify filter.
 */
function _glossify_taxonomy_process($text, $filter, $format, $langcode, $cache, $cache_id) {

  //get vocabularies
  $vocabs = array_filter($filter->settings['glossify_taxonomy_vocabs']);
  if (!$vocabs) {
    return $text;
  }

  //get terms
  $result = db_query("SELECT tid AS id, name, LOWER(name) AS name_norm, description AS tip, format FROM {taxonomy_term_data} WHERE vid IN (:vids) ORDER by CHAR_LENGTH(name) DESC", array(
    ':vids' => $vocabs,
  ));
  $terms = $result
    ->fetchAllAssoc('name_norm');

  //process text
  if (count($terms) > 0) {
    return _glossify_to_links($text, $terms, 'taxonomy', $filter->settings['glossify_taxonomy_case_sensitivity'], $filter->settings['glossify_taxonomy_first_only'], $filter->settings['glossify_taxonomy_tooltips']);
  }
  else {
    return $text;
  }
}

/**
 * Content filter process callback for the glossify filter.
 */
function _glossify_content_process($text, $filter, $format, $langcode, $cache, $cache_id) {

  //get content types
  $types = array_filter($filter->settings['glossify_content_types']);
  if (!$types) {
    return $text;
  }

  //get titles
  if (!$filter->settings['glossify_content_tooltips']) {
    $result = db_query("SELECT nid AS id, title AS name, LOWER(title) AS name_norm FROM {node} WHERE status = 1 AND type IN (:types)", array(
      ':types' => $types,
    ));
  }
  else {
    $field_name = $filter->settings['glossify_content_tooltip_field'];
    $result = db_query("SELECT n.nid AS id, n.title AS name, LOWER(title) AS name_norm, f." . $field_name . "_value AS tip, f." . $field_name . "_format AS format FROM {node n} LEFT OUTER JOIN {field_data_" . $field_name . " f} ON n.vid = f.revision_id WHERE n.status = 1 AND n.type IN (:types) AND (f.delta = 0 OR f.delta IS NULL)", array(
      ':types' => $types,
    ));
  }
  $terms = $result
    ->fetchAllAssoc('name_norm');

  //process text
  if (count($terms) > 0) {
    return _glossify_to_links($text, $terms, 'content', $filter->settings['glossify_content_case_sensitivity'], $filter->settings['glossify_content_first_only'], $filter->settings['glossify_content_tooltips']);
  }
  else {
    return $text;
  }
}

/**
 * Convert terms in text to links.
 *
 * @param $text
 *   The HTML text upon which the filter is acting.
 * @param $terms
 *   The terms (strings) to be replaced with links.
 * @param $type
 *   'taxonomy' for linking to taxonomy terms.
 *   'content' for linking to nodes.
 *
 * @return
 *   The original HTML with the term string replaced by links.
 */
function _glossify_to_links($text, $terms, $type, $case_sensitivity, $first_only, $tooltips) {
  static $recursion_protection = FALSE;
  if ($recursion_protection) {
    return $text;
  }
  $recursion_protection = TRUE;

  //create dom document
  $html_dom = filter_dom_load($text);
  $xpath = new DOMXPath($html_dom);
  $pattern = array();
  $matched = array();

  //transform terms into normalized search pattern
  foreach ($terms as $term) {
    $term_norm = preg_replace('/\\s+/u', ' ', preg_quote(trim($term->name)));
    $term_norm = preg_replace('#/#u', '\\/', $term_norm);
    $pattern[] = preg_replace('/ /u', '\\s+', $term_norm);
  }
  $pattern = '/\\b(' . implode('|', $pattern) . ')\\b/u';
  if (!$case_sensitivity) {
    $pattern .= 'i';
  }

  //process HTML
  $text_nodes = $xpath
    ->query('//text()[not(ancestor::a)]');
  foreach ($text_nodes as $original_node) {
    $text = $original_node->nodeValue;
    $hitcount = preg_match_all($pattern, $text, $matches, PREG_OFFSET_CAPTURE);
    if ($hitcount > 0) {
      $offset = 0;
      $parent = $original_node->parentNode;
      $refnode = $original_node->nextSibling;
      $parent
        ->removeChild($original_node);
      foreach ($matches[0] as $i => $match) {
        $term_txt = $match[0];
        $term_pos = $match[1];
        $term_norm = preg_replace('/\\s+/u', ' ', $term_txt);

        // insert any text before the term instance
        $prefix = substr($text, $offset, $term_pos - $offset);
        $parent
          ->insertBefore($html_dom
          ->createTextNode($prefix), $refnode);

        // insert the actual term instance as a link
        $link = $html_dom
          ->createDocumentFragment();
        if ($first_only && in_array($case_sensitivity ? $match[0] : drupal_strtolower($match[0]), $matched)) {
          $link
            ->appendXML($term_txt);
        }
        else {
          $tip = $tooltips ? check_markup($terms[drupal_strtolower($term_norm)]->tip, $terms[drupal_strtolower($term_norm)]->format) : '';
          $link
            ->appendXML(theme('glossify_links', array(
            'type' => $type,
            'id' => $terms[drupal_strtolower($term_norm)]->id,
            'text' => $term_txt,
            'tip' => $tip,
          )));
          $matched[] = $case_sensitivity ? $match[0] : drupal_strtolower($match[0]);
        }
        $parent
          ->insertBefore($link, $refnode);
        $offset = $term_pos + strlen($term_txt);

        // last match, append remaining text
        if ($i == $hitcount - 1) {
          $suffix = substr($text, $offset);
          $parent
            ->insertBefore($html_dom
            ->createTextNode($suffix), $refnode);
        }
      }
    }
  }
  $recursion_protection = FALSE;
  return filter_dom_serialize($html_dom);
}

/**
 * Implements hook_theme().
 */
function glossify_theme() {
  return array(
    'glossify_links' => array(
      'arguments' => array(
        'type' => NULL,
        'id' => NULL,
        'text' => NULL,
        'tip' => NULL,
      ),
    ),
  );
}

/**
 * Render a glossify link.
 */
function theme_glossify_links($vars) {
  global $base_url;
  drupal_add_css(drupal_get_path('module', 'glossify') . '/glossify.css');
  if ($vars['type'] == 'taxonomy') {
    $path = 'taxonomy/term/' . $vars['id'];
  }
  else {
    $path = 'node/' . $vars['id'];
  }
  if ($vars['tip']) {
    return l(check_plain($vars['text']) . '<img alt="info-icon" src = "' . $base_url . '/' . drupal_get_path('module', 'glossify') . '/info.png" />', $path, array(
      'html' => true,
      'attributes' => array(
        'class' => array(
          'glossify-link',
        ),
        'title' => $vars['tip'],
      ),
    ));
  }
  else {
    return l(check_plain($vars['text']) . '<img alt="info-icon" src = "' . $base_url . '/' . drupal_get_path('module', 'glossify') . '/info.png" />', $path, array(
      'html' => true,
      'attributes' => array(
        'class' => array(
          'glossify-link',
        ),
      ),
    ));
  }
}

Functions

Namesort descending Description
glossify_filter_info Implements hook_filter_info().
glossify_help Implements hook_help().
glossify_theme Implements hook_theme().
theme_glossify_links Render a glossify link.
_glossify_content_process Content filter process callback for the glossify filter.
_glossify_content_settings The settings for configuring the glossify content filter.
_glossify_taxonomy_process Taxonomy filter process callback for the glossify filter.
_glossify_taxonomy_settings The settings for configuring the glossify taxonomy filter.
_glossify_to_links Convert terms in text to links.