You are here

similarterms.module in Similar By Terms 6

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_perm() {
  return array(
    'access similarterms content',
    'administer similarterms content',
  );
}

// function similarterms_perm()

/**
 * Implementation of hook_block().
 */
function similarterms_block($op = 'list', $delta = 0, $edit = array()) {
  $block = $form = array();
  switch ($op) {
    case 'list':
      return similarterms_block_list();
    case 'view':
      return similarterms_block_view($delta);
    case 'configure':
      return similarterms_block_configure($delta);
    case 'save':
      similarterms_block_save($delta, $edit);
      return;
  }
}

/**
 * Perform the "list" op for hook_block().
 *
 * @return
 *   Array of block definition.
 */
function similarterms_block_list() {
  $blocks[0]['info'] = t('Similar entries from ANY vocabulary.');
  $blocks[0]['cache'] = BLOCK_CACHE_PER_PAGE;
  if (variable_get("similarterms_vocabularies", 'multi_select_and_tags') == 'all') {
    foreach (taxonomy_get_vocabularies() as $v) {
      $blocks[$v->vid]['info'] = t('Similar entries from the @vocab vocabulary.', array(
        '@vocab' => $v->name,
      ));
      $blocks[$v->vid]['cache'] = BLOCK_CACHE_PER_PAGE;
    }
  }
  else {
    foreach (taxonomy_get_vocabularies() as $v) {

      // this only makes sense for multi-select vocabularies and free tagging
      if ($v->multiple || $v->tags) {
        $blocks[$v->vid]['info'] = t('Similar entries from the @vocab vocabulary.', array(
          '@vocab' => $v->name,
        ));
        $blocks[$v->vid]['cache'] = BLOCK_CACHE_PER_PAGE;
      }
    }
  }
  return $blocks;
}

/**
 * Perform the "view" op for hook_block().
 *
 * @param $delta
 *   String specifying which block to proocess.
 *
 * @return
 *   Array of block contents and title.
 */
function similarterms_block_view($delta) {
  if ($delta == 0) {
    $block['subject'] = t('Similar');
    $block['content'] = theme('similarterms', variable_get('similarterms_display_options', 'title_only'), similarterms_list());
  }
  else {
    $block['subject'] = t('Similar');
    $block['content'] = theme('similarterms', variable_get('similarterms_display_options', 'title_only'), similarterms_list($delta));
  }
  return $block;
}

/**
 * Perform the "configure" op for hook_block().
 *
 * @param $delta
 *   String specifying which block to proocess.
 *
 * @return
 *   Settings form array.
 */
