Relevanssi – A Better Search - Version 3.5.11

Version Description

  • Synonym indexing failed if synonyms contained a forward slash.
  • Highlighting HTML tags has been improved further.
  • New filter: relevanssi_tag_before_tokenize allows you to access tag content before indexing.
  • Relevanssi now actively blocks one-letter search terms, as they are generally pointless and can cause "out of memory" issues. One-letter search terms are no longer highlighted, either. These are usually caused by cases like "word's" being interpreted as "word s".
  • New filter: relevanssi_disable_shortcodes_excerpt lets you add more shortcodes to be disabled before excerpts are built.
Download this release

Release Info

Developer msaari
Plugin Icon 128x128 Relevanssi – A Better Search
Version 3.5.11
Comparing to
See all releases

Code changes from version 3.5.10 to 3.5.11

lib/excerpts-highlights.php CHANGED
@@ -22,10 +22,12 @@ function relevanssi_do_excerpt($t_post, $query) {
22
  $terms = relevanssi_tokenize($query, $remove_stopwords, -1);
23
 
24
  // These shortcodes cause problems with Relevanssi excerpts
25
- remove_shortcode('layerslider');
26
- remove_shortcode('responsive-flipbook');
27
- remove_shortcode('breadcrumb');
28
- remove_shortcode('maxmegamenu');
 
 
29
 
30
  $content = apply_filters('relevanssi_pre_excerpt_content', $post->post_content, $post, $query);
31
  $content = apply_filters('the_content', $content);
@@ -77,7 +79,7 @@ function relevanssi_do_excerpt($t_post, $query) {
77
 
78
  $highlight = get_option('relevanssi_highlight');
79
  if ("none" != $highlight) {
80
- if ( !is_admin() || ( defined( 'DOING_AJAX' ) || DOING_AJAX ) ) {
81
  $query = relevanssi_add_synonyms($query);
82
  $excerpt = relevanssi_highlight_terms($excerpt, $query);
83
  }
@@ -264,7 +266,8 @@ function relevanssi_highlight_terms($excerpt, $query, $in_docs = false) {
264
  mb_internal_encoding("UTF-8");
265
 
266
  do_action('relevanssi_highlight_tokenize');
267
- $terms = array_keys(relevanssi_tokenize($query, $remove_stopwords = true, $min_word_length = -1));
 
268
 
269
  if (is_array($query)) $query = implode(' ', $query); // just in case
270
  $phrases = relevanssi_extract_phrases(stripslashes($query));
@@ -290,7 +293,7 @@ function relevanssi_highlight_terms($excerpt, $query, $in_docs = false) {
290
  $pr_term = relevanssi_add_accent_variations($pr_term);
291
 
292
  $undecoded_excerpt = $excerpt;
293
- $excerpt = html_entity_decode($excerpt);
294
 
295
  if ($word_boundaries) {
296
  // get_option('relevanssi_fuzzy') != 'none' ? $regex = "/($pr_term)(?!(^&+)?(;))/iu" : $regex = "/(\b$pr_term|$pr_term\b)(?!(^&+)?(;))/iu";
@@ -360,18 +363,49 @@ function relevanssi_replace_punctuation($a) {
360
  function relevanssi_fix_entities($excerpt, $in_docs) {
361
  if (!$in_docs) {
362
  // For excerpts, use htmlentities()
363
- $excerpt = htmlentities($excerpt, ENT_QUOTES, 'UTF-8');
364
 
365
  // Except for allowed tags, which are turned back into tags.
366
  $tags = get_option('relevanssi_excerpt_allowable_tags', '');
367
  $tags = trim(str_replace("<", " <", $tags));
368
- $tags = explode(" ", $tags);
369
- $tags = relevanssi_generate_closing_tags($tags);
370
 
371
- $tags_entitied = htmlentities(implode(" ", $tags), ENT_QUOTES, 'UTF-8');
372
  $tags_entitied = explode(" ", $tags_entitied);
373
 
374
- $excerpt = str_replace($tags_entitied, $tags, $excerpt);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  }
376
  else {
377
  // Running htmlentities() for whole posts tends to ruin things.
@@ -409,7 +443,7 @@ function relevanssi_generate_closing_tags($tags) {
409
  $closing_tags[] = $a;
410
  $closing_tags[] = $b;
411
  }
412
- return array_merge($tags, $closing_tags);
413
  }
414
 
415
  function relevanssi_remove_nested_highlights($s, $a, $b) {
22
  $terms = relevanssi_tokenize($query, $remove_stopwords, -1);
23
 
24
  // These shortcodes cause problems with Relevanssi excerpts
25
+ $problem_shortcodes = apply_filters('relevanssi_disable_shortcodes_excerpt',
26
+ array('layerslider', 'responsive-flipbook', 'breadcrumb', 'maxmegamenu', 'robogallery')
27
+ );
28
+ foreach ($problem_shortcodes as $shortcode) {
29
+ remove_shortcode($shortcode);
30
+ }
31
 
32
  $content = apply_filters('relevanssi_pre_excerpt_content', $post->post_content, $post, $query);
33
  $content = apply_filters('the_content', $content);
79
 
80
  $highlight = get_option('relevanssi_highlight');
81
  if ("none" != $highlight) {
82
+ if ( !is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
83
  $query = relevanssi_add_synonyms($query);
84
  $excerpt = relevanssi_highlight_terms($excerpt, $query);
85
  }
266
  mb_internal_encoding("UTF-8");
267
 
268
  do_action('relevanssi_highlight_tokenize');
269
+ $terms = array_keys(relevanssi_tokenize($query, $remove_stopwords = true, $min_word_length = 2));
270
+ // Setting min_word_length to 2, in order to avoid 1-letter highlights.
271
 
272
  if (is_array($query)) $query = implode(' ', $query); // just in case
273
  $phrases = relevanssi_extract_phrases(stripslashes($query));
293
  $pr_term = relevanssi_add_accent_variations($pr_term);
294
 
295
  $undecoded_excerpt = $excerpt;
296
+ $excerpt = html_entity_decode($excerpt, ENT_QUOTES, 'UTF-8');
297
 
298
  if ($word_boundaries) {
299
  // get_option('relevanssi_fuzzy') != 'none' ? $regex = "/($pr_term)(?!(^&+)?(;))/iu" : $regex = "/(\b$pr_term|$pr_term\b)(?!(^&+)?(;))/iu";
363
  function relevanssi_fix_entities($excerpt, $in_docs) {
364
  if (!$in_docs) {
365
  // For excerpts, use htmlentities()
366
+ $excerpt = htmlentities($excerpt, ENT_NOQUOTES, 'UTF-8'); // ENT_QUOTES or ENT_NOQUOTES?
367
 
368
  // Except for allowed tags, which are turned back into tags.
369
  $tags = get_option('relevanssi_excerpt_allowable_tags', '');
370
  $tags = trim(str_replace("<", " <", $tags));
371
+ $tags = explode(" ", $tags);
372
+ $closing_tags = relevanssi_generate_closing_tags($tags);
373
 
374
+ $tags_entitied = htmlentities(implode(" ", $tags), ENT_NOQUOTES, 'UTF-8'); // ENT_QUOTES or ENT_NOQUOTES?
375
  $tags_entitied = explode(" ", $tags_entitied);
376
 
377
+ $closing_tags_entitied = htmlentities(implode(" ", $closing_tags), ENT_NOQUOTES, 'UTF-8'); // ENT_QUOTES or ENT_NOQUOTES?
378
+ $closing_tags_entitied = explode(" ", $closing_tags_entitied);
379
+
380
+ $tags_entitied_regexped = array();
381
+ $i = 0;
382
+ foreach ($tags_entitied as $tag) {
383
+ $tag = str_replace("&gt;", "(.*?)&gt;", $tag);
384
+ $pattern = "~$tag~";
385
+ $tags_entitied_regexped[] = $pattern;
386
+
387
+ $matching_tag = $tags[$i];
388
+ $matching_tag = str_replace(">", '\1>', $matching_tag);
389
+ $tags[$i] = $matching_tag;
390
+ $i++;
391
+ }
392
+
393
+ $closing_tags_entitied_regexped = array();
394
+ foreach ($closing_tags_entitied as $tag) {
395
+ $pattern = "~" . preg_quote($tag) . "~";
396
+ $closing_tags_entitied_regexped[] = $pattern;
397
+ }
398
+
399
+ $tags = array_merge($tags, $closing_tags);
400
+ $tags_entitied = array_merge($tags_entitied_regexped, $closing_tags_entitied_regexped);
401
+
402
+ $excerpt = preg_replace($tags_entitied, $tags, $excerpt);
403
+
404
+ // In case there are attributes. This is the easiest solution, as
405
+ // using quotes and apostrophes un-entitied can't really break
406
+ // anything.
407
+ $excerpt = str_replace('&quot;', '"', $excerpt);
408
+ $excerpt = str_replace('&#039;', "'", $excerpt);
409
  }
410
  else {
411
  // Running htmlentities() for whole posts tends to ruin things.
443
  $closing_tags[] = $a;
444
  $closing_tags[] = $b;
445
  }
446
+ return $closing_tags;
447
  }
448
 
449
  function relevanssi_remove_nested_highlights($s, $a, $b) {
lib/indexing.php CHANGED
@@ -133,9 +133,13 @@ function relevanssi_build_index($extend = false, $verbose = true, $post_limit =
133
 
134
  $complete = false;
135
  if ($verbose) {
136
- echo '<div id="message" class="updated fade"><p>'
137
- . __((($size == 0) || (count($content) < $size)) ? "Indexing complete!" : "More to index...", "relevanssi")
138
- . '</p></div>';
 
 
 
 
139
  }
140
  else {
141
  if (($size == 0) || (count($content) < $size)) $complete = true;
@@ -315,6 +319,10 @@ function relevanssi_index_doc($indexpost, $remove_first = false, $custom_fields
315
  if (is_array($custom_fields)) {
316
  if ($debug) relevanssi_debug_echo("Custom fields to index: " . implode(", ", $custom_fields));
317
  $custom_fields = array_unique($custom_fields); // no reason to index duplicates
 
 
 
 
318
  foreach ($custom_fields as $field) {
319
  if ($remove_underscore_fields) {
320
  if (substr($field, 0, 1) == '_') continue;
@@ -557,7 +565,7 @@ function relevanssi_index_taxonomy_terms($post = null, $taxonomy = "", $insert_d
557
  $tagstr .= $ptag->name . ' ';
558
  }
559
  }
560
- $tagstr = trim($tagstr);
561
  $ptags = relevanssi_tokenize($tagstr, true, $min_word_length);
562
  if (count($ptags) > 0) {
563
  foreach ($ptags as $ptag => $count) {
@@ -786,17 +794,19 @@ function relevanssi_get_comments($postID) {
786
  * Droz Raphaël, June 2016
787
  */
788
  function relevanssi_index_acf(&$insert_data, $post_id, $field_name, $field_value) {
789
- if (! is_admin() ) include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); // otherwise is_plugin_active will cause a fatal error
790
- if (! is_plugin_active('advanced-custom-fields/acf.php') &&
791
- ! is_plugin_active('advanced-custom-fields-pro/acf.php')) return;
 
 
792
 
793
  $field_object = get_field_object($field_name, $post_id);
794
- if (! isset($field_object['choices'])) return; // not a "select" field
795
  if (is_array($field_value)) return; // not handled (currently)
796
- if (! isset($field_object['choices'][$field_value])) return; // value does not exist
797
 
798
  if ( ($value = $field_object['choices'][$field_value]) ) {
799
- if (! isset($insert_data[$value]['customfield']) ) $insert_data[$value]['customfield'] = 0;
800
  $insert_data[$value]['customfield']++;
801
  }
802
  }
133
 
134
  $complete = false;
135
  if ($verbose) {
136
+ if (($size == 0) || (count($content) < $size)) {
137
+ $message = __("Indexing complete!", "relevanssi");
138
+ }
139
+ else {
140
+ $message = __("More to index...", "relevanssi");
141
+ }
142
+ echo '<div id="message" class="updated fade"><p>' . $message . '</p></div>';
143
  }
144
  else {
145
  if (($size == 0) || (count($content) < $size)) $complete = true;
319
  if (is_array($custom_fields)) {
320
  if ($debug) relevanssi_debug_echo("Custom fields to index: " . implode(", ", $custom_fields));
321
  $custom_fields = array_unique($custom_fields); // no reason to index duplicates
322
+
323
+ $repeater_fields = array();
324
+ if (function_exists('relevanssi_add_repeater_fields')) relevanssi_add_repeater_fields($custom_fields, $post->ID);
325
+
326
  foreach ($custom_fields as $field) {
327
  if ($remove_underscore_fields) {
328
  if (substr($field, 0, 1) == '_') continue;
565
  $tagstr .= $ptag->name . ' ';
566
  }
567
  }
568
+ $tagstr = apply_filters('relevanssi_tag_before_tokenize', trim($tagstr));
569
  $ptags = relevanssi_tokenize($tagstr, true, $min_word_length);
570
  if (count($ptags) > 0) {
571
  foreach ($ptags as $ptag => $count) {
794
  * Droz Raphaël, June 2016
795
  */
796
  function relevanssi_index_acf(&$insert_data, $post_id, $field_name, $field_value) {
797
+ if (!is_admin() ) include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); // otherwise is_plugin_active will cause a fatal error
798
+ if (!is_plugin_active('advanced-custom-fields/acf.php') &&
799
+ !is_plugin_active('advanced-custom-fields-pro/acf.php')) return;
800
+
801
+ if (!function_exists('get_field_object')) return; // ACF is active, but not loaded.
802
 
803
  $field_object = get_field_object($field_name, $post_id);
804
+ if (!isset($field_object['choices'])) return; // not a "select" field
805
  if (is_array($field_value)) return; // not handled (currently)
806
+ if (!isset($field_object['choices'][$field_value])) return; // value does not exist
807
 
808
  if ( ($value = $field_object['choices'][$field_value]) ) {
809
+ if (!isset($insert_data[$value]['customfield']) ) $insert_data[$value]['customfield'] = 0;
810
  $insert_data[$value]['customfield']++;
811
  }
812
  }
lib/init.php CHANGED
@@ -150,7 +150,7 @@ function relevanssi_create_database_tables($relevanssi_db_version) {
150
  taxonomy_detail longtext NOT NULL,
151
  customfield_detail longtext NOT NULL,
152
  mysqlcolumn_detail longtext NOT NULL,
153
- type varchar(210) NOT NULL DEFAULT 'post',
154
  item bigint(20) NOT NULL DEFAULT '0',
155
  UNIQUE KEY doctermitem (doc, term, item)) $charset_collate";
156
 
@@ -246,4 +246,17 @@ function relevanssi_create_database_tables($relevanssi_db_version) {
246
  relevanssi_populate_stopwords();
247
  }
248
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  ?>
150
  taxonomy_detail longtext NOT NULL,
151
  customfield_detail longtext NOT NULL,
152
  mysqlcolumn_detail longtext NOT NULL,
153
+ type varchar(210) NOT NULL DEFAULT 'post',
154
  item bigint(20) NOT NULL DEFAULT '0',
155
  UNIQUE KEY doctermitem (doc, term, item)) $charset_collate";
156
 
246
  relevanssi_populate_stopwords();
247
  }
248
  }
249
+
250
+ function relevanssi_action_links ($links) {
251
+ $root = "relevanssi";
252
+ if (RELEVANSSI_PREMIUM) $root = "relevanssi-premium";
253
+ $relevanssi_links = array(
254
+ '<a href="' . admin_url( 'options-general.php?page=' . $root . '/relevanssi.php' ) . '">' . __('Settings', 'relevanssi') . '</a>',
255
+ );
256
+ if (!RELEVANSSI_PREMIUM) {
257
+ $relevanssi_links[] = '<a href="https://www.relevanssi.com/buy-premium/">' . __('Go Premium!', 'relevanssi') . '</a>';
258
+ }
259
+ return array_merge($links, $relevanssi_links);
260
+ }
261
+
262
  ?>
lib/interface.php CHANGED
@@ -1335,7 +1335,7 @@ EOH;
1335
 
1336
  <label for='relevanssi_index_excerpt'><?php _e('Index and search post excerpts:', 'relevanssi'); ?>
1337
  <input type='checkbox' name='relevanssi_index_excerpt' id='relevanssi_index_excerpt' <?php echo $index_excerpt ?> /></label><br />
1338
- <small><?php _e("If checked, Relevanssi will also index and search the excerpts of your posts.Remember to rebuild the index if you change this option!", 'relevanssi'); ?></small>
1339
 
1340
  <br /><br />
1341
 
@@ -1351,7 +1351,7 @@ EOH;
1351
 
1352
  <label for='relevanssi_index_fields'><?php _e("Custom fields to index:", "relevanssi"); ?>
1353
  <input type='text' name='relevanssi_index_fields' id='relevanssi_index_fields' size='30' value='<?php echo esc_attr($index_fields) ?>' /></label><br />
1354
- <small><?php _e("A comma-separated list of custom fields to include in the index. Set to 'visible' to index all visible custom fields and to 'all' to index all custom fields, also those starting with a '_' character.", "relevanssi"); ?></small>
1355
 
1356
  <br /><br />
1357
 
1335
 
1336
  <label for='relevanssi_index_excerpt'><?php _e('Index and search post excerpts:', 'relevanssi'); ?>
1337
  <input type='checkbox' name='relevanssi_index_excerpt' id='relevanssi_index_excerpt' <?php echo $index_excerpt ?> /></label><br />
1338
+ <small><?php _e("If checked, Relevanssi will also index and search the excerpts of your posts. Remember to rebuild the index if you change this option!", 'relevanssi'); ?></small>
1339
 
1340
  <br /><br />
1341
 
1351
 
1352
  <label for='relevanssi_index_fields'><?php _e("Custom fields to index:", "relevanssi"); ?>
1353
  <input type='text' name='relevanssi_index_fields' id='relevanssi_index_fields' size='30' value='<?php echo esc_attr($index_fields) ?>' /></label><br />
1354
+ <small><?php _e("A comma-separated list of custom fields to include in the index. Set to 'visible' to index all visible custom fields and to 'all' to index all custom fields, also those starting with a '_' character. With Relevanssi Premium, you can also use 'fieldname_%_subfieldname' notation for ACF repeater fields. You can use 'relevanssi_index_custom_fields' filter hook to adjust which custom fields are indexed.", "relevanssi"); ?></small>
1355
 
1356
  <br /><br />
1357
 
lib/search.php CHANGED
@@ -374,23 +374,36 @@ function relevanssi_search($args) {
374
  }
375
  }
376
 
377
- if (!$post_type && get_option('relevanssi_respect_exclude') == 'on') {
378
- if (function_exists('get_post_types')) {
379
- $pt_1 = get_post_types(array('exclude_from_search' => '0'));
380
- $pt_2 = get_post_types(array('exclude_from_search' => false));
381
- $post_type = implode(',', array_merge($pt_1, $pt_2));
382
- }
 
383
  }
384
 
385
  if ($post_type) {
386
  if ($post_type == -1) $post_type = null; // Facetious sets post_type to -1 if not selected
387
  if (!is_array($post_type)) {
388
- $post_types = esc_sql(explode(',', $post_type));
389
  }
390
  else {
391
- $post_types = esc_sql($post_type);
392
  }
393
- $post_type = count($post_types) ? "'" . implode( "', '", $post_types) . "'" : 'NULL';
 
 
 
 
 
 
 
 
 
 
 
 
394
  }
395
 
396
  if ($post_status) {
@@ -401,7 +414,7 @@ function relevanssi_search($args) {
401
  $post_statuses = esc_sql($post_status);
402
  }
403
 
404
- $post_status = count($post_statuses) ? "'" . implode( "', '", $post_statuses) . "'" : 'NULL';
405
  }
406
 
407
  //Added by OdditY:
@@ -516,17 +529,39 @@ function relevanssi_search($args) {
516
  }
517
 
518
  if ($post_type) {
519
- global $wp_query;
520
- if ($wp_query->is_admin) {
521
- $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
522
- WHERE posts.post_type IN ($post_type))))";
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  }
524
- else {
525
- $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
526
- WHERE posts.post_type IN ($post_type))) OR (doc = -1))";
527
- // the -1 is there to get user profiles and category pages
 
 
 
 
528
  }
529
- // Clean: $post_type is escaped
 
 
 
 
 
530
  }
531
 
532
  if ($post_status) {
@@ -619,6 +654,7 @@ function relevanssi_search($args) {
619
  do {
620
  foreach ($terms as $term) {
621
  $term = trim($term); // numeric search terms will start with a space
 
622
  $term = esc_sql($term);
623
 
624
  if (strpos($o_term_cond, 'LIKE') !== false) {
@@ -1287,7 +1323,9 @@ function relevanssi_do_query(&$query) {
1287
 
1288
  $filter_data = array($hits, $q);
1289
  $hits_filters_applied = apply_filters('relevanssi_hits_filter', $filter_data);
1290
- $hits = $hits_filters_applied[0];
 
 
1291
 
1292
  $query->found_posts = sizeof($hits);
1293
  if (!isset($query->query_vars["posts_per_page"]) || $query->query_vars["posts_per_page"] == 0) {
@@ -1399,4 +1437,30 @@ function relevanssi_limit_filter($query) {
1399
  }
1400
  }
1401
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1402
  ?>
374
  }
375
  }
376
 
377
+ // If $post_type is not set, see if there are post types to exclude from the search.
378
+ // If $post_type is set, there's no need to exclude, as we only include.
379
+ !$post_type ? $negative_post_type = relevanssi_get_negative_post_type() : $negative_post_type = NULL;
380
+
381
+ $non_post_post_types_array = array();
382
+ if (function_exists('relevanssi_get_non_post_post_types')) {
383
+ $non_post_post_types_array = relevanssi_get_non_post_post_types();
384
  }
385
 
386
  if ($post_type) {
387
  if ($post_type == -1) $post_type = null; // Facetious sets post_type to -1 if not selected
388
  if (!is_array($post_type)) {
389
+ $post_types = explode(',', $post_type);
390
  }
391
  else {
392
+ $post_types = $post_type;
393
  }
394
+ // This array will contain all regular post types involved in the search parameters.
395
+ $post_post_types = array_diff($post_types, $non_post_post_types_array);
396
+
397
+ // This array has the non-post post types involved.
398
+ $non_post_post_types = array_intersect($post_types, $non_post_post_types_array);
399
+
400
+ // Escape both for SQL queries, just in case.
401
+ $non_post_post_types = esc_sql($non_post_post_types);
402
+ $post_types = esc_sql($post_post_types);
403
+
404
+ // Implode to a parameter string, or set to NULL if empty.
405
+ $non_post_post_type = count($non_post_post_types) ? "'" . implode( "', '", $non_post_post_types) . "'" : NULL;
406
+ $post_type = count($post_types) ? "'" . implode( "', '", $post_types) . "'" : NULL;
407
  }
408
 
409
  if ($post_status) {
414
  $post_statuses = esc_sql($post_status);
415
  }
416
 
417
+ $post_status = count($post_statuses) ? "'" . implode( "', '", $post_statuses) . "'" : NULL;
418
  }
419
 
420
  //Added by OdditY:
529
  }
530
 
531
  if ($post_type) {
532
+ // A post type is set: add a restriction
533
+ $restriction = " AND (
534
+ relevanssi.doc IN (
535
+ SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
536
+ WHERE posts.post_type IN ($post_type)
537
+ ) *np*
538
+ )";
539
+ // Clean: $post_type is escaped
540
+
541
+ // There are post types involved that are taxonomies or users, so can't
542
+ // match to wp_posts. Add a relevanssi.type restriction.
543
+ if ($non_post_post_type) {
544
+ $restriction = str_replace('*np*', "OR (relevanssi.type IN ($non_post_post_type))", $restriction);
545
+ // Clean: $non_post_post_types is escaped
546
+ } else {
547
+ // No non-post post types, so remove the placeholder.
548
+ $restriction = str_replace('*np*', '', $restriction);
549
  }
550
+ $query_restrictions .= $restriction;
551
+ }
552
+ else {
553
+ // No regular post types
554
+ if ($non_post_post_type) {
555
+ // But there is a non-post post type restriction.
556
+ $query_restrictions .= " AND (relevanssi.type IN ($non_post_post_type))";
557
+ // Clean: $non_post_post_types is escaped
558
  }
559
+ }
560
+
561
+ if ($negative_post_type) {
562
+ $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
563
+ WHERE posts.post_type NOT IN ($negative_post_type))) OR (doc = -1))";
564
+ // Clean: $negative_post_type is escaped
565
  }
566
 
567
  if ($post_status) {
654
  do {
655
  foreach ($terms as $term) {
656
  $term = trim($term); // numeric search terms will start with a space
657
+ if (relevanssi_strlen($term) < 2) continue;
658
  $term = esc_sql($term);
659
 
660
  if (strpos($o_term_cond, 'LIKE') !== false) {
1323
 
1324
  $filter_data = array($hits, $q);
1325
  $hits_filters_applied = apply_filters('relevanssi_hits_filter', $filter_data);
1326
+ $hits = array_values($hits_filters_applied[0]);
1327
+ // array_values() to make sure the $hits array is indexed in numerical order
1328
+ // Manipulating the array with array_unique() for example may mess with that.
1329
 
1330
  $query->found_posts = sizeof($hits);
1331
  if (!isset($query->query_vars["posts_per_page"]) || $query->query_vars["posts_per_page"] == 0) {
1437
  }
1438
  }
1439
 
1440
+ function relevanssi_get_negative_post_type() {
1441
+ $negative_post_type = NULL;
1442
+
1443
+ if (get_option('relevanssi_respect_exclude') == 'on') {
1444
+ // If Relevanssi is set to respect exclude_from_search, find out which
1445
+ // post types should be excluded from search.
1446
+ if (function_exists('get_post_types')) {
1447
+ $pt_1 = get_post_types(array('exclude_from_search' => '1'));
1448
+ $pt_2 = get_post_types(array('exclude_from_search' => true));
1449
+ $negative_post_type_list = implode(',', array_merge($pt_1, $pt_2));
1450
+ }
1451
+
1452
+ // Post types to exclude.
1453
+ if ($negative_post_type_list) {
1454
+ if (!is_array($negative_post_type)) {
1455
+ $negative_post_types = esc_sql(explode(',', $negative_post_type));
1456
+ }
1457
+ else {
1458
+ $negative_post_types = esc_sql($negative_post_type);
1459
+ }
1460
+ $negative_post_type = count($negative_post_types) ? "'" . implode( "', '", $negative_post_types) . "'" : NULL;
1461
+ }
1462
+ }
1463
+
1464
+ return $negative_post_type;
1465
+ }
1466
  ?>
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: msaari
3
  Donate link: https://www.relevanssi.com/buy-premium/
4
  Tags: search, relevance, better search
5
  Requires at least: 4.0
6
- Tested up to: 4.7.5
7
- Stable tag: 3.5.10
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -268,6 +268,13 @@ Each document database is full of useless words. All the little words that appea
268
 
269
  == Changelog ==
270
 
 
 
 
 
 
 
 
271
  = 3.5.10 =
272
  * Some users got a fatal parse error. That shouldn't happen anymore.
273
  * FacetWP users ran into trouble, as `relevanssi_do_query()` started to explicitly expect a WP_Query object in version 1.15.0. That expectation is removed; it's still highly recommended for future compatibility that you use WP_Query objects.
@@ -1051,6 +1058,9 @@ Each document database is full of useless words. All the little words that appea
1051
 
1052
  == Upgrade notice ==
1053
 
 
 
 
1054
  = 3.5.10 =
1055
  * Prevented a fatal error for some users, small bug fixes.
1056
 
3
  Donate link: https://www.relevanssi.com/buy-premium/
4
  Tags: search, relevance, better search
5
  Requires at least: 4.0
6
+ Tested up to: 4.9
7
+ Stable tag: 3.5.11
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
268
 
269
  == Changelog ==
270
 
271
+ = 3.5.11 =
272
+ * Synonym indexing failed if synonyms contained a forward slash.
273
+ * Highlighting HTML tags has been improved further.
274
+ * New filter: `relevanssi_tag_before_tokenize` allows you to access tag content before indexing.
275
+ * Relevanssi now actively blocks one-letter search terms, as they are generally pointless and can cause "out of memory" issues. One-letter search terms are no longer highlighted, either. These are usually caused by cases like "word's" being interpreted as "word s".
276
+ * New filter: `relevanssi_disable_shortcodes_excerpt` lets you add more shortcodes to be disabled before excerpts are built.
277
+
278
  = 3.5.10 =
279
  * Some users got a fatal parse error. That shouldn't happen anymore.
280
  * FacetWP users ran into trouble, as `relevanssi_do_query()` started to explicitly expect a WP_Query object in version 1.15.0. That expectation is removed; it's still highly recommended for future compatibility that you use WP_Query objects.
1058
 
1059
  == Upgrade notice ==
1060
 
1061
+ = 3.5.11 =
1062
+ * Improvements in excerpts, new filters.
1063
+
1064
  = 3.5.10 =
1065
  * Prevented a fatal error for some users, small bug fixes.
1066
 
relevanssi.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Relevanssi
4
  Plugin URI: http://www.relevanssi.com/
5
  Description: This plugin replaces WordPress search with a relevance-sorting search.
6
- Version: 3.5.10
7
  Author: Mikko Saari
8
  Author URI: http://www.mikkosaari.fi/
9
  */
@@ -35,6 +35,8 @@ global $wpdb;
35
 
36
  define('RELEVANSSI_PREMIUM', false);
37
 
 
 
38
  global $relevanssi_variables;
39
 
40
  $relevanssi_variables['relevanssi_table'] = $wpdb->prefix . "relevanssi";
3
  Plugin Name: Relevanssi
4
  Plugin URI: http://www.relevanssi.com/
5
  Description: This plugin replaces WordPress search with a relevance-sorting search.
6
+ Version: 3.5.11
7
  Author: Mikko Saari
8
  Author URI: http://www.mikkosaari.fi/
9
  */
35
 
36
  define('RELEVANSSI_PREMIUM', false);
37
 
38
+ add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'relevanssi_action_links');
39
+
40
  global $relevanssi_variables;
41
 
42
  $relevanssi_variables['relevanssi_table'] = $wpdb->prefix . "relevanssi";