You are here

similarterms.module in Similar By Terms 7

Similar By Terms module displays a block with similar content based on taxonomy terms.

File

similarterms.module
View source
<?php

// by Jeff Robbins

/**
 * @file
 * Similar By Terms module displays a block with similar content
 * based on taxonomy terms.
 */

/**
 * Display help and module information
 * @param path which path of the site we're displaying help
 * @param arg array that holds the current path as would be returned from arg() function
 * @return help text for the path
 */
function similarterms_help($path, $arg) {
  $output = '';
  switch ($path) {
    case "admin/help#similarterms":
      $output = '<p>' . t("Displays a block with similar content based on taxonomy terms.") . '</p>';
      break;
  }
  return $output;
}

// function similarterms_help

/**
 * Valid permissions for this module
 * @return array An array of valid permissions for the similarterms module
 */
function similarterms_permission() {
  return array(
    'access similarterms content' => array(
      'title' => t('access similarterms content'),
      'description' => t('Allows role the abilty to see similar by terms content'),
    ),
    'administer similarterms content' => array(
      'title' => t('administer similarterms content'),
      'description' => t('Allows role the abilty to see administer similar by terms settings'),
    ),
  );
}

// function similarterms_perm()

/**
 * Implements hook_block_info().
 */
function similarterms_block_info() {
  $blocks['all'] = array(
    'info' => t('Similar entries from ANY vocabulary.'),
    'cache' => DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE,
  );
  foreach (taxonomy_get_vocabularies() as $v) {
    $blocks[$v->vid] = array(
      'info' => t('Similar entries from the @vocab vocabulary.', array(
        '@vocab' => $v->name,
      )),
      'cache' => DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE,
    );
  }
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function similarterms_block_view($delta = '') {
  $vocid = $delta ? $delta : 'all';
  $block = array(
    'subject' => t('Similar'),
    'content' => theme('similarterms', array(
      'display_options' => variable_get('similarterms_display_options', 'title_only'),
      'nodes' => similarterms_list($vocid),
    )),
  );
  return $block;
}

/**
 * Implements hook_block_configure().
 */
function similarterms_block_configure($delta = '') {
  $form['count'] = array(
    '#type' => 'textfield',
    '#title' => t('Item count'),
    '#default_value' => variable_get('similarterms_count_' . $delta, 5),
    '#size' => 3,
    '#description' => t('The maximum number of similar items to display'),
  );

  //petertj addition to configuration to permit display of current node in list
  $form['showcurrentnode'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show current node as active in the list'),
    '#default_value' => variable_get('similarterms_showcurrentnode_' . $delta, FALSE),
    '#required' => FALSE,
  );

  //mimo addition to configuration to limit to same page type
  $types = array(
    '0' => t('<none>'),
    '1' => t('same as current'),
  );
  $arr_types_obj = node_type_get_types();
  foreach ($arr_types_obj as $type => $obj) {
    $types[$type] = $obj->name;
  }
  $form['sametype'] = array(
    '#type' => 'select',
    '#title' => t('Content type limit'),
    '#default_value' => variable_get('similarterms_sametype_' . $delta, FALSE),
    '#options' => $types,
    '#description' => t('Limit to pages of this or these content type(s)'),
    '#multiple' => TRUE,
  );
  if ($delta != 'all') {
    $terms = array();
    $tree = $delta;
    foreach ($tree as $term) {
      $terms[$term->tid] = $term->name;
    }
    $form['ignoreterms'] = array(
      '#type' => 'select',
      '#title' => t('Terms to be ignored'),
      '#default_value' => variable_get('similarterms_ignoreterms_' . $delta, array()),
      '#options' => $terms,
      '#description' => t('Ignore selected terms here from being used to search for similar entries'),
      '#multiple' => TRUE,
    );
  }
  return $form;
}

/**
 * Perform the "save" op for hook_block().
 *
 * @param $delta
 *   String specifying which block to proocess.
 * @param $edit
 *   Array containg the form input.
 *
 * @return
 *   None. Values are saved as system variables.
 */
function similarterms_block_save($delta = '', $edit = array()) {
  variable_set('similarterms_count_' . $delta, $edit['count']);
  variable_set('similarterms_sametype_' . $delta, $edit['sametype']);
  variable_set('similarterms_showcurrentnode_' . $delta, $edit['showcurrentnode']);

  //Only conditionally shown in the edit form, so not always set.
  if (isset($edit['ignoreterms'])) {
    variable_set('similarterms_ignoreterms_' . $delta, $edit['ignoreterms']);
  }
}

/**
 * Output the block
 *
 * @param $vocid
 *   integer - vocabulary id, leave out to use ALL terms for this node
 * @param $nid
 *   integer - nid, leave out to use the current node
 * @return
 *   an array of node objects
 */
function similarterms_list($vocid = 'all', $nid = NULL) {
  $lists = array();
  $nodes = array();
  $args = array();
  $list_num = 0;
  $sql = "";
  $cache_lifetime = variable_get('similarterms_cache_options', 3600);
  $count = variable_get('similarterms_count_' . $vocid, 5);
  if (arg(0) == 'node' && is_numeric(arg(1)) && !$nid) {
    $nid = arg(1);
  }
  if ($nid != NULL) {
    $cid = "{$vocid}:{$nid}";
    if ($cache_lifetime) {
      if ($cached = cache_get($cid, 'cache_similarterms')) {
        return $cached->data;
      }
    }
    if (variable_get('similarterms_override_options', 0)) {
      $lists = similarterms_get_overrides($nid, $vocid);
      if ($lists[$vocid]) {
        foreach ($lists[$vocid] as $nid_list) {
          if (is_numeric($nid_list)) {
            if ($list_num <= $count) {
              $list_num = $list_num + 1;
              $nodes[] = node_load($nid_list);
            }
          }
        }
      }
    }
    $node_obj = node_load($nid);
    if ($vocid == 'all') {
      $terms = array_keys(similarterms_taxonomy_node_get_terms($node_obj));
    }
    else {
      $terms = array_keys(similarterms_taxonomy_node_get_terms($node_obj, $vocid));
    }

    // Filter out some terms
    $terms_filter = variable_get('similarterms_ignoreterms_' . $vocid, array());
    foreach ($terms_filter as $v) {
      $idx = array_search($v, $terms);
      if ($idx >= 0) {
        unset($terms[$idx]);
      }
    }
    if (!empty($terms)) {

      //past events
      $pasts = array();
      $query = db_select('node', 'n');
      $query
        ->addField('n', 'nid');
      $query
        ->addExpression('COUNT(n.nid)', 'ncount');
      $query
        ->innerJoin('taxonomy_index', 'ti', 'n.nid = ti.nid');
      $query
        ->condition('ti.tid', $terms, 'IN');
      $types = variable_get('similarterms_sametype_' . $vocid, FALSE);
      if ($types !== FALSE && is_array($types) && count($types) > 0 && !isset($types['0'])) {
        if (isset($types['1'])) {
          unset($types['1']);
          $types[$node_obj->type] = $node_obj->type;
        }
        $query
          ->condition('n.type', $types, 'IN');
      }

      //if showcurrentnode option is false (default state), create filter for query.
      if (!variable_get('similarterms_showcurrentnode_' . $vocid, FALSE)) {
        $query
          ->condition('n.nid', $nid, '<>');
      }
      $query
        ->condition('n.status', 1)
        ->condition(db_or()
        ->condition('n.language', 'und')
        ->condition('n.language', $node_obj->language))
        ->groupBy('n.nid')
        ->groupBy('n.title')
        ->groupBy('n.created');
      if (variable_get('similarterms_ncount_options', 'default') == 'default') {
        $query
          ->orderBy('ncount', 'DESC');
      }
      else {
        $query
          ->orderBy('ncount', 'ASC');
      }
      $query
        ->orderBy('n.created', 'DESC');
      $query
        ->range(0, $count);
      $result = $query
        ->execute();
      $nids = array();
      foreach ($result as $record) {
        $nids[] = $record->nid;
      }
      $nodes = node_load_multiple($nids);

      // Allow modules to alter the list of nodes by implementing a hook.
      // Design pattern from comment_invoke_comment().
      foreach (module_implements('similarterms') as $name) {
        $function = $name . '_similarterms';
        $function($nodes, $node_obj);
      }
      if ($cache_lifetime) {
        cache_set($cid, $nodes, 'cache_similarterms', REQUEST_TIME + $cache_lifetime);
      }
    }
  }
  return $nodes;
}

/**
 * Implements hook_flush_caches().
 */
function similarterms_flush_caches() {
  return array(
    'cache_similarterms',
  );
}

/**
 * Implements hook_form_alter().
 */
function similarterms_form_alter(&$form, &$form_state, $form_id) {
  if (!empty($form['#node_edit_form'])) {
    if (!variable_get('similarterms_override_options', 0)) {
      return;
    }

    // no need to alter form if block module is off
    if (!module_exists('block')) {
      return;
    }
    $blocks = similarterms_get_active_block_vocabularies();

    // no need to alter form if no similarterms blocks are active
    if (sizeof($blocks) == 0) {
      return;
    }
    $overrides = array();
    if (is_numeric($form['nid']['#value'])) {
      $overrides = similarterms_get_overrides($form['nid']['#value']);
    }
    $form['similarterms'] = array(
      '#type' => 'fieldset',
      '#title' => t('Similar Terms'),
      '#description' => t('Override the links generated by similar terms module.'),
      '#collapsible' => 1,
      '#collapsed' => sizeof($overrides) ? 0 : 1,
    );
    $node_type = $form['type']['#value'];
    $no_similarterms = 1;
    foreach (similarterms_taxonomy_get_vocabularies() as $v) {
      if (!$blocks[$v->vid]) {
        continue;
      }
      $types = variable_get('similarterms_sametype_' . $v->vid, array(
        '0' => '0',
      ));
      if (!$types[$node_type] && !isset($types[0])) {
        continue;
      }
      $no_similarterms = 0;
      $form['similarterms']['similarterms_vid_' . $v->vid] = array(
        '#type' => 'fieldset',
        '#title' => check_plain($v->name),
        '#collapsible' => 1,
        '#collapsed' => sizeof($overrides[$v->vid]) ? 0 : 1,
        '#tree' => 1,
      );
      $form['similarterms']['similarterms_vid_' . $v->vid]['similarterms_paths'] = array(
        '#type' => 'fieldset',
        '#title' => t('Paths'),
        '#tree' => 1,
      );
      $count = variable_get('similarterms_count_' . $v->vid, 5);
      for ($i = 0; $i < $count; $i++) {
        $default_value = '';
        if ($overrides[$v->vid][$i]) {
          $default_value = drupal_lookup_path('alias', 'node/' . $overrides[$v->vid][$i]);
          if (!$default_value) {
            $default_value = 'node/' . $overrides[$v->vid][$i];
          }
        }
        $form['similarterms']['similarterms_vid_' . $v->vid]['similarterms_paths'][$i] = array(
          '#type' => 'textfield',
          '#title' => t('Path %d', array(
            '%d' => $i + 1,
          )),
          '#default_value' => $default_value ? $default_value : '',
        );
      }
      $form['similarterms']['similarterms_vid_' . $v->vid]['similarterms_override_delete'] = array(
        '#type' => 'checkbox',
        '#title' => t('Delete Similar Terms Overrides?'),
        '#description' => t('If this option is checked all paths will be deleted for this vocabulary.'),
        '#default_value' => 0,
      );
    }
    if ($no_similarterms) {
      unset($form['similarterms']);
    }
  }
}

/**
 * This function returns a list of overrides
 */
function similarterms_get_overrides($nid, $vid = NULL) {
  $paths = array();
  $query = "SELECT * FROM {similarterms_override} WHERE nid = '%s'";
  $args = array(
    $nid,
  );
  if ($vid) {
    $query .= " AND vid = %s";
    $args[] = $vid;
  }
  $result = db_query($query, $args);
  while ($object = db_fetch_object($result)) {
    $paths[$object->vid][] = $object->path;
  }
  return $paths;
}

/**
 * Implements hook_node_delete().
 */
function similarterms_node_delete($node, $vid = NULL) {
  if (!variable_get('similarterms_override_options', 0)) {
    return;
  }
  $query = "DELETE FROM {similarterms_override} WHERE nid = %d";
  $args = array(
    $node->nid,
  );
  if ($vid) {
    $query .= " AND vid = %d";
    $args[] = $vid;
  }
  db_query($query, $args);
}

/**
 * Implements hook_node_update().
 */
function similarterms_node_update($node) {
  similarterms_node_save($node);
}

/**
 * Implements hook_node_insert().
 */
function similarterms_node_insert($node) {
  similarterms_node_save($node);
}

/**
 * Function to populate overrides table
 */
function similarterms_node_save($node) {
  if (!variable_get('similarterms_override_options', 0)) {
    return;
  }
  $result = array();
  $query = "INSERT INTO {similarterms_override} (nid, path, vid) VALUES(%d, %d, %d)";
  foreach (similarterms_taxonomy_get_vocabularies() as $v) {
    $vid = 'similarterms_vid_' . $v->vid;
    $alias =& $node->{$vid};
    similarterms_node_delete($node, $v->vid);
    if ($alias['similarterms_override_delete']) {
      continue;
    }
    foreach ($alias['similarterms_paths'] as $id => $path) {
      $pieces = explode('/', $path);
      if (sizeof($pieces) == 2 && $pieces[0] == "node" && is_numeric($pieces[1])) {
        $nid = $pieces[1];
      }
      else {
        $path = drupal_lookup_path('source', $path);
        $pieces = explode('/', $path);
        $nid = $pieces[1];
      }
      if ($nid) {
        $args = array(
          $node->nid,
          $nid,
          $v->vid,
        );
        $result[$id] = db_query($query, $args);
      }
    }
  }
}

/**
 * Implements hook_node_validate().
 */
function similarterms_node_validate($node, $form) {
  if (!variable_get('similarterms_override_options', 0)) {
    return;
  }
  foreach (similarterms_taxonomy_get_vocabularies() as $v) {
    $vid = 'similarterms_vid_' . $v->vid;
    $alias =& $node->{$vid};

    // Make sure that the paths are valid
    foreach ($alias['similarterms_paths'] as $id => $path) {
      $pieces = explode('/', $path);
      if (sizeof($pieces) == 2 && $pieces[0] == "node" && is_numeric($pieces[1])) {

        // If there's a better way to check if a node exists, replace this code
        $thisnode = node_load($pieces[1]);
        if (!$thisnode->nid) {
          form_set_error("{$vid}][similarterms_paths][{$id}", t('%path is not a valid path', array(
            '%path' => $path,
          )));
        }
      }
      elseif ($path == '') {
        continue;
      }
      elseif (!drupal_lookup_path('source', $path)) {
        form_set_error("{$vid}][similarterms_paths][{$id}", t('%vocab %path is not a valid path', array(
          '%path' => $path,
          '%vocab' => $v->name,
        )));
      }
    }
  }
}

/**
 * This function returns a list of all blocks for similarterms
 *
 * Note: I could not find a public function in the block module to do this
 * so I wrote this function based on _block_rehash(). If there's a better
 * way to do this, this code should be refactored
 */
function similarterms_get_active_block_vocabularies() {

  // TODO: add in code to check for blockcache module
  global $theme_key;
  init_theme();
  $result = db_query("SELECT * FROM {blocks} WHERE theme = '%s' AND module = '%s'", $theme_key, 'similarterms');
  while ($object = db_fetch_object($result)) {
    if ($object->status) {
      $blocks[$object->delta] = 1;
    }
  }
  return $blocks;
}

/**
 * This function gets taxonomy vocabularies and add in 0 for "all vocabs";
 */
function similarterms_taxonomy_get_vocabularies() {
  $object = taxonomy_get_vocabularies();
  $object[0]->vid = 0;
  $object[0]->name = t('Any Vocabulary');
  return $object;
}

/**
 * This function gets all taxonomy vocabularies for a certain node with option limits for Vocabularies
 */
function similarterms_taxonomy_node_get_terms($node, $vid = NULL) {
  $terms =& drupal_static(__FUNCTION__);
  $fields = field_info_fields();
  $story_vocabs = array();
  foreach ($fields as $field_name => $field) {
    if ($field['type'] == 'taxonomy_term_reference' && !empty($field['bundles']['node']) && in_array($node->type, $field['bundles']['node'])) {
      $node_fields = field_get_items('node', $node, $field['field_name']);
      foreach ($node_fields as $item) {
        if ($vid == NULL || $item['taxonomy_term']->vid == $vid) {
          $terms[$node->vid]['tid'][$item['tid']] = $item['taxonomy_term'];
        }
      }
    }
  }
  return $terms[$node->vid]['tid'];
}

/**
 * Implements hook_menui().
 */
function similarterms_menu() {

  // Admin settings for the site.
  $items['admin/config/content/similarterms'] = array(
    'title' => 'Similar By Terms',
    'description' => 'Basic Settings for similar term most settings are in the blocks config.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'similarterms_admin_settings',
    ),
    'file' => 'similarterms.admin.inc',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function similarterms_theme() {
  return array(
    'similarterms' => array(
      'template' => 'similarterms',
      'variables' => array(
        'display_options' => NULL,
        'nodes' => NULL,
      ),
    ),
  );
}

/**
 * Preprocess for similarterms.tpl.php.
 *
 * @todo: Needs to be converted to renderable array output.
 */
function template_preprocess_similarterms(&$variables) {
  $links = array();
  $nodes = $variables['nodes'];
  if ($nodes) {
    foreach ($nodes as $node) {
      if ($variables['display_options'] == 'teaser') {
        $teaser = ' - ' . $node->teaser;
      }
      else {
        $teaser = '';
      }
      $links[] = l($node->title, 'node/' . $node->nid) . $teaser;
    }
    $variables['items'] = theme('item_list', array(
      'items' => $links,
    ));
  }
  else {
    $variables['items'] = "";
  }
}

Functions

Namesort descending Description
similarterms_block_configure Implements hook_block_configure().
similarterms_block_info Implements hook_block_info().
similarterms_block_save Perform the "save" op for hook_block().
similarterms_block_view Implements hook_block_view().
similarterms_flush_caches Implements hook_flush_caches().
similarterms_form_alter Implements hook_form_alter().
similarterms_get_active_block_vocabularies This function returns a list of all blocks for similarterms
similarterms_get_overrides This function returns a list of overrides
similarterms_help Display help and module information
similarterms_list Output the block
similarterms_menu Implements hook_menui().
similarterms_node_delete Implements hook_node_delete().
similarterms_node_insert Implements hook_node_insert().
similarterms_node_save Function to populate overrides table
similarterms_node_update Implements hook_node_update().
similarterms_node_validate Implements hook_node_validate().
similarterms_permission Valid permissions for this module
similarterms_taxonomy_get_vocabularies This function gets taxonomy vocabularies and add in 0 for "all vocabs";
similarterms_taxonomy_node_get_terms This function gets all taxonomy vocabularies for a certain node with option limits for Vocabularies
similarterms_theme Implements hook_theme().
template_preprocess_similarterms Preprocess for similarterms.tpl.php.