You are here

glossify.module in Glossify 6

File

glossify.module
View source
<?php

/**
 * Implementation of hook_perm().
 */
function glossify_perm() {
  return array(
    'administer glossify',
  );
}

/**
 * Implementation of hook_menu().
 */
function glossify_menu() {
  $items['admin/settings/glossify'] = array(
    'title' => 'Glossify',
    'description' => 'Manipulate glossify behaviour',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'glossify_admin_settings',
    ),
    'access arguments' => array(
      'administer glossify',
    ),
    'file' => 'glossify.admin.inc',
  );
  $weight = 1;
  $items['admin/settings/glossify/global'] = array(
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'title' => t('Global'),
    'weight' => $weight,
  );
  $configurations = variable_get('glossify_configurations', array());
  foreach ($configurations as $configuration => $values) {
    if ($configuration !== 'global') {
      $weight++;
      $items["admin/settings/glossify/{$configuration}"] = array(
        'type' => MENU_LOCAL_TASK,
        'title' => $configuration,
        'page arguments' => array(
          'glossify_admin_settings',
        ),
        'access arguments' => array(
          'administer glossify',
        ),
        'file' => 'glossify.admin.inc',
        'weight' => $weight,
      );
    }
  }
  $items['admin/settings/glossify/add'] = array(
    'type' => MENU_LOCAL_TASK,
    'title' => t('Add a new configuration'),
    'page arguments' => array(
      'glossify_admin_settings',
    ),
    'access arguments' => array(
      'administer glossify',
    ),
    'file' => 'glossify.admin.inc',
    'weight' => 99,
  );
  return $items;
}

/**
 * Implementation of hook_theme().
 */
function glossify_theme() {
  return array(
    'glossify_term' => array(
      'arguments' => array(
        'nid' => NULL,
        'glossify_style' => NULL,
      ),
    ),
    'glossify_reference_section' => array(
      'arguments' => array(
        'term_definition_list' => NULL,
      ),
    ),
  );
}

// endfunction glossify_theme

/**
 * Render a glossary term.
 */
function theme_glossify_term($target, $glossify_style) {

  // outputs proper div so hovertip will work
  if (is_numeric($target)) {
    $term = node_load($target);
  }
  elseif (drupal_lookup_path('source', $target)) {
    $exploded_path = explode('/', drupal_lookup_path('source', $target));
    $term = node_load($exploded_path[count($exploded_path) - 1]);
  }
  switch ($glossify_style) {
    case 'reference':
      $output = '<dt>' . check_plain($term->title) . '</dt>';
      $output .= '<dd>' . check_markup($term->body) . '</dd>';
      break;
    case 'hovertip':
    default:
      $output = '<div id="' . check_plain($term->title) . '" class="hovertip" style="display: none;">';

      // Output a DIV to make hovertip work.
      $output .= '<h1>' . check_plain($term->title) . '</h1>';
      $output .= '<p>' . check_markup($term->body) . '</p>';
      $output .= '</div>';
      break;
  }

  // endswitch glossify style
  return $output;
}

/**
 * Render a glossary term reference.
 */
function theme_glossify_reference_section($term_definition_list) {
  $output = '<div id="glossify-reference">';
  $output .= '<h3>Terms referenced:</h3>';
  $output .= '<dl>';
  $output .= $term_definition_list;
  $output .= '</dl>';
  $output .= '</div>';
  return $output;
}

/**
 * Implementation of hook_form_alter().
 */
