You are here

word_link.module in Word Link 7

Same filename and directory in other branches
  1. 8 word_link.module
  2. 7.2 word_link.module

This module allows users to replace previously defined words to the links.

File

word_link.module
View source
<?php

/**
 * @file
 * This module allows users to replace previously defined words to the links.
 */

/**
 * Implements hook_permission().
 */
function word_link_permission() {
  return array(
    'view word link' => array(
      'title' => t('View Word Link'),
    ),
    'create word link' => array(
      'title' => t('Create Word Link'),
    ),
    'edit word link' => array(
      'title' => t('Edit Word Link'),
    ),
    'delete word link' => array(
      'title' => t('Delete Word Link'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function word_link_menu() {
  $items = array();
  $items['admin/config/content/word-link'] = array(
    'title' => 'Word Link',
    'description' => 'Administer Words Link',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'word_link_list_page_form',
    ),
    'access arguments' => array(
      'view word link',
    ),
    'file' => 'word_link.admin.inc',
  );
  $items['admin/config/content/word-link/list'] = array(
    'title' => 'List',
    'description' => 'Words list',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'word_link_list_page_form',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'word_link.admin.inc',
  );
  $items['admin/config/content/word-link/configuration'] = array(
    'title' => 'Configuration',
    'description' => 'Set the preferences for Word Link module.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'word_link_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'word_link.admin.inc',
    'weight' => 2,
  );
  $items['admin/config/content/word-link/add'] = array(
    'title' => 'Add new word',
    'description' => 'Add word',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'word_link_add_form',
    ),
    'access arguments' => array(
      'create word link',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'word_link.admin.inc',
  );
  $items['admin/config/content/word-link/edit/%'] = array(
    'title' => 'Edit word',
    'description' => 'Word edit',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'word_link_add_form',
      5,
    ),
    'access arguments' => array(
      'edit word link',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'word_link.admin.inc',
  );
  $items['admin/config/content/word-link/delete/%'] = array(
    'title' => 'Are you sure you want to delete this word?',
    'description' => 'Word delete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'word_link_del_form',
      5,
    ),
    'access arguments' => array(
      'delete word link',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'word_link.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_menu_local_tasks_alter().
 */
function word_link_menu_local_tasks_alter(&$data, $router_item, $root_path) {

  // Add action link to 'admin/config/content/word-link/add'
  // on 'admin/config/content/word-link/list' page.
  if ($root_path == 'admin/config/content/word-link') {
    $item = menu_get_item('admin/config/content/word-link/add');
    if ($item['access']) {
      $data['actions']['output'][] = array(
        '#theme' => 'menu_local_action',
        '#link' => $item,
      );
    }
  }
}

/**
 * Implements hook_comment_view().
 */
function word_link_comment_view($comment, $view_mode, $langcode) {
  $types = variable_get('word_link_node_types', array());

  // Get node type.
  $node_type = str_replace("comment_node_", '', $comment->node_type);
  if (isset($types[$node_type]['fields']['comments'])) {
    $words = word_link_get_link();

    // Check CSS status.
    if (variable_get('word_link_css', 1)) {
      drupal_add_css(drupal_get_path('module', 'word_link') . '/css/word_link.css');
    }

    // Rewrite value.
    $value = $comment->content['comment_body'][0]['#markup'];
    $comment->content['comment_body'][0]['#markup'] = word_link_replace_text($words, $value);
  }
}

/**
 * Implements hook_node_update().
 */
function word_link_node_update($node) {
  $types = variable_get('word_link_node_types', array());

  // Check if for this node selected any field to convert words.
  if (!empty($types[$node->type]['fields'])) {
    $cache_key = "word_link_nid_{$node->nid}";
    cache_clear_all($cache_key, 'cache', TRUE);
  }
}

/**
 * Implements hook_node_view().
 */
function word_link_node_view($node, $view_mode, $langcode) {

  // Replace words for every view mode except rss.
  if ($view_mode != 'rss') {
    $types = variable_get('word_link_node_types', array());

    // Check current content type.
    if (!empty($types[$node->type]['fields'])) {

      // Get all words from DB.
      $words = word_link_get_link();
      if (!empty($words)) {

        // Add style if need.
        $css_status = variable_get('word_link_css', 1);
        if ($css_status) {
          drupal_add_css(drupal_get_path('module', 'word_link') . '/css/word_link.css');
        }

        // Convert words for each field.
        word_link_process_node_fields($node, $types, $words);
      }
    }
  }
}

/**
 * Find node fields content and pass it for processing.
 *
 * @param object $node
 *   Full node object.
 * @param array $types
 *   Content types in which need to convert words.
 * @param array $words
 *   User defined words.
 */
function word_link_process_node_fields(&$node, $types, $words) {
  foreach ($types[$node->type]['fields'] as $field_name => $field) {

    // Check if we process a field.
    if (!is_array($field)) {

      // Check if field has some text.
      if (!empty($node->content[$field]['#items'])) {

        // Convert words for each field value.
        foreach ($node->content[$field]['#items'] as $delta => $item) {

          // If output format is not HTML, then don’t do anything.
          if ($item['format'] != 'plain_text' && !empty($node->content[$field][$delta]['#markup'])) {

            // Change field value.
            $cache_key = "word_link_nid_{$node->nid}_{$field}_{$delta}_" . strlen($node->content[$field][$delta]['#markup']);
            $cached = cache_get($cache_key);
            if (isset($cached->data)) {
              $node->content[$field][$delta]['#markup'] = $cached->data;
            }
            else {
              $words = word_link_get_link();
              $node->content[$field][$delta]['#markup'] = word_link_replace_text($words, $node->content[$field][$delta]['#markup']);
              cache_set($cache_key, $node->content[$field][$delta]['#markup']);
            }
          }
        }
      }
    }
    else {
      foreach ($field as $fc) {

        // Check if field have some value.
        if (!empty($node->content[$field_name]['#items'])) {

          // Convert words for each field value.
          foreach ($node->content[$field_name]['#items'] as $fc_delta => $fc_item) {
            if (!empty($node->content[$field_name][$fc_delta]['entity']['field_collection_item'][$fc_item['value']][$fc])) {
              $fc_field =& $node->content[$field_name][$fc_delta]['entity']['field_collection_item'][$fc_item['value']][$fc];
              foreach ($fc_field['#items'] as $fc_field_delta => $fc_field_value) {

                // Change field value if output format is not plain text.
                if ($fc_field_value['format'] != 'plain_text' && !empty($fc_field[$fc_field_delta]['#markup'])) {
                  $cache_key = "word_link_nid_{$node->nid}_{$field}_{$fc_delta}_" . strlen($node->content[$field][$delta]['#markup']);
                  $cached = cache_get($cache_key);
                  if (isset($cached->data)) {
                    $value = $cached->data;
                  }
                  else {
                    $words = word_link_get_link();
                    $value = word_link_replace_text($words, $fc_field[$fc_field_delta]['#markup']);
                    cache_set($cache_key, $value);
                  }
                  $fc_field[$fc_field_delta]['#markup'] = $value;
                }
              }
            }
          }
        }
      }
    }
  }
}

/**
 * Find and convert defined word to link.
 *
 * @param array $words
 *   Array with words.
 * @param string $subject
 *   String in which need convert words to links.
 *
 * @return string
 *   String with converted words.
 */
function word_link_replace_text($words, $subject) {

  // Get disallowed html tags and explode it to array.
  $disallowed_tags = variable_get('word_link_tags_except', NULL);
  $disallowed_tags = preg_split('/\\s+|<|>/', $disallowed_tags, -1, PREG_SPLIT_NO_EMPTY);

  // Implode all disallowed tags to string.
  $not = '';
  if (!empty($disallowed_tags)) {
    $not = array();
    foreach ($disallowed_tags as $ancestor) {
      $not[] = 'and not(ancestor::' . $ancestor . ')';
    }
    $not = implode(' ', $not);
  }

  // Get word link limit.
  $limit = variable_get('word_link_limit', 0);
  $preg_limit = $limit == 0 ? -1 : $limit;

  // Get current path.
  $path = drupal_strtolower(drupal_get_path_alias());
  $current_path = current_path();

  // Get highlight status.
  $highlight = variable_get('word_link_highlight', 0);

  // Create array of links and replace patterns for each word.
  foreach ($words as $word) {
    $url = drupal_get_path_alias($word->url);
    $match = FALSE;

    // Check if current path matches word except path.
    if (!empty($word->except)) {
      $match = drupal_match_path($path, $word->except);
      if ($path != $current_path) {
        $match = $match || drupal_match_path($current_path, $word->except);
      }
    }

    // Get visibility status and check if need to convert word on this page.
    $visibility = empty($word->except) || !isset($word->visibility) ? FALSE : $word->visibility;
    if ($url != $path && !$match && !$visibility || $url != $path && $visibility && $match) {

      // Pattern which will be used to replace words.
      $pattern = '$0';

      // If highlight is off build links.
      if (!$highlight) {

        // Build external link.
        if (strpos($word->url, 'http://') === 0 || strpos($word->url, 'https://') === 0) {
          $replace[] = l($pattern, $word->url, array(
            'attributes' => array_filter(array(
              'class' => array(
                $word->class,
              ),
              'rel' => $word->rel,
              'title' => $word->url_title,
              'target' => '_blank',
            )),
            'external' => TRUE,
          ));
        }
        else {
          $replace[] = l($pattern, $word->url, array(
            'attributes' => array_filter(array(
              'class' => array(
                $word->class,
              ),
              'rel' => $word->rel,
              'title' => $word->url_title,
            )),
          ));
        }
      }
      else {
        $replace[] = '<span title="' . $word->url_title . '" class="' . $word->class . '">' . $pattern . '</span>';
      }

      // Build a patterns array.
      $pattern = '/((\\b)|(?<=))(' . preg_quote($word->text, '/') . ')(?!(".*>)|[\'"&’^])\\b/u';
      if ($word->case_sensitive) {
        $search[] = $pattern;
      }
      else {
        $search[] = $pattern . 'i';
      }
    }
  }

  // Find and convert words if $searh and $replace arrays are not empty.
  if (isset($search) && isset($replace)) {

    // Replace '&' to '&amp;';
    $subject = preg_replace('/&(?![a-z])/u', '&amp;', $subject);

    // Replace '&amp;' to '&#38;#38;';
    $subject = str_replace('&amp;', '&#38;#38;', $subject);

    // Turn off errors.
    libxml_use_internal_errors(TRUE);

    // Load string to DOMDocument.
    $dom = filter_dom_load($subject);
    $xpath = new DOMXPath($dom);
    if ($limit != 0) {
      $counts = array_fill(0, count($search), 0);
      foreach ($xpath
        ->query('//text()[not(ancestor::a) ' . $not . ']') as $node) {
        $replaced = $node->wholeText;
        $del = array_filter($counts);
        foreach ($del as $key => $val) {
          if ($val >= $limit) {
            unset($search[$key], $replace[$key]);
          }
        }
        foreach ($search as $id => $search_word) {
          $new_preg_limit = $preg_limit - $counts[$id];
          $replaced = preg_replace($search_word, $replace[$id], $replaced, $new_preg_limit, $count);
          $counts[$id] += $count;
        }
        $new_node = $dom
          ->createDocumentFragment();
        $new_node
          ->appendXML($replaced);
        $node->parentNode
          ->replaceChild($new_node, $node);
      }
    }
    else {
      foreach ($xpath
        ->query('//text()[not(ancestor::a) ' . $not . ']') as $node) {
        $replaced = preg_replace($search, $replace, $node->wholeText);
        $new_node = $dom
          ->createDocumentFragment();
        $new_node
          ->appendXML($replaced);
        $node->parentNode
          ->replaceChild($new_node, $node);
      }
    }

    // Convert a DOM object back to an HTML snippet.
    $subject = filter_dom_serialize($dom);
  }

  // Remove '#38;' entity.
  $subject = str_replace('#38;', '', $subject);
  return $subject;
}

/**
 * Get links from DB.
 *
 * @param int $id
 *   (optional) ID of a word.
 * @param array $header
 *   (optional) header to build a table.
 * @param int $limit
 *   (optional) determines the number of fields to get.
 *
 * @return array
 *   Formatted array with links.
 */
function word_link_get_link($id = NULL, $header = array(), $limit = NULL) {
  $words = drupal_static(__FUNCTION__);
  $key = FALSE;

  // Get only one link by given id.
  if ($id) {
    $query = isset($words['id'][$id]) ? $words['id'][$id] : db_select('word_link', 'wl')
      ->fields('wl')
      ->condition('id', $id, '=')
      ->execute()
      ->fetchAll();
    $key = 'id_' . $id;
  }
  elseif (empty($id) && empty($header) && empty($limit)) {
    $query = isset($words['empty']) ? $words['empty'] : db_select('word_link', 'wl')
      ->fields('wl')
      ->execute()
      ->fetchAll();
    $key = 'empty';
  }
  else {
    $header_serialized = serialize($header);
    $query = isset($words['header'][$header_serialized]) ? $words['header'][$header_serialized] : ($query = db_select('word_link', 'wl')
      ->extend('PagerDefault')
      ->limit($limit)
      ->extend('TableSort')
      ->orderByHeader($header)
      ->fields('wl')
      ->execute()
      ->fetchAll());
    $key = 'header_' . $header_serialized;
  }
  $words[$key] = $query;
  $links = array();
  if (isset($words['ready'][$key])) {
    $links = $words['ready'][$key];
  }
  elseif (count($query) > 0) {
    foreach ($query as $value) {
      $links[$value->id] = $value;
    }
    $words['ready'][$key] = $links;
  }
  return $links;
}

/**
 * Verify by text if link already exists.
 *
 * @param string $text
 *   Word text which need to find.
 * @param int $id
 *   (optional) ID of a word.
 *
 * @return bool
 *   Return word ID if word exist or FALSE if not.
 */
function word_link_word_exist($text, $id = 0) {
  if ($text) {
    $result = db_select('word_link', 'wl')
      ->fields('wl', array(
      'text',
      'case_sensitive',
      'id',
    ))
      ->condition('id', $id, '!=')
      ->condition('text', $text, '=')
      ->execute()
      ->fetchAll();
  }
  if (empty($result)) {
    return FALSE;
  }
  if (count($result) > 1) {
    return $result[0]->id;
  }
  elseif (drupal_strtolower($text) == drupal_strtolower($result[0]->text)) {
    return $result[0]->id;
  }
  return FALSE;
}

/**
 * Add/Update link to DB.
 *
 * @param string $action
 *   Action which need to do.
 *   Can be 'insert' or 'update'.
 * @param array $values
 *   Array with values that need to process.
 * @param int $id
 *   (optional) ID of a word.
 */
function word_link_add_update_link($action, $values, $id = NULL) {
  if ($action == 'insert') {
    drupal_write_record('word_link', $values);
  }
  elseif ($action == 'update') {
    $values['id'] = $id;
    drupal_write_record('word_link', $values, 'id');
  }
}

/**
 * Delete link from DB.
 *
 * @param int $id
 *   ID of a word.
 */
function word_link_del_link($id) {
  db_delete('word_link')
    ->condition('id', $id)
    ->execute();
}

/**
 * Get textarea fields of given content type.
 *
 * @param string $type
 *   The bundle name for which to return fields.
 *
 * @return array
 *   Array with textarea fields or empty array if it's not found.
 */
function word_link_get_node_fields($type) {
  $fields = field_info_instances('node', $type);
  $text_fields = array();
  foreach ($fields as $name => $field) {
    if ($field['widget']['type'] == 'text_textarea_with_summary' || $field['widget']['type'] == 'text_textarea') {
      $text_fields[$field['field_name']] = $field['label'];
    }
    elseif ($field['widget']['type'] == 'field_collection_embed') {
      $fc_fields = field_info_instances('field_collection_item', $field['field_name']);
      foreach ($fc_fields as $fc_name => $fc_field) {
        if ($fc_field['widget']['type'] == 'text_textarea_with_summary' || $fc_field['widget']['type'] == 'text_textarea') {
          $text_fields[$field['field_name']][$field['field_name'] . '__' . $fc_field['field_name']] = $fc_field['label'];
        }
      }
    }
  }
  return $text_fields ? $text_fields : array();
}

Functions

Namesort descending Description
word_link_add_update_link Add/Update link to DB.
word_link_comment_view Implements hook_comment_view().
word_link_del_link Delete link from DB.
word_link_get_link Get links from DB.
word_link_get_node_fields Get textarea fields of given content type.
word_link_menu Implements hook_menu().
word_link_menu_local_tasks_alter Implements hook_menu_local_tasks_alter().
word_link_node_update Implements hook_node_update().
word_link_node_view Implements hook_node_view().
word_link_permission Implements hook_permission().
word_link_process_node_fields Find node fields content and pass it for processing.
word_link_replace_text Find and convert defined word to link.
word_link_word_exist Verify by text if link already exists.