/[drupal]/contributions/modules/similar/similar.module
ViewVC logotype

Diff of /contributions/modules/similar/similar.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.12, Fri May 8 15:00:45 2009 UTC revision 1.13, Fri May 8 21:10:26 2009 UTC
# Line 6  Line 6 
6   * Module that shows a block listing similar entries.   * Module that shows a block listing similar entries.
7   * NOTE: Uses MySQL's FULLTEXT indexing for MyISAM tables.   * NOTE: Uses MySQL's FULLTEXT indexing for MyISAM tables.
8   *   *
  * Caching feature sponsored by http://xomba.com/  
  *  
9   * @author David Kent Norman http://deekayen.net/   * @author David Kent Norman http://deekayen.net/
10   * @author Arnab Nandi http://arnab.org/   * @author Arnab Nandi http://arnab.org/
11   */   */
# Line 26  function similar_help($path, $arg) { Line 24  function similar_help($path, $arg) {
24  }  }
25    
26  /**  /**
27   * Implementation of hook_block().   * Implementation of hook_block_list().
28   *   */
29   * This hook both declares to Drupal what blocks are provided by the module, and  function similar_block_list() {
30   * generates the contents of the blocks themselves.    $blocks[0]['info'] = t('Similar entries');
31   *    $blocks[0]['cache'] = BLOCK_CACHE_PER_PAGE | BLOCK_CACHE_PER_ROLE;
32   * @param string $op    return $blocks;
33   * @param integer $delta  }
34   * @param array $edit  
35   */  /**
36  function similar_block($op = 'list', $delta = 0, $edit = array()) {   * Implementation of hook_block_configure().
37    switch ($op) {   */
38      case 'list':  function similar_block_configure($delta = 0) {
39        $blocks[0]['info'] = t('Similar entries');    $form = array();
40        $blocks[0]['cache'] = BLOCK_CACHE_PER_PAGE | BLOCK_CACHE_PER_ROLE;    if ($delta == 0) {
41        return $blocks;      $form['similar_teaser_enabled'] = array(
42          '#type' => 'radios',
43      case 'configure':        '#title' => t('Include teaser text'),
44        $form = array();        '#default_value' => variable_get('similar_teaser_enabled', 0),
45        if ($delta == 0) {        '#options' => array(t('No'), t('Yes'))
46          $form['similar_teaser_enabled'] = array(      );
47            '#type' => 'radios',      $form['similar_rel_nofollow'] = array(
48            '#title' => t('Include teaser text'),        '#type' => 'radios',
49            '#default_value' => variable_get('similar_teaser_enabled', 0),        '#title' => t('Block search engines'),
50            '#options' => array(t('No'), t('Yes'))        '#description' => t('Adds rel="nofollow" to the HTML source of similar links so search engines won\'t count similar links in their ranking calculations.'),
51          );        '#default_value' => variable_get('similar_rel_nofollow', 0),
52          $form['similar_rel_nofollow'] = array(        '#options' => array(t('No'), t('Yes'))
53            '#type' => 'radios',      );
54            '#title' => t('Block search engines'),      for ($i=1, $options=array(); $i < 101; $options[$i] = $i, $i+=1);
55            '#description' => t('Adds rel="nofollow" to the HTML source of similar links so search engines won\'t count similar links in their ranking calculations.'),      $form['similar_num_display'] = array(
56            '#default_value' => variable_get('similar_rel_nofollow', 0),        '#type' => 'select',
57            '#options' => array(t('No'), t('Yes'))        '#title' => t('Number of similar entries to find'),
58          );        '#default_value' => variable_get('similar_num_display', 5),
59          for ($i=1, $options=array(); $i < 101; $options[$i] = $i, $i+=1);        '#options' => $options
60          $form['similar_num_display'] = array(      );
61            '#type' => 'select',      $types = _similar_published_node_types();
62            '#title' => t('Number of similar entries to find'),      $form['similar_node_types'] = array(
63            '#default_value' => variable_get('similar_num_display', 5),        '#type' => 'checkboxes',
64            '#options' => $options        '#multiple' => TRUE,
65          );        '#title' => t('Node types to display'),
66          $types = _similar_published_node_types();        '#default_value' => variable_get('similar_node_types', $types),
67          $form['similar_node_types'] = array(        '#options' => $types
68            '#type' => 'checkboxes',      );
69            '#multiple' => TRUE,  
70            '#title' => t('Node types to display'),      if (module_exists('taxonomy')) {
71            '#default_value' => variable_get('similar_node_types', $types),        $names = _similar_taxonomy_names();
72            '#options' => $types        $form['similar_taxonomy'] = array(
73          );          '#type' => 'fieldset',
74            '#title' => t('Taxonomy category filter'),
75          if (module_exists('taxonomy')) {          '#collapsible' => TRUE,
76            $names = _similar_taxonomy_names();          '#collapsed' => TRUE
77            $form['similar_taxonomy'] = array(        );
78              '#type' => 'fieldset',        $form['similar_taxonomy']['similar_taxonomy_filter'] = array(
79              '#title' => t('Taxonomy category filter'),          '#type' => 'radios',
80              '#collapsible' => TRUE,          '#title' => t('Filter by taxonomy categories'),
81              '#collapsed' => TRUE          '#default_value' => variable_get('similar_taxonomy_filter', 0),
82            );          '#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')),
83            $form['similar_taxonomy']['similar_taxonomy_filter'] = array(          '#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.')
84              '#type' => 'radios',        );
85              '#title' => t('Filter by taxonomy categories'),        $form['similar_taxonomy']['similar_taxonomy_select'] = array(
86              '#default_value' => variable_get('similar_taxonomy_filter', 0),          '#type' => 'fieldset',
87              '#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')),          '#title' => t('Taxonomy categories to display'),
88              '#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.')          '#collapsible' => TRUE,
89            );          '#collapsed' => TRUE
90            $form['similar_taxonomy']['similar_taxonomy_select'] = array(        );
91              '#type' => 'fieldset',        $form['similar_taxonomy']['similar_taxonomy_select']['similar_taxonomy_tids'] = array(
92              '#title' => t('Taxonomy categories to display'),          '#type' => 'select',
93              '#collapsible' => TRUE,          '#default_value' => variable_get('similar_taxonomy_tids', array_keys($names)),
94              '#collapsed' => TRUE          '#description' => t('Hold the CTRL key to (de)select multiple options.'),
95            );          '#options' => $names,
96            $form['similar_taxonomy']['similar_taxonomy_select']['similar_taxonomy_tids'] = array(          '#multiple' => TRUE
97              '#type' => 'select',        );
98              '#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;  
99    }    }
100      return $form;
101    }
102    
103    /**
104     * Implementation of hook_block_save().
105     */
106    function similar_block_save($delta = 0, $edit = array()) {
107      if ($delta == 0) {
108        variable_set('similar_teaser_enabled', $edit['similar_teaser_enabled']);
109        variable_set('similar_rel_nofollow', $edit['similar_rel_nofollow']);
110        variable_set('similar_num_display', $edit['similar_num_display']);
111        variable_set('similar_node_types', $edit['similar_node_types']);
112        if (module_exists('taxonomy')) {
113          variable_set('similar_taxonomy_filter', $edit['similar_taxonomy_filter']);
114          variable_set('similar_taxonomy_tids', $edit['similar_taxonomy_tids']);
115        }
116      }
117    }
118    
119    /**
120     * Implementation of hook_block_view().
121     */
122    function similar_block_view($delta = 0) {
123      if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) != 'edit') {
124        $node = node_load(arg(1));
125      }
126      else {
127        return;
128      }
129    
130      $similar_node_types = variable_get('similar_node_types', _similar_published_node_types());
131    
132      if ($node->nid > 0 && !empty($similar_node_types[$node->type])) {
133        unset($similar_node_types);
134    
135        switch ($delta) {
136          case 0:
137            // The subject is displayed at the top of the block. Note that it should
138            // be passed through t() for translation.
139            $block['subject'] = t('Similar entries');
140            $block['content'] = theme('similar_content', $node);
141        }
142      }
143      return empty($block['content']) ? '' : $block;
144  }  }
145    
146  /**  /**
# Line 155  function similar_block($op = 'list', $de Line 152  function similar_block($op = 'list', $de
152  function _similar_published_node_types() {  function _similar_published_node_types() {
153    $types  = array();    $types  = array();
154    $result = db_query('SELECT DISTINCT(n.type) FROM {node} n WHERE n.status <> 0 ORDER BY n.type ASC');    $result = db_query('SELECT DISTINCT(n.type) FROM {node} n WHERE n.status <> 0 ORDER BY n.type ASC');
155    while ($type = db_fetch_object($result)) {    while ($type = $result->fetchObject()) {
156      $types[$type->type] = $type->type;      $types[$type->type] = $type->type;
157    }    }
158    return $types;    return $types;
# Line 169  function _similar_published_node_types() Line 166  function _similar_published_node_types()
166   */   */
167  function _similar_taxonomy_names() {  function _similar_taxonomy_names() {
168    $names  = array();    $names  = array();
169    $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');    $result = db_query('SELECT d.tid, v.vid, v.name AS vocab_name, d.name AS data_name FROM {taxonomy_term_data} d, {taxonomy_vocabulary} v WHERE v.vid = d.vid ORDER BY v.name, d.name ASC');
170    while ($data = db_fetch_object($result)) {    while ($data = $result->fetchObject()) {
171      $names[$data->tid] = $data->vocab_name .': '. $data->data_name;      $names[$data->tid] = $data->vocab_name . ': ' . $data->data_name;
172    }    }
173    return $names;    return $names;
174  }  }
# Line 184  function _similar_taxonomy_names() { Line 181  function _similar_taxonomy_names() {
181   */   */
182  function _similar_taxonomy_membership($nid) {  function _similar_taxonomy_membership($nid) {
183    $tids = array();    $tids = array();
184    $result = db_query('SELECT t.tid FROM {term_node} t WHERE t.nid = %d', $nid);    $result = db_query('SELECT t.tid FROM {taxonomy_term_node} t WHERE t.nid = :nid', array(':nid' => $nid));
185    while ($data = db_fetch_object($result)) {    while ($data = $result->fetchObject()) {
186      $tids[$data->tid] = $data->tid;      $tids[$data->tid] = $data->tid;
187    }    }
188    return $tids;    return $tids;
# Line 199  function _similar_content_type_escape(&$ Line 196  function _similar_content_type_escape(&$
196  }  }
197    
198  /**  /**
199   * SQL injection prevention   * SQL injection prevention. Probably overkill.
200   */   */
201  function _similar_force_int(&$item) {  function _similar_force_int(&$item) {
202    $item = (int)$item;    $item = (int)$item;
# Line 234  function theme_similar_content($node) { Line 231  function theme_similar_content($node) {
231    $types = variable_get('similar_node_types', $types);    $types = variable_get('similar_node_types', $types);
232    array_walk($types, '_similar_content_type_escape');    array_walk($types, '_similar_content_type_escape');
233    
234    if (sizeof($types) > 1) {    $query = db_select('node_revision', 'r');
235      $types = implode("','", $types);    $query->addField('r', 'nid', 'nid');
236    }    $query->addExpression('MATCH(r.body, r.title) AGAINST (:matchtext)', 'score', array(':matchtext' => $text));
237    else {    $query->innerJoin('node', 'n', 'r.nid = n.nid AND r.vid = n.vid');
238      list(, $types) = each($types);  
   }  
   $types = "'$types'";  
239    if (module_exists('taxonomy') && (variable_get('similar_taxonomy_filter', 0) == 2 && $taxonomy_tids = variable_get('similar_taxonomy_tids', array()))    if (module_exists('taxonomy') && (variable_get('similar_taxonomy_filter', 0) == 2 && $taxonomy_tids = variable_get('similar_taxonomy_tids', array()))
240      || (variable_get('similar_taxonomy_filter', 0) == 1 && $taxonomy_tids = _similar_taxonomy_membership($node->nid))) {      || (variable_get('similar_taxonomy_filter', 0) == 1 && $taxonomy_tids = _similar_taxonomy_membership($node->nid))) {
241    
242      array_walk($taxonomy_tids, '_similar_force_int');      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;  
     }  
243    
244      $result = db_query_range("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 MATCH(r.body, r.title) AGAINST ('%s') AND n.status <> 0 AND r.nid <> %d AND n.type IN ($types) GROUP BY n.nid ORDER BY score DESC, r.vid DESC", $text, $taxonomy_tids, $text, $node->nid, 0, variable_get('similar_num_display', 5));      $query->innerJoin('taxonomy_term_node', 't', 'n.nid = t.nid AND t.tid IN (:tids)', array(':tids' => $taxonomy_tids));
   }  
   else {  
     $result = db_query_range("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 MATCH(r.body, r.title) AGAINST ('%s') AND n.status <> 0 AND r.nid <> %d AND n.type IN ($types) GROUP BY n.nid ORDER BY score DESC, r.vid DESC", $text, $text, $node->nid, 0, variable_get('similar_num_display', 5));  
245    }    }
246    while ($node = db_fetch_object($result)) {  
247      $query->where("MATCH(r.body, r.title) AGAINST (:wheretext)", array(':wheretext' => $text));
248      $query->condition('n.status', 0, '<>');
249      $query->condition('r.nid', $node->nid, '<>');
250      $query->condition('n.type', $types, 'IN');
251      $query->groupBy('nid');
252      $query->orderBy('score', 'DESC');
253      $query->orderBy('r.vid', 'DESC');
254      $query->range(0, variable_get('similar_num_display', 5));
255      $result = $query->execute();
256    
257      while ($node = $result->fetchObject()) {
258      $content = node_load($node->nid);      $content = node_load($node->nid);
259      if ($teaser) {      if ($teaser) {
260        $items[] = '<div class="similar-title">'.        $items[] = '<div class="similar-title">' .
261          l($content->title,          l($content->title,
262            'node/'. $node->nid,            'node/' . $node->nid,
263            array(            array(
264              'attributes' => variable_get('similar_rel_nofollow', 0) ? array('rel' => 'nofollow') : NULL,              'attributes' => variable_get('similar_rel_nofollow', 0) ? array('rel' => 'nofollow') : NULL,
265              'absolute' => TRUE              'absolute' => TRUE
266            )            )
267          ) .          ) .
268          '</div><div class="similar-teaser">'. check_markup($content->teaser, $content->format, FALSE) .'</div>';          '</div><div class="similar-teaser">' . check_markup($content->teaser, $content->format, FALSE) . '</div>';
269      }      }
270      else {      else {
271        $items[] = l(        $items[] = l(
272          $content->title,          $content->title,
273          'node/'. $node->nid,          'node/' . $node->nid,
274          array('attributes' => variable_get('similar_rel_nofollow', 0) ? array('rel' => 'nofollow') : NULL)          array('attributes' => variable_get('similar_rel_nofollow', 0) ? array('rel' => 'nofollow') : NULL)
275        );        );
276      }      }

Legend:
Removed from v.1.12  
changed lines
  Added in v.1.13

  ViewVC Help
Powered by ViewVC 1.1.2