function glossify_form_alter(&$form, $form_state, $form_id) {
  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id) {
    $configurations = variable_get('glossify_configurations', array());
    $use_internal = FALSE;
    foreach ($configurations as $configuration) {
      if ($configuration['methods']['use_internal']) {
        $use_internal = TRUE;
      }
    }
    if ($use_internal) {
      $node = $form['#node'];
      $form['glossify'] = array(
        '#type' => 'fieldset',
        '#title' => t('Glossify'),
        '#collapsible' => TRUE,
        '#collapsed' => isset($node->glossify_keywords) && !empty($node->glossify_keywords) || isset($node->glossify_override) && !empty($node->glossify_override) ? FALSE : TRUE,
        '#weight' => 0,
      );
      $form['glossify']['glossify_keywords'] = array(
        '#type' => 'textfield',
        '#title' => t('Glossify Keywords'),
        '#default_value' => isset($node->glossify_keywords) ? $node->glossify_keywords : '',
        '#description' => t('You can add more than one keyword by seperating them with commas.'),
        '#weight' => 0,
      );
      $form['glossify']['glossify_override'] = array(
        '#type' => 'textfield',
        '#title' => t('Glossify Override'),
        '#default_value' => isset($node->glossify_override) ? $node->glossify_override : '',
        '#weight' => 1,
      );
    }
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function glossify_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  switch ($op) {
    case 'load':
      $glossify_keywords = implode(', ', _fetch_keywords($node->nid, 'internal'));
      $glossify_override = db_result(db_query('SELECT alternate FROM {glossify} WHERE nid = %d', $node->nid));
      return array(
        'glossify_keywords' => $glossify_keywords,
        'glossify_override' => $glossify_override,
      );
      break;
    case 'presave':
      $configurations = variable_get('glossify_configurations', array());
      $clear_filter_cache = array();
      foreach ($configurations as $config_name => $configuration) {
        $clear_filter_cache = in_array($node->type, array_merge($configuration['to'], $configuration['from']));
      }
      if ($clear_filter_cache) {
        cache_clear_all('*', 'cache_filter', TRUE);
      }
      break;
    case 'insert':
      $configurations = variable_get('glossify_configurations', array());
      $keywords = array();
      $affected_content_types = array();
      $html_body = str_get_html($node->body);
      $html_teaser = str_get_html($node->teaser);
      foreach ($configurations as $configuration) {
        if (in_array($node->type, $configuration['to'])) {
          if ($configuration['methods']['use_title']) {
            _keyword_table('insert', $node->nid, 'title', $node->language, $node->title);
            $keywords[$node->title] = $node->title;
          }
          if ($configuration['methods']['use_internal'] && !empty($node->glossify_keywords)) {
            foreach (array_map('trim', explode(',', $node->glossify_keywords)) as $keyword) {
              _keyword_table('insert', $node->nid, 'internal', $node->language, $keyword, $node->glossify_override);
              $keywords[$keyword] = $keyword;
            }
          }
          if ($configuration['methods']['use_cck']) {
            $fieldname = $configuration['methods']['keyword_field'];
            $override = $configuration['methods']['override_field'];
            foreach (array_map('trim', explode(',', $node->{$fieldname}[0]['value'])) as $keyword) {
              _keyword_table('insert', $node->nid, 'internal', $node->language, $keyword, $node->{$override}[0]['value']);
              $keywords[$keyword] = $keyword;
            }
          }
          if ($configuration['methods']['use_taxonomy']) {
            foreach (array_map('trim', explode(',', $node->taxonomy['tags'][$configuration['methods']['vocabulary']])) as $keyword) {
              _keyword_table('insert', $node->nid, 'internal', $node->language, $keyword);
              $keywords[$keyword] = $keyword;
            }
          }
          $affected_content_types = array_merge($affected_content_types, $configuration['from']);
        }
      }
      break;
    case 'update':
      $configurations = variable_get('glossify_configurations', array());
      $keywords = array();
      $affected_content_types = array();
      $html_body = str_get_html($node->body);
      $html_teaser = str_get_html($node->teaser);
      foreach ($configurations as $config_name => $configuration) {
        if (in_array($node->type, $configuration['to'])) {
          if ($configuration['methods']['use_title']) {
            if (!db_result(db_query("SELECT gtid FROM {glossify} WHERE nid = %d AND method = '%s'", $node->nid, 'title'))) {
              _keyword_table('insert', $node->nid, 'title', $node->language, $node->title);
              $keywords[$node->title] = $node->title;
            }
            elseif (!db_result(db_query("SELECT gtid FROM {glossify} WHERE nid = %d AND method = '%s' AND term = '%s'", $node->nid, 'title', $node->title))) {
              $keywords = array_merge($keywords, _keyword_table('update', $node->nid, 'title', $node->language, $node->title));
              $keywords[$node->title] = $node->title;
            }
          }
          if ($configuration['methods']['use_internal']) {
            $keywords = array_merge($keywords, _fetch_affected_keywords($node, 'internal'));
          }
          if ($configuration['methods']['use_cck']) {
            $keywords = array_merge($keywords, _fetch_affected_keywords($node, 'cck', $configuration['methods']['keyword_field'], $configuration['methods']['override_field']));
          }
          if ($configuration['methods']['use_taxonomy']) {
            foreach ($configuration['methods']['vocabulary'] as $vocabulary) {
              $keywords = array_merge($keywords, _fetch_affected_keywords($node, 'taxonomy', $vocabulary));
            }
          }
          $affected_content_types[$config_name] = array_merge($affected_content_types, $configuration['from']);
        }
      }
      break;
    case 'view':
      $configurations = variable_get('glossify_configurations', array());
      $term_definition_list = '';
      $referenced_terms = array();
      foreach ($configurations as $config_name => $configuration) {
        if (in_array($node->type, $configuration['from'])) {
          $possible_keywords = _fetch_possible_keywords($configuration, $node->nid);
          if (in_array('reference', $configuration['style'])) {
            foreach ($possible_keywords as $term_title => $path) {
              if ($configuration['break']) {
                if (preg_match('/\\b' . $term_title . '\\b/', $node->body)) {
                  if (!in_array($path, $referenced_terms)) {
                    $term_definition_list .= theme('glossify_term', $path, 'reference');
                    $referenced_terms[] = $path;
                  }
                }
              }
              else {
                if (preg_match('/' . $term_title . '/', $node->body)) {
                  if (!in_array($path, $referenced_terms)) {
                    $term_definition_list .= theme('glossify_term', $path, 'reference');
                    $referenced_terms[] = $path;
                  }
                }
              }
            }
          }
          if (in_array('hovertip', $configuration['style'])) {
            foreach ($possible_keywords as $term_title => $path) {
              if ($configuration['break']) {
                if (preg_match('/\\b' . $term_title . '\\b/', $node->body)) {
                  $node->content['glossify_hovertip'][$term_title] = array(
                    '#value' => theme('glossify_term', $path, 'hovertip'),
                    '#weight' => 10,
                  );
                  $node->content['glossify_hovertip']['#weight'] = 10;
                }
              }
              else {
                if (preg_match('/' . $term_title . '/', $node->body)) {
                  $node->content['glossify_hovertip'][$term_title] = array(
                    '#value' => theme('glossify_term', $path, 'hovertip'),
                    '#weight' => 10,
                  );
                  $node->content['glossify_hovertip']['#weight'] = 10;
                }
              }
            }
          }
        }
      }
      if ($term_definition_list !== '') {
        $node->content['glossify_reference'] = array(
          '#weight' => '10',
          '#value' => theme('glossify_reference_section', $term_definition_list),
        );
      }
      break;
  }
}

