similar.module in Similar Entries 6
Same filename and directory in other branches
Module that shows a block listing similar entries. NOTE: Uses MySQL's FULLTEXT indexing for MyISAM tables.
Caching feature sponsored by http://xomba.com/
@author David Kent Norman http://deekayen.net/ @author Arnab Nandi http://arnab.org/
File
similar.moduleView source
<?php
/**
* @file
* Module that shows a block listing similar entries.
* NOTE: Uses MySQL's FULLTEXT indexing for MyISAM tables.
*
* Caching feature sponsored by http://xomba.com/
*
* @author David Kent Norman http://deekayen.net/
* @author Arnab Nandi http://arnab.org/
*/
/**
* Implements hook_help().
*/
function similar_help($path, $arg) {
switch ($path) {
case 'admin/help#similar':
return t('<p>Lists the most similar nodes to the current node.</p>');
break;
}
}
/**
* Implements hook_block().
*/
function similar_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0]['info'] = t('Similar Entries');
$blocks[0]['cache'] = BLOCK_CACHE_PER_PAGE | BLOCK_CACHE_PER_ROLE;
return $blocks;
case 'configure':
$form = array();
if ($delta == 0) {
$form['similar_teaser_enabled'] = array(
'#type' => 'radios',
'#title' => t('Include teaser text'),
'#default_value' => variable_get('similar_teaser_enabled', 0),
'#options' => array(
t('No'),
t('Yes'),
),
);
$form['similar_rel_nofollow'] = array(
'#type' => 'radios',
'#title' => t('Block search engines'),
'#description' => t('Adds rel="nofollow" to the HTML source of similar links so search engines won\'t count similar links in their ranking calculations.'),
'#default_value' => variable_get('similar_rel_nofollow', 0),
'#options' => array(
t('No'),
t('Yes'),
),
);
for ($i = 1, $options = array(); $i < 101; $options[$i] = $i, $i += 1) {
}
$form['similar_num_display'] = array(
'#type' => 'select',
'#title' => t('Number of similar entries to find'),
'#default_value' => variable_get('similar_num_display', 5),
'#options' => $options,
);
$types = _similar_published_node_types();
$form['similar_node_types'] = array(
'#type' => 'checkboxes',
'#multiple' => TRUE,
'#title' => t('Node types to display'),
'#default_value' => variable_get('similar_node_types', $types),
'#options' => $types,
);
if (module_exists('taxonomy')) {
$names = _similar_taxonomy_names();
$form['similar_taxonomy'] = array(
'#type' => 'fieldset',
'#title' => t('Taxonomy category filter'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['similar_taxonomy']['similar_taxonomy_filter'] = array(
'#type' => 'radios',
'#title' => t('Filter by taxonomy categories'),
'#default_value' => variable_get('similar_taxonomy_filter', 0),
'#options' => array(
t('No category filtering'),
t('Only show the similar nodes in the same category as the original node'),
t('Use global category filtering'),
),
'#description' => t('By selecting global filtering, only nodes assigned to the following selected categories will display as similar nodes, regardless of the categories the original node is or is not assigned to.'),
);
$form['similar_taxonomy']['similar_taxonomy_select'] = array(
'#type' => 'fieldset',
'#title' => t('Taxonomy categories to display'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['similar_taxonomy']['similar_taxonomy_select']['similar_taxonomy_tids'] = array(
'#type' => 'select',
'#default_value' => variable_get('similar_taxonomy_tids', array_keys($names)),
'#description' => t('Hold the CTRL key to (de)select multiple options.'),
'#options' => $names,
'#multiple' => TRUE,
);
}
}
return $form;
case 'save':
if ($delta == 0) {
variable_set('similar_teaser_enabled', $edit['similar_teaser_enabled']);
variable_set('similar_rel_nofollow', $edit['similar_rel_nofollow']);
variable_set('similar_num_display', $edit['similar_num_display']);
variable_set('similar_node_types', $edit['similar_node_types']);
if (module_exists('taxonomy')) {
variable_set('similar_taxonomy_filter', $edit['similar_taxonomy_filter']);
variable_set('similar_taxonomy_tids', $edit['similar_taxonomy_tids']);
}
}
return;
case 'view':
default:
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) != 'edit') {
$node = node_load(array(
'nid' => arg(1),
));
}
else {
return;
}
$similar_node_types = variable_get('similar_node_types', _similar_published_node_types());
if ($node->nid > 0 && !empty($similar_node_types[$node->type])) {
unset($similar_node_types);
switch ($delta) {
case 0:
// The subject is displayed at the top of the block. Note that it should
// be passed through t() for translation.
$block['subject'] = t('Similar Entries');
$block['content'] = theme('similar_content', $node);
}
}
return empty($block['content']) ? '' : $block;
break;
}
}
/**
* Queries for published node types.
* @link http://drupal.org/node/33444 @endlink
*
* @return
* An array of node types that are available.
*/
function _similar_published_node_types() {
$types = array();
$result = db_query('SELECT DISTINCT(n.type) FROM {node} n WHERE n.status <> 0 ORDER BY n.type ASC');
while ($type = db_fetch_object($result)) {
$types[$type->type] = $type->type;
}
return $types;
}
/**
* Queries for taxonomy names.
* @link http://drupal.org/node/51041 @endlink
*
*@return
* An array of taxonomy term names to be used.
*/
function _similar_taxonomy_names() {
$names = array();
$result = db_query('SELECT d.tid, v.vid, v.name AS vocab_name, d.name AS data_name FROM {term_data} d, {vocabulary} v WHERE v.vid = d.vid ORDER BY v.name, d.name ASC');
while ($data = db_fetch_object($result)) {
$names[$data->tid] = $data->vocab_name . ': ' . $data->data_name;
}
return $names;
}
/**
* Queries for taxonomies to which a specific node belongs.
* @link http://drupal.org/node/51041 @endlink
*
* @param $nid
* A node ID.
* @return
* An array of terms related to the given node.
*/
function _similar_taxonomy_membership($nid) {
$tids = array();
$result = db_query('SELECT t.tid FROM {term_node} t WHERE t.nid = %d', $nid);
while ($data = db_fetch_object($result)) {
$tids[$data->tid] = $data->tid;
}
return $tids;
}
/**
* Strips characters from node type strings.
*/
function _similar_content_type_escape(&$item) {
$item = str_replace(array(
"\0",
"\n",
"\r",
"\\",
"'",
"\"",
"\32",
), '', $item);
}
/**
* Prevents SQL injection be forcing an integer value.
*/
function _similar_force_int(&$item) {
$item = (int) $item;
}
/**
* Implements hook_theme().
*/
function similar_theme() {
return array(
'similar_content' => array(
'arguments' => array(
'node' => NULL,
),
),
);
}
/**
* Queries the database for similar entries and puts them in a HTML list.
*
* @param $node
* The current node being viewed.
* @return
* A themed item list of related links.
*/
function theme_similar_content($node) {
$items = array();
$text = "{$node->title} {$node->body}";
$teaser = variable_get('similar_teaser_enabled', 0);
$types = _similar_published_node_types();
$types = variable_get('similar_node_types', $types);
array_walk($types, '_similar_content_type_escape');
if (sizeof($types) > 1) {
$types = implode("','", $types);
}
else {
list(, $types) = each($types);
}
$types = "'{$types}'";
if (module_exists('taxonomy') && (variable_get('similar_taxonomy_filter', 0) == 2 && ($taxonomy_tids = variable_get('similar_taxonomy_tids', array()))) || variable_get('similar_taxonomy_filter', 0) == 1 && ($taxonomy_tids = _similar_taxonomy_membership($node->nid))) {
array_walk($taxonomy_tids, '_similar_force_int');
if (sizeof($taxonomy_tids) > 1) {
$taxonomy_tids = implode(',', $taxonomy_tids);
}
else {
list(, $taxonomy_tids) = each($taxonomy_tids);
$taxonomy_tids = (int) $taxonomy_tids;
}
$query = "SELECT r.nid, MATCH(r.body, r.title) AGAINST ('%s') AS score FROM {node_revisions} r INNER JOIN {node} n ON r.nid = n.nid AND r.vid = n.vid INNER JOIN {term_node} t ON n.nid = t.nid AND t.tid IN (%s) WHERE n.status <> 0 AND r.nid <> %d AND n.type IN ({$types}) GROUP BY n.nid HAVING score > 0 ORDER BY score DESC, r.vid DESC";
}
else {
$query = "SELECT r.nid, MATCH(r.body, r.title) AGAINST ('%s') AS score FROM {node_revisions} r INNER JOIN {node} n ON r.nid = n.nid AND r.vid = n.vid WHERE n.status <> 0 AND r.nid <> %d AND n.type IN ({$types}) GROUP BY n.nid HAVING score > 0 ORDER BY score DESC, r.vid DESC";
}
$query = db_rewrite_sql($query, 'n', 'nid');
$result = db_query_range($query, strip_tags($text), $node->nid, 0, variable_get('similar_num_display', 5));
while ($node = db_fetch_object($result)) {
$content = node_load($node->nid);
$no_follow = variable_get('similar_rel_nofollow', 0) ? array(
'rel' => 'nofollow',
) : array();
if ($teaser) {
$items[] = '<div class="similar-title">' . l($content->title, 'node/' . $node->nid, array(
'attributes' => array(
'title' => $content->title,
) + $no_follow,
'absolute' => TRUE,
)) . '</div><div class="similar-teaser">' . check_markup($content->teaser, $content->format, FALSE) . '</div>';
}
else {
$items[] = l($content->title, 'node/' . $node->nid, array(
'attributes' => array(
'title' => $content->title,
) + $no_follow,
));
}
}
return sizeof($items) > 0 ? theme('item_list', $items) : '';
}
Functions
Name![]() |
Description |
---|---|
similar_block | Implements hook_block(). |
similar_help | Implements hook_help(). |
similar_theme | Implements hook_theme(). |
theme_similar_content | Queries the database for similar entries and puts them in a HTML list. |
_similar_content_type_escape | Strips characters from node type strings. |
_similar_force_int | Prevents SQL injection be forcing an integer value. |
_similar_published_node_types | Queries for published node types. http://drupal.org/node/33444 |
_similar_taxonomy_membership | Queries for taxonomies to which a specific node belongs. http://drupal.org/node/51041 |
_similar_taxonomy_names | Queries for taxonomy names. http://drupal.org/node/51041 |