function similarterms_block_configure($delta = 0) {
  $form['count'] = array(
    '#type' => 'textfield',
    '#title' => t('Item count'),
    '#default_value' => variable_get('simterms_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_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('simterms_sametype_' . $delta, FALSE),
    '#options' => $types,
    '#description' => t('Limit to pages of this or these content type(s)'),
    '#multiple' => TRUE,
  );
  if ($delta > 0) {
    $terms = array();
    $tree = taxonomy_get_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('simterms_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 = 0, $edit = array()) {
  variable_set('simterms_count_' . $delta, $edit['count']);
  variable_set('simterms_sametype_' . $delta, $edit['sametype']);
  variable_set('simterms_ignoreterms_' . $delta, $edit['ignoreterms']);
  variable_set('similarterms_showcurrentnode_' . $delta, $edit['showcurrentnode']);
}

/**
 * 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 = 0, $nid = NULL) {
  $lists = array();
  $nodes = array();
  $args = array();
  $list_num = 0;
  $sql = "";
  $cache_lifetime = variable_get('similarterms_cache_options', 3600);
  $count = variable_get('simterms_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 == 0) {
      $terms = array_keys(taxonomy_node_get_terms($node_obj));
    }
    else {
      $terms = array_keys(taxonomy_node_get_terms_by_vocabulary($node_obj, $vocid));
    }

    // Filter out some terms
    $terms_filter = variable_get('simterms_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();
      $sql = 'SELECT n.nid, n.title, COUNT(n.nid) AS ncount ';
      $sql .= 'FROM {node} n ';
      $sql .= 'INNER JOIN {term_node} tn ON n.vid = tn.vid ';
      $sql .= 'WHERE tn.tid IN (';
      $number_of_terms = count($terms);
      foreach ($terms as $terms_items) {
        $number_of_terms--;
        if ($number_of_terms) {
          $sql .= "'%s',";
        }
        else {
          $sql .= "'%s'";
        }
        $args[] = $terms_items;
      }
      $sql .= ') ';
      $types = variable_get('simterms_sametype_' . $vocid, FALSE);
      if ($types !== FALSE && is_array($types) && count($types) > 0 && $types['0'] == NULL) {
        if ($types[1]) {
          $node_obj = node_load($nid);
          $types[1] = $node_obj->type;
        }
        $sql .= 'AND n.type IN (';
        $number_of_types = count($types);
        foreach ($types as $types_items) {
          $number_of_types--;
          if ($number_of_types) {
            $sql .= "'%s',";
          }
          else {
            $sql .= "'%s'";
          }
          $args[] = $types_items;
        }
        $sql .= ') ';
      }

      //if showcurrentnode option is false (default state), create filter for query.
      if (!variable_get('similarterms_showcurrentnode_' . $vocid, FALSE)) {
        $sql .= 'AND n.nid != %d ';
        $args[] = $nid;
      }
      $sql .= 'AND n.status = 1 ';
      $sql .= 'AND n.moderate = 0 ';
      $sql .= "AND (n.language = '' OR n.language = '%s') ";
      $args[] = $node_obj->language;
      $sql .= 'GROUP BY n.nid, n.title, n.created ';
      if (variable_get('similarterms_ncount_options', 'default') == 'default') {
        $sql .= 'ORDER BY ncount DESC, ';
      }
      else {
        $sql .= 'ORDER BY ncount ASC, ';
      }
      $sql .= 'n.created DESC ';
      $sql .= 'LIMIT %d';
      $args[] = $count;
      $sql = db_rewrite_sql($sql);
      $result = db_query($sql, $args);

      //      watchdog('similarterms',  $sql, NULL, WATCHDOG_INFO);
      while ($r = db_fetch_object($result)) {
        $nodes[] = node_load($r->nid);
      }

      // 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', time() + $cache_lifetime);
      }
    }
  }
  return $nodes;
}

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

/**
 * Implementation of hook_form_alter().
 */
function similarterms_form_alter(&$form, $form_state, $form_id) {
  if (isset($form['type']) && $form['type']['#value'] . '_node_form' == $form_id) {
    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('simterms_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' => $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('simterms_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;
}

/**
 * Implementation of hook_nodeapi().
 */
function similarterms_nodeapi($node, $op, $arg = 0) {
  switch ($op) {
    case 'delete':
      similarterms_node_delete($node);
      break;
    case 'insert':
    case 'update':
      similarterms_node_save($node);
      break;
    case 'validate':
      similarterms_node_validate($node);
      break;
  }
}

/**
 * Function to delete entries from overrides table when a node is deleted
 */
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);
}

/**
 * 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);
      }
    }
  }
}

/**
 * Function to validate entries into the overrides table
 */
function similarterms_node_validate($node) {
  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;
}

/**
 * Implementation of hook_menu().
 */
function similarterms_menu() {

  // Admin settings for the site.
  $items['admin/settings/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;
}

/**
 * Theme function for similar block
 *
 */
function similarterms_theme() {
  return array(
    'similarterms' => array(
      'template' => 'similarterms',
      'arguments' => array(
        'display_options' => NULL,
        'items' => NULL,
      ),
    ),
  );
}

Functions

Namesort descending Description
similarterms_block Implementation of hook_block().
similarterms_block_configure Perform the "configure" op for hook_block().
similarterms_block_list Perform the "list" op for hook_block().
similarterms_block_save Perform the "save" op for hook_block().
similarterms_block_view Perform the "view" op for hook_block().
similarterms_flush_caches Implementation of hook_flush_caches().
similarterms_form_alter Implementation of 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 Implementation of hook_menu().
similarterms_nodeapi Implementation of hook_nodeapi().
similarterms_node_delete Function to delete entries from overrides table when a node is deleted
similarterms_node_save Function to populate overrides table
similarterms_node_validate Function to validate entries into the overrides table
similarterms_perm Valid permissions for this module
similarterms_taxonomy_get_vocabularies This function gets taxonomy vocabularies and add in 0 for "all vocabs";
similarterms_theme Theme function for similar block