/**
 * Implementation of hook_filter().
 */
function glossify_filter($op, $delta = 0, $format = -1, $text = '', $cache_id = 0) {
  switch ($op) {
    case 'list':
      return array(
        0 => t('Glossify filter'),
      );
    case 'description':
      return t('Glossify the entered content.');
    case 'prepare':
      return $text;
    case 'process':
      if (($node = menu_get_object()) == NULL) {
        $q = db_query("SELECT nid FROM {node_revisions} WHERE '%s'=CONCAT(format, ':', MD5(body))", $cache_id);
        $r = db_fetch_object($q);
        $node = node_load($r->nid);
      }
      $configurations = variable_get('glossify_configurations', array());
      $html_body = str_get_html($text);
      foreach ($configurations as $config_name => $configuration) {
        if (in_array($node->type, $configuration['from'])) {
          foreach (_fetch_possible_keywords($configuration, $node->nid) as $term_title => $target_url) {
            foreach ($configuration['style'] as $style => $enabled) {
              if ($enabled) {
                if (isset($old_body) && $old_body !== $html_body->innertext) {
                  $html_body = str_get_html($html_body->innertext);
                }
                $replacement = _fetch_replacement($style, $term_title, $target_url);
                $old_body = $html_body->innertext;
                $replaced = 0;
                _glossify_replace($configuration, $html_body, $term_title, $replacement, $replaced);
              }
            }
          }
        }
      }
      return $html_body->innertext;
    default:
      return $text;
  }
}

/**
 * Helper function that fetches and returns the styled term depending on the style and term.
 */
function _glossify_replace($configuration, &$html, $term_title, $replacement, &$replaced) {
  if (count($html
    ->childNodes()) > 0) {
    foreach ($html
      ->childNodes() as $child_node) {
      _glossify_replace($configuration, $child_node, $term_title, $replacement, $replaced);
    }
  }
  else {
    if (isset($html->tag) && $html->tag !== 'root') {
      if ($configuration['only_first'] && $replaced == 0) {
        if ($configuration['break']) {
          $html->innertext = preg_replace('/\\b' . $term_title . '\\b/', $replacement, $html->innertext, $configuration['only_first'] ? 1 : -1, $temp_replaced);
        }
        else {
          $html->innertext = preg_replace('/' . $term_title . '/', $replacement, $html->innertext, $configuration['only_first'] ? 1 : -1, $temp_replaced);
        }
        $replaced = $temp_replaced;
      }
    }
  }
}

/**
 * Helper function that fetches and returns the styled term depending on the style and term.
 */
function _fetch_replacement($style, $term_title, $target_url) {
  switch ($style) {
    case 'links':
      return $replacement = l($term_title, $target_url, array(
        'attributes' => array(
          'title' => $term_title,
          'class' => 'glossify_term',
        ),
      ));
    case 'hovertip':
      return $replacement = '<span class="glossify_term hovertip_target" hovertip="' . $term_title . '">' . $term_title . '</span>';
    case 'reference':
      return $replacement = '<span class="glossify_term">' . $term_title . '</span>';
  }
}

/**
 * Helper function that fetches and returns an array of all keywords of a node, or just the ones for a specific method.
 */
function _fetch_keywords($nid, $method = FALSE) {
  $q = db_query("SELECT term, method FROM {glossify} WHERE nid = %d ORDER BY term", $nid);
  $glossary = array();
  while ($r = db_fetch_array($q)) {
    if ($method) {
      if ($method == $r['method']) {
        $glossary[] = $r['term'];
      }
    }
    else {
      $glossary[$r['method']][] = $r['term'];
    }
  }
  return $glossary;
}

/**
 * Helper function that performs operations on the Glossify keyword-table and returns the new, old or removed values, depending on the operation.
 */
function _keyword_table($operation, $nid, $method, $language = '', $term = '', $alternate = '') {
  switch ($operation) {
    case 'insert':
      $gnid = db_result(db_query("SELECT nid FROM {glossify} WHERE term = '%s' AND language = '%s'", $term, $language));
      $new_terms = array();
      if (!$gnid) {
        db_query("INSERT INTO {glossify} (nid, term, language, method, alternate) VALUES (%d, '%s', '%s', '%s', '%s')", $nid, $term, $language, $method, $alternate);
        $new_terms[$term] = $term;
      }
      else {
        $node = node_load($gnid);
        drupal_set_message(t("Keyword: '%term' already exists for <a href=\"/node/{$node->nid}\">%title</a>", array(
          '%term' => $term,
          '%title' => $node->title,
        )), 'warning');
      }
      return $new_terms;
      break;
    case 'update':
      $old_term = db_result(db_query("SELECT term FROM {glossify} WHERE nid = %d AND method = '%s'", $nid, 'title'));
      db_query("UPDATE {glossify} SET term = '%s', language = '%s', method = '%s' WHERE nid = %d AND method = '%s'", $term, $language, $method, $nid, 'title');
      return array(
        $old_term => $old_term,
      );
      break;
    case 'delete':
      $old_terms = array();
      if (!empty($term)) {
        $old_terms[$term] = $term;
        db_query("DELETE FROM {glossify} WHERE nid = %d AND method = '%s' AND language = '%s' AND term = '%s'", $nid, $method, $language, $term);
      }
      else {
        $q = db_query("SELECT term FROM {glossify} WHERE nid = %d AND method = '%s' ORDER BY term", $nid, $method);
        while ($r = db_fetch_array($q)) {
          $old_terms[$r['term']] = $r['term'];
        }
        db_query("DELETE FROM {glossify} WHERE nid = %d AND method = '%s'", $nid, $method);
      }
      return $old_terms;
      break;
  }
  return TRUE;
}

/**
 * Helper function that fetches and returns an array of all new and old keywords of a node, depending on the method.
 */
function _fetch_affected_keywords($node, $method, $keywordsource = NULL, $override = '') {
  $keywords = array();
  $existing_keywords = _fetch_keywords($node->nid, $method);
  switch ($method) {
    case 'internal':
      $form_keywords = !empty($node->glossify_keywords) ? array_map('trim', explode(',', $node->glossify_keywords)) : array();
      $form_override = $node->glossify_override;
      break;
    case 'cck':
      if (isset($node->{$keywordsource}[0]['value']) && isset($node->{$override}[0]['value'])) {
        $form_keywords = !empty($node->{$keywordsource}[0]['value']) ? array_map('trim', explode(',', $node->{$keywordsource}[0]['value'])) : array();
        $form_override = $node->{$override}[0]['value'];
      }
      else {
        drupal_set_message(t("This content type does not have the selected CCK Fields attached to it!<br />You need to either change the configuration or attach the selected CCK Fields to this content type."), 'warning');
        return array();
      }
      break;
    case 'taxonomy':
      if (isset($node->taxonomy['tags'][$keywordsource])) {
        $form_keywords = !empty($node->taxonomy['tags'][$keywordsource]) ? array_map('trim', explode(',', $node->taxonomy['tags'][$keywordsource])) : array();
        $form_override = $override;
      }
      else {
        drupal_set_message(t("This content type does not have the selected Vocabulary attached to it!<br />You need to either change the configuration or attach the selected Vocabulary to this content type."), 'warning');
        return array();
      }
      break;
  }
  if (count($form_keywords) > 0) {
    if (count($existing_keywords)) {
      $ikwds1 = array_diff($existing_keywords, $form_keywords);
      $ikwds2 = array_diff($form_keywords, $existing_keywords);
      if (count($ikwds1) !== count($ikwds2) || count($ikwds1) !== 0 && count($ikwds2) !== 0) {
        $ikwds3 = array_intersect($ikwds1, $ikwds2);
        if (count($ikwds3) == 0) {
          foreach ($ikwds1 as $keyword) {
            $keywords = array_merge($keywords, _keyword_table('delete', $node->nid, $method, $node->language, $keyword));
          }
          foreach ($ikwds2 as $keyword) {
            $keywords = array_merge($keywords, _keyword_table('insert', $node->nid, $method, $node->language, $keyword, $form_override));
          }
        }
      }
    }
    else {
      foreach ($form_keywords as $keyword) {
        $keywords = array_merge($keywords, _keyword_table('insert', $node->nid, $method, $node->language, $keyword, $form_override));
      }
    }
  }
  else {
    if (count($existing_keywords) > 0) {
      $keywords = array_merge($keywords, _keyword_table('delete', $node->nid, $method));
    }
  }
  return $keywords;
}

/**
 * Helper function that fetches and returns an array of all the nodes that contain the keywords.
 */
function _fetch_affected_nodes($keywords, $content_types) {
  $nodes = array();
  foreach ($keywords as $keyword) {
    $q = db_query("SELECT n.nid FROM {node} n INNER JOIN {node_revisions} r ON r.nid = n.nid WHERE n.type IN (" . db_placeholders($content_types, 'varchar') . ") AND r.body LIKE '%%%s%%'", $content_types, $keyword);
    while ($r = db_fetch_array($q)) {
      $nodes[] = $r['nid'];
    }
  }
  return $nodes;
}

/**
 * Helper function the possible keywords for a given configuration and nid.
 */
function _fetch_possible_keywords($configuration, $nid) {
  $glossify_dict = array();
  $methods = array(
    "'title'",
  );
  if ($configuration['methods']['use_internal']) {
    $methods[] = "'internal'";
  }
  if ($configuration['methods']['use_cck']) {
    $methods[] = "'cck'";
  }
  if ($configuration['methods']['use_taxonomy']) {
    $methods[] = "'taxonomy'";
  }
  if ($configuration['language']) {
    $q = db_query("SELECT g.term, g.nid, g.alternate, g.language FROM {node} n INNER JOIN {glossify} g ON g.nid = n.nid WHERE g.language = n.language AND n.type IN (" . db_placeholders($configuration['to'], 'varchar') . ") AND g.nid != %d AND g.method IN (" . implode(', ', $methods) . ")", $configuration['to'], $nid);
  }
  else {
    $q = db_query("SELECT g.term, g.nid, g.alternate, g.language FROM {node} n INNER JOIN {glossify} g ON g.nid = n.nid WHERE n.type IN (" . db_placeholders($configuration['to'], 'varchar') . ") AND g.nid != %d AND g.method IN (" . implode(', ', $methods) . ")", $configuration['to'], $nid);
  }
  while ($r = db_fetch_array($q)) {
    if (!empty($r['alternate'])) {
      if (is_numeric($r['alternate'])) {
        $glossify_dict[$r['term']] = drupal_lookup_path('alias', 'node/' . $r['alternate'], $r['language']);
      }
      elseif ($path = drupal_lookup_path('alias', $r['alternate'], $r['language'])) {
        $glossify_dict[$r['term']] = $path;
      }
      else {
        $glossify_dict[$r['term']] = $r['alternate'];
      }
    }
    else {
      $glossify_dict[$r['term']] = drupal_lookup_path('alias', 'node/' . $r['nid'], $r['language']);
    }
  }
  return $glossify_dict;
}

/**
 * Helper function that cleans up old variables and inserts old cck values into the keyword table.
 */
function _backwards_compatibility() {
  $keyword_field = variable_get('glossify_use_this_cck_field_for_keyword_synonyms', FALSE);
  $override_field = variable_get('glossify_use_this_cck_field_for_target_url_override', FALSE);
  if ($keyword_field) {
    $content_types = variable_get('glossify_glossary_content_type', array());
    foreach ($content_types as $content_type) {
      $table = 'content_type_' . $content_type;
      $q = db_query("SELECT nid FROM {$table} WHERE 1");
      while ($r = db_fetch_array($q)) {
        $node = node_load($r['nid'], NULL, TRUE);
        $keyword = $node->{$keyword_field};
        if ($override_field == 'none') {
          _keyword_table('insert', $node->nid, 'cck', $node->language, $keyword[0]['value'], '');
        }
        else {
          $override = $node->{$override_field};
          _keyword_table('insert', $node->nid, 'cck', $node->language, $keyword[0]['value'], $override[0]['value']);
        }
        _keyword_table('insert', $node->nid, 'title', $node->language, $node->title, '');
      }
    }
  }
  variable_del('glossify_glossary_content_type');
  variable_del('glossify_content_types_to_search');
  variable_del('glossify_link_first_only');
  variable_del('glossify_do_we_need_unicode_compatibility');
  variable_del('glossify_teaser');
  variable_del('glossify_style');
  variable_del('glossify_dont_break_words');
  variable_del('glossify_display_parsing_time_for_performance_debugging');
  variable_del('glossify_use_this_cck_field_for_keyword_synonyms');
  variable_del('glossify_use_this_cck_field_for_target_url_override');
}

Functions

Namesort descending Description
glossify_filter Implementation of hook_filter().
glossify_form_alter Implementation of hook_form_alter().
glossify_menu Implementation of hook_menu().
glossify_nodeapi Implementation of hook_nodeapi().
glossify_perm Implementation of hook_perm().
glossify_theme Implementation of hook_theme().
theme_glossify_reference_section Render a glossary term reference.
theme_glossify_term Render a glossary term.
_backwards_compatibility Helper function that cleans up old variables and inserts old cck values into the keyword table.
_fetch_affected_keywords Helper function that fetches and returns an array of all new and old keywords of a node, depending on the method.
_fetch_affected_nodes Helper function that fetches and returns an array of all the nodes that contain the keywords.
_fetch_keywords Helper function that fetches and returns an array of all keywords of a node, or just the ones for a specific method.
_fetch_possible_keywords Helper function the possible keywords for a given configuration and nid.
_fetch_replacement Helper function that fetches and returns the styled term depending on the style and term.
_glossify_replace Helper function that fetches and returns the styled term depending on the style and term.
_keyword_table Helper function that performs operations on the Glossify keyword-table and returns the new, old or removed values, depending on the operation.