Relevanssi – A Better Search - Version 4.0.11

Version Description

  • Home page links were getting the highlight parameter even though they shouldn't. This has been fixed.
  • Added support for WP JV Post Reading Groups.
  • Improved handling of HTML entities.
  • Events Made Easy Calendar shortcodes are now removed when building excerpts.
  • set_time_limit() was removed from the indexing; it's no longer necessary, and it can break the indexing on sites that don't allow the use of the function.
  • relevanssi_post_title_before_tokenize filter was moved a bit so that it's the last thing that runs before tokenizing.
  • Disabled shortcodes are handled better in the indexing: the shortcode names won't be indexed anymore like they were before.
  • Made sure there won't be a warning for non-numeric values when searching.
  • New filter: relevanssi_clean_excerpt lets you remove unwanted highlights from excerpts.
  • Highlighting works better with pre and code tags.
  • New filter: relevanssi_comment_author_to_index lets you filter comment author names before indexing.
  • relevanssi_comment_content_to_index doesn't include the comment author name anymore.
Download this release

Release Info

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

Code changes from version 4.0.10.1 to 4.0.11

lib/common.php CHANGED
@@ -180,7 +180,7 @@ function relevanssi_default_post_ok( $post_ok, $post_id ) {
180
  $current_user = wp_get_current_user();
181
  $post_ok = Groups_Post_Access::user_can_read_post( $post_id, $current_user->ID );
182
  }
183
- if ( class_exists( 'MeprUpdateCtrl' ) && MeprUpdateCtrl::is_activated() ) {
184
  // Memberpress.
185
  $post = get_post( $post_id );
186
  if ( MeprRule::is_locked( $post ) ) {
@@ -193,6 +193,10 @@ function relevanssi_default_post_ok( $post_ok, $post_id ) {
193
  $post = get_post( $post_id );
194
  $post_ok = $access_ctrl->can_i_read_post( $post );
195
  }
 
 
 
 
196
 
197
  /**
198
  * Filters statuses allowed in admin searches.
@@ -484,6 +488,7 @@ function relevanssi_remove_punct( $a ) {
484
  return '';
485
  }
486
 
 
487
  $a = preg_replace( '/<[^>]*>/', ' ', $a );
488
 
489
  $punct_options = get_option( 'relevanssi_punctuation' );
@@ -530,6 +535,7 @@ function relevanssi_remove_punct( $a ) {
530
  '€' => '',
531
  '®' => '',
532
  '©' => '',
 
533
  '&shy;' => '',
534
  '&nbsp;' => ' ',
535
  chr( 194 ) . chr( 160 ) => ' ',
@@ -1085,11 +1091,13 @@ function relevanssi_get_the_title( $post_id ) {
1085
  *
1086
  * @global object $wpdb The WordPress database interface.
1087
  * @global array $relevanssi_variables The Relevanssi global variable, used for table names.
 
1088
  */
1089
  function relevanssi_update_doc_count() {
1090
  global $wpdb, $relevanssi_variables;
1091
- $doc_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT(relevanssi.doc)) FROM ' . $relevanssi_variables['relevanssi_table'] . ' AS relevanssi' ); // WPCS: unprepared SQL ok, Relevanssi table name.
1092
  update_option( 'relevanssi_doc_count', $doc_count );
 
1093
  }
1094
 
1095
  /**
@@ -1174,20 +1182,31 @@ function relevanssi_switch_blog( $new_blog, $prev_blog ) {
1174
  * @global object $post The global post object.
1175
  *
1176
  * @param string $permalink The link to patch.
 
 
1177
  *
1178
  * @return string The link with the parameter added.
1179
  */
1180
- function relevanssi_add_highlight( $permalink ) {
1181
  $highlight_docs = get_option( 'relevanssi_highlight_docs' );
1182
  $query = get_search_query();
1183
  if ( isset( $highlight_docs ) && 'off' !== $highlight_docs && ! empty( $query ) ) {
1184
- global $post;
1185
- $frontpage_id = get_option( 'page_on_front' );
1186
  // We won't add the highlight parameter for the front page, as that will break the link.
1187
- if ( is_object( $post ) && $post->ID !== $frontpage_id ) {
 
 
 
 
 
 
 
 
 
 
 
1188
  $query = str_replace( '&quot;', '"', $query );
1189
- $permalink = esc_attr( add_query_arg( array( 'highlight' => rawurlencode( $query ) ), $permalink )
1190
- );
1191
  }
1192
  }
1193
  return $permalink;
@@ -1218,7 +1237,7 @@ function relevanssi_get_permalink() {
1218
  * if necessary using relevanssi_add_highlight(), then echoes it out.
1219
  */
1220
  function relevanssi_the_permalink() {
1221
- echo esc_attr( relevanssi_get_permalink() );
1222
  }
1223
 
1224
  /**
@@ -1251,7 +1270,7 @@ function relevanssi_permalink( $link, $link_post = null ) {
1251
  }
1252
 
1253
  if ( is_search() ) {
1254
- $link = relevanssi_add_highlight( $link );
1255
  }
1256
  return $link;
1257
  }
180
  $current_user = wp_get_current_user();
181
  $post_ok = Groups_Post_Access::user_can_read_post( $post_id, $current_user->ID );
182
  }
183
+ if ( class_exists( 'MeprUpdateCtrl', false ) && MeprUpdateCtrl::is_activated() ) { // False, because class_exists() can be really slow sometimes otherwise.
184
  // Memberpress.
185
  $post = get_post( $post_id );
186
  if ( MeprRule::is_locked( $post ) ) {
193
  $post = get_post( $post_id );
194
  $post_ok = $access_ctrl->can_i_read_post( $post );
195
  }
196
+ if ( function_exists( 'wp_jv_prg_user_can_see_a_post' ) ) {
197
+ // WP JV Post Reading Groups.
198
+ $post_ok = wp_jv_prg_user_can_see_a_post( get_current_user_id(), $post_id );
199
+ }
200
 
201
  /**
202
  * Filters statuses allowed in admin searches.
488
  return '';
489
  }
490
 
491
+ $a = html_entity_decode( $a, ENT_QUOTES );
492
  $a = preg_replace( '/<[^>]*>/', ' ', $a );
493
 
494
  $punct_options = get_option( 'relevanssi_punctuation' );
535
  '€' => '',
536
  '®' => '',
537
  '©' => '',
538
+ '™' => '',
539
  '&shy;' => '',
540
  '&nbsp;' => ' ',
541
  chr( 194 ) . chr( 160 ) => ' ',
1091
  *
1092
  * @global object $wpdb The WordPress database interface.
1093
  * @global array $relevanssi_variables The Relevanssi global variable, used for table names.
1094
+ * @return int The doc count.
1095
  */
1096
  function relevanssi_update_doc_count() {
1097
  global $wpdb, $relevanssi_variables;
1098
+ $doc_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT(doc)) FROM ' . $relevanssi_variables['relevanssi_table'] ); // WPCS: unprepared SQL ok, Relevanssi table name.
1099
  update_option( 'relevanssi_doc_count', $doc_count );
1100
+ return $doc_count;
1101
  }
1102
 
1103
  /**
1182
  * @global object $post The global post object.
1183
  *
1184
  * @param string $permalink The link to patch.
1185
+ * @param object $link_post The post object for the current link, global $post if
1186
+ * the parameter is set to null. Default null.
1187
  *
1188
  * @return string The link with the parameter added.
1189
  */
1190
+ function relevanssi_add_highlight( $permalink, $link_post = null ) {
1191
  $highlight_docs = get_option( 'relevanssi_highlight_docs' );
1192
  $query = get_search_query();
1193
  if ( isset( $highlight_docs ) && 'off' !== $highlight_docs && ! empty( $query ) ) {
1194
+ $frontpage_id = intval( get_option( 'page_on_front' ) );
 
1195
  // We won't add the highlight parameter for the front page, as that will break the link.
1196
+ $front_page = false;
1197
+ if ( is_object( $link_post ) ) {
1198
+ if ( $link_post->ID === $frontpage_id ) {
1199
+ $front_page = true;
1200
+ }
1201
+ } else {
1202
+ global $post;
1203
+ if ( is_object( $post ) && $post->ID === $frontpage_id ) {
1204
+ $front_page = true;
1205
+ }
1206
+ }
1207
+ if ( ! $front_page ) {
1208
  $query = str_replace( '&quot;', '"', $query );
1209
+ $permalink = esc_attr( add_query_arg( array( 'highlight' => rawurlencode( $query ) ), $permalink ) );
 
1210
  }
1211
  }
1212
  return $permalink;
1237
  * if necessary using relevanssi_add_highlight(), then echoes it out.
1238
  */
1239
  function relevanssi_the_permalink() {
1240
+ echo esc_url( relevanssi_get_permalink() );
1241
  }
1242
 
1243
  /**
1270
  }
1271
 
1272
  if ( is_search() ) {
1273
+ $link = relevanssi_add_highlight( $link, $link_post );
1274
  }
1275
  return $link;
1276
  }
lib/compatibility/wpml.php CHANGED
@@ -73,7 +73,12 @@ function relevanssi_wpml_filter( $data ) {
73
  }
74
  }
75
 
76
- return array( $filtered_hits, $data[1] );
 
 
 
 
 
77
  }
78
 
79
  return $data;
73
  }
74
  }
75
 
76
+ // A bit of foolproofing, avoid a warning if someone passes this filter bad data.
77
+ $query = '';
78
+ if ( isset( $data[1] ) ) {
79
+ $query = $data[1];
80
+ }
81
+ return array( $filtered_hits, $query );
82
  }
83
 
84
  return $data;
lib/excerpts-highlights.php CHANGED
@@ -535,8 +535,8 @@ function relevanssi_highlight_terms( $content, $query, $in_docs = false ) {
535
  }
536
  }
537
 
538
- if ( preg_match_all( '/<(style|script|object|embed)>.*<\/(style|script|object|embed)>/U', $content, $matches ) > 0 ) {
539
- // Remove highlights in style, object, embed and script tags.
540
  foreach ( $matches as $match ) {
541
  $new_match = str_replace( $start_emp_token, '', $match );
542
  $new_match = str_replace( $end_emp_token, '', $new_match );
@@ -548,6 +548,21 @@ function relevanssi_highlight_terms( $content, $query, $in_docs = false ) {
548
  $content = relevanssi_remove_nested_highlights( $content, $start_emp_token, $end_emp_token );
549
  $content = relevanssi_fix_entities( $content, $in_docs );
550
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
  $content = str_replace( $start_emp_token, $start_emp, $content );
552
  $content = str_replace( $end_emp_token, $end_emp, $content );
553
  $content = str_replace( $end_emp . $start_emp, '', $content );
@@ -625,8 +640,8 @@ function relevanssi_fix_entities( $excerpt, $in_docs ) {
625
  // Running htmlentities() for whole posts tends to ruin things.
626
  // However, we want to run htmlentities() for anything inside
627
  // <pre> and <code> tags.
628
- $excerpt = relevanssi_entities_inside( $excerpt, 'code' );
629
  $excerpt = relevanssi_entities_inside( $excerpt, 'pre' );
 
630
  }
631
  return $excerpt;
632
  }
@@ -641,18 +656,18 @@ function relevanssi_fix_entities( $excerpt, $in_docs ) {
641
  * ran through htmlentities().
642
  */
643
  function relevanssi_entities_inside( $content, $tag ) {
644
- $hits = preg_match_all( '/<' . $tag . '>(.*?)<\/' . $tag . '>/im', $content, $matches );
645
  if ( $hits > 0 ) {
646
  $replacements = array();
647
  foreach ( $matches[1] as $match ) {
648
  if ( ! empty( $match ) ) {
649
- $replacements[] = '<xxx' . $tag . '>' . htmlentities( $match, ENT_QUOTES, 'UTF-8' ) . '</xxx' . $tag . '>';
650
  }
651
  }
652
  if ( ! empty( $replacements ) ) {
653
  $count_replacements = count( $replacements );
654
  for ( $i = 0; $i < $count_replacements; $i++ ) {
655
- $patterns[] = '/<' . $tag . '>(.*?)<\/' . $tag . '>/im';
656
  }
657
  $content = preg_replace( $patterns, $replacements, $content, 1 );
658
  }
@@ -1059,6 +1074,8 @@ function relevanssi_remove_page_builder_shortcodes( $content ) {
1059
  '/\[maxmegamenu.*?\]/',
1060
  // All-in-one Events Calendar shortcode doesn't look good.
1061
  '/\[ai1ec.*?\]/',
 
 
1062
  ));
1063
  $content = preg_replace( $search_array, '', $content );
1064
  return $content;
535
  }
536
  }
537
 
538
+ if ( preg_match_all( '/<(style|script|object|embed|pre|code)>.*<\/(style|script|object|embed|pre|code)>/U', $content, $matches ) > 0 ) {
539
+ // Remove highlights in style, object, embed, script and pre tags.
540
  foreach ( $matches as $match ) {
541
  $new_match = str_replace( $start_emp_token, '', $match );
542
  $new_match = str_replace( $end_emp_token, '', $new_match );
548
  $content = relevanssi_remove_nested_highlights( $content, $start_emp_token, $end_emp_token );
549
  $content = relevanssi_fix_entities( $content, $in_docs );
550
 
551
+ /**
552
+ * Allows cleaning unwanted highlights.
553
+ *
554
+ * This filter lets you clean unwanted highlights, for example from within <pre>
555
+ * tags. To remove a highlight, remove the matching starting and ending tokens
556
+ * from the $content string.
557
+ *
558
+ * @param string $content The highlighted content.
559
+ * @param string $start_emp_token A token that signifies the start of a highlight.
560
+ * @param string $end_emp_token A token that signifies the end of a highlight.
561
+ *
562
+ * @return string The highlighted content.
563
+ */
564
+ $content = apply_filters( 'relevanssi_clean_excerpt', $content, $start_emp_token, $end_emp_token );
565
+
566
  $content = str_replace( $start_emp_token, $start_emp, $content );
567
  $content = str_replace( $end_emp_token, $end_emp, $content );
568
  $content = str_replace( $end_emp . $start_emp, '', $content );
640
  // Running htmlentities() for whole posts tends to ruin things.
641
  // However, we want to run htmlentities() for anything inside
642
  // <pre> and <code> tags.
 
643
  $excerpt = relevanssi_entities_inside( $excerpt, 'pre' );
644
+ $excerpt = relevanssi_entities_inside( $excerpt, 'code' );
645
  }
646
  return $excerpt;
647
  }
656
  * ran through htmlentities().
657
  */
658
  function relevanssi_entities_inside( $content, $tag ) {
659
+ $hits = preg_match_all( '/<' . $tag . '.*?>(.*?)<\/' . $tag . '>/ims', $content, $matches );
660
  if ( $hits > 0 ) {
661
  $replacements = array();
662
  foreach ( $matches[1] as $match ) {
663
  if ( ! empty( $match ) ) {
664
+ $replacements[] = '<xxx' . $tag . '\1>' . htmlentities( $match, ENT_QUOTES, 'UTF-8' ) . '</xxx' . $tag . '>';
665
  }
666
  }
667
  if ( ! empty( $replacements ) ) {
668
  $count_replacements = count( $replacements );
669
  for ( $i = 0; $i < $count_replacements; $i++ ) {
670
+ $patterns[] = '/<' . $tag . '(.*?)>(.*?)<\/' . $tag . '>/ims';
671
  }
672
  $content = preg_replace( $patterns, $replacements, $content, 1 );
673
  }
1074
  '/\[maxmegamenu.*?\]/',
1075
  // All-in-one Events Calendar shortcode doesn't look good.
1076
  '/\[ai1ec.*?\]/',
1077
+ // Events Made Easy Calendar shortcodes should be removed.
1078
+ '/\[eme_.*?\]/',
1079
  ));
1080
  $content = preg_replace( $search_array, '', $content );
1081
  return $content;
lib/indexing.php CHANGED
@@ -247,7 +247,6 @@ function relevanssi_build_index( $extend_offset = false, $verbose = true, $post_
247
 
248
  // Thanks to Julien Mession. This speeds up indexing a lot.
249
  wp_suspend_cache_addition( true );
250
- set_time_limit( 0 );
251
 
252
  // The values generated by these functions are safe to use for MySQL.
253
  $restriction = relevanssi_post_type_restriction();
@@ -367,8 +366,7 @@ function relevanssi_build_index( $extend_offset = false, $verbose = true, $post_
367
  update_option( 'relevanssi_indexed', 'done' );
368
 
369
  // Update the document count variable.
370
- $document_count = $wpdb->get_var( "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi" ); // WPCS: unprepared SQL ok, Relevanssi table name.
371
- update_option( 'relevanssi_doc_count', $document_count );
372
 
373
  wp_suspend_cache_addition( false );
374
 
@@ -729,15 +727,15 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
729
  if ( $debug ) {
730
  relevanssi_debug_echo( 'Indexing post title.' );
731
  }
 
 
732
  /**
733
  * Filters the title before tokenizing and indexing.
734
  *
735
  * @param string $post->post_title The title.
736
  * @param object $post The full post object.
737
  */
738
- $filtered_title = apply_filters( 'relevanssi_post_title_before_tokenize', $post->post_title, $post );
739
- /** This filter is documented in wp-includes/post-template.php */
740
- $filtered_title = apply_filters( 'the_title', $filtered_title, $post->ID );
741
  /**
742
  * Filters whether stopwords should be removed from titles in tokenizing or not.
743
  *
@@ -746,7 +744,7 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
746
  $title_tokens = relevanssi_tokenize( $filtered_title, apply_filters( 'relevanssi_remove_stopwords_in_titles', true ), $min_word_length );
747
 
748
  if ( $debug ) {
749
- relevanssi_debug_echo( "\tTitle, tokenized: " . implode( ' ', array_keys( $titles ) ) );
750
  }
751
 
752
  if ( count( $title_tokens ) > 0 ) {
@@ -820,54 +818,62 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
820
  $my_tablepress_controller->init_shortcodes();
821
  }
822
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
  $disable_shortcodes = get_option( 'relevanssi_disable_shortcodes' );
824
  $shortcodes = explode( ',', $disable_shortcodes );
 
825
  foreach ( $shortcodes as $shortcode ) {
 
 
 
826
  remove_shortcode( trim( $shortcode ) );
 
827
  }
828
- remove_shortcode( 'contact-form' ); // Jetpack Contact Form causes an error message.
829
- remove_shortcode( 'starrater' ); // GD Star Rating rater shortcode causes problems.
830
- remove_shortcode( 'responsive-flipbook' ); // Responsive Flipbook causes problems.
831
- remove_shortcode( 'avatar_upload' ); // WP User Avatar is incompatible.
832
- remove_shortcode( 'product_categories' ); // A problematic WooCommerce shortcode.
833
- remove_shortcode( 'recent_products' ); // A problematic WooCommerce shortcode.
834
- remove_shortcode( 'php' ); // PHP Code for Posts.
835
- remove_shortcode( 'watupro' ); // Watu PRO doesn't co-operate.
836
- remove_shortcode( 'starbox' ); // Starbox shortcode breaks Relevanssi.
837
- remove_shortcode( 'cfdb-save-form-post' ); // Contact Form DB.
838
- remove_shortcode( 'cfdb-datatable' );
839
- remove_shortcode( 'cfdb-table' );
840
- remove_shortcode( 'cfdb-json' );
841
- remove_shortcode( 'cfdb-value' );
842
- remove_shortcode( 'cfdb-count' );
843
- remove_shortcode( 'cfdb-html' );
844
- remove_shortcode( 'woocommerce_cart' ); // WooCommerce.
845
- remove_shortcode( 'woocommerce_checkout' );
846
- remove_shortcode( 'woocommerce_order_tracking' );
847
- remove_shortcode( 'woocommerce_my_account' );
848
- remove_shortcode( 'woocommerce_edit_account' );
849
- remove_shortcode( 'woocommerce_change_password' );
850
- remove_shortcode( 'woocommerce_view_order' );
851
- remove_shortcode( 'woocommerce_logout' );
852
- remove_shortcode( 'woocommerce_pay' );
853
- remove_shortcode( 'woocommerce_thankyou' );
854
- remove_shortcode( 'woocommerce_lost_password' );
855
- remove_shortcode( 'woocommerce_edit_address' );
856
- remove_shortcode( 'tc_process_payment' );
857
- remove_shortcode( 'maxmegamenu' ); // Max Mega Menu.
858
- remove_shortcode( 'searchandfilter' ); // Search and Filter.
859
- remove_shortcode( 'downloads' ); // Easy Digital Downloads.
860
- remove_shortcode( 'download_history' );
861
- remove_shortcode( 'purchase_history' );
862
- remove_shortcode( 'download_checkout' );
863
- remove_shortcode( 'purchase_link' );
864
- remove_shortcode( 'download_cart' );
865
- remove_shortcode( 'edd_profile_editor' );
866
- remove_shortcode( 'edd_login' );
867
- remove_shortcode( 'edd_register' );
868
- remove_shortcode( 'swpm_protected' ); // Simple Membership Partially Protected content.
869
- remove_shortcode( 'gravityform' ); // Gravity Forms.
870
- remove_shortcode( 'sdm_latest_downloads' ); // SDM Simple Download Monitor.
871
 
872
  $post_before_shortcode = $post;
873
  $contents = do_shortcode( $contents );
@@ -907,7 +913,6 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
907
  */
908
  $contents = apply_filters( 'relevanssi_post_content_before_tokenize', $contents, $post );
909
  $content_tokens = relevanssi_tokenize( $contents, true, $min_word_length );
910
-
911
  if ( $debug ) {
912
  relevanssi_debug_echo( "\tContent, tokenized:\n" . implode( ' ', array_keys( $content_tokens ) ) );
913
  }
@@ -1331,13 +1336,20 @@ function relevanssi_get_comments( $post_id ) {
1331
  break;
1332
  }
1333
  foreach ( $comments as $comment ) {
 
 
 
 
 
 
 
1334
  /**
1335
  * Filters the comment content before indexing.
1336
  *
1337
- * @param string Comment author, a space, the comment content.
1338
  * @param int The comment ID.
1339
  */
1340
- $comment_string .= apply_filters( 'relevanssi_comment_content_to_index', $comment->comment_author . ' ' . $comment->comment_content . ' ', $comment->comment_ID );
1341
  }
1342
  $offset += $limit;
1343
  }
247
 
248
  // Thanks to Julien Mession. This speeds up indexing a lot.
249
  wp_suspend_cache_addition( true );
 
250
 
251
  // The values generated by these functions are safe to use for MySQL.
252
  $restriction = relevanssi_post_type_restriction();
366
  update_option( 'relevanssi_indexed', 'done' );
367
 
368
  // Update the document count variable.
369
+ relevanssi_update_doc_count();
 
370
 
371
  wp_suspend_cache_addition( false );
372
 
727
  if ( $debug ) {
728
  relevanssi_debug_echo( 'Indexing post title.' );
729
  }
730
+ /** This filter is documented in wp-includes/post-template.php */
731
+ $filtered_title = apply_filters( 'the_title', $post->post_title, $post->ID );
732
  /**
733
  * Filters the title before tokenizing and indexing.
734
  *
735
  * @param string $post->post_title The title.
736
  * @param object $post The full post object.
737
  */
738
+ $filtered_title = apply_filters( 'relevanssi_post_title_before_tokenize', $filtered_title, $post );
 
 
739
  /**
740
  * Filters whether stopwords should be removed from titles in tokenizing or not.
741
  *
744
  $title_tokens = relevanssi_tokenize( $filtered_title, apply_filters( 'relevanssi_remove_stopwords_in_titles', true ), $min_word_length );
745
 
746
  if ( $debug ) {
747
+ relevanssi_debug_echo( "\tTitle, tokenized: " . implode( ' ', array_keys( $title_tokens ) ) );
748
  }
749
 
750
  if ( count( $title_tokens ) > 0 ) {
818
  $my_tablepress_controller->init_shortcodes();
819
  }
820
 
821
+ $default_disables = array(
822
+ 'contact-form', // Jetpack Contact Form causes an error message.
823
+ 'starrater', // GD Star Rating rater shortcode causes problems.
824
+ 'responsive-flipbook', // Responsive Flipbook causes problems.
825
+ 'avatar_upload', // WP User Avatar is incompatible.
826
+ 'product_categories', // A problematic WooCommerce shortcode.
827
+ 'recent_products', // A problematic WooCommerce shortcode.
828
+ 'php', // PHP Code for Posts.
829
+ 'watupro', // Watu PRO doesn't co-operate.
830
+ 'starbox', // Starbox shortcode breaks Relevanssi.
831
+ 'cfdb-save-form-post', // Contact Form DB.
832
+ 'cfdb-datatable',
833
+ 'cfdb-table',
834
+ 'cfdb-json',
835
+ 'cfdb-value',
836
+ 'cfdb-count',
837
+ 'cfdb-html',
838
+ 'woocommerce_cart', // WooCommerce.
839
+ 'woocommerce_checkout',
840
+ 'woocommerce_order_tracking',
841
+ 'woocommerce_my_account',
842
+ 'woocommerce_edit_account',
843
+ 'woocommerce_change_password',
844
+ 'woocommerce_view_order',
845
+ 'woocommerce_logout',
846
+ 'woocommerce_pay',
847
+ 'woocommerce_thankyou',
848
+ 'woocommerce_lost_password',
849
+ 'woocommerce_edit_address',
850
+ 'tc_process_payment',
851
+ 'maxmegamenu', // Max Mega Menu.
852
+ 'searchandfilter', // Search and Filter.
853
+ 'downloads', // Easy Digital Downloads.
854
+ 'download_history',
855
+ 'purchase_history',
856
+ 'download_checkout',
857
+ 'purchase_link',
858
+ 'download_cart',
859
+ 'edd_profile_editor',
860
+ 'edd_login',
861
+ 'edd_register',
862
+ 'swpm_protected', // Simple Membership Partially Protected content.
863
+ 'gravityform', // Gravity Forms.
864
+ 'sdm_latest_downloads', // SDM Simple Download Monitor.
865
+ );
866
+
867
  $disable_shortcodes = get_option( 'relevanssi_disable_shortcodes' );
868
  $shortcodes = explode( ',', $disable_shortcodes );
869
+ $shortcodes = array_unique( array_merge( $shortcodes, $default_disables ) );
870
  foreach ( $shortcodes as $shortcode ) {
871
+ if ( empty( $shortcode ) ) {
872
+ continue;
873
+ }
874
  remove_shortcode( trim( $shortcode ) );
875
+ add_shortcode( $shortcode, '__return_empty_string' );
876
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
877
 
878
  $post_before_shortcode = $post;
879
  $contents = do_shortcode( $contents );
913
  */
914
  $contents = apply_filters( 'relevanssi_post_content_before_tokenize', $contents, $post );
915
  $content_tokens = relevanssi_tokenize( $contents, true, $min_word_length );
 
916
  if ( $debug ) {
917
  relevanssi_debug_echo( "\tContent, tokenized:\n" . implode( ' ', array_keys( $content_tokens ) ) );
918
  }
1336
  break;
1337
  }
1338
  foreach ( $comments as $comment ) {
1339
+ /**
1340
+ * Filters the comment author before indexing.
1341
+ *
1342
+ * @param string Comment author display name.
1343
+ * @param int The comment ID.
1344
+ */
1345
+ $comment_string .= ' ' . apply_filters( 'relevanssi_comment_author_to_index', $comment->comment_author, $comment->comment_ID );
1346
  /**
1347
  * Filters the comment content before indexing.
1348
  *
1349
+ * @param string Comment content.
1350
  * @param int The comment ID.
1351
  */
1352
+ $comment_string .= ' ' . apply_filters( 'relevanssi_comment_content_to_index', $comment->comment_content, $comment->comment_ID );
1353
  }
1354
  $offset += $limit;
1355
  }
lib/init.php CHANGED
@@ -142,7 +142,7 @@ function relevanssi_init() {
142
  function relevanssi_admin_init() {
143
  global $relevanssi_variables;
144
 
145
- require $relevanssi_variables['plugin_dir'] . 'lib/admin-ajax.php';
146
 
147
  add_action( 'admin_enqueue_scripts', 'relevanssi_add_admin_scripts' );
148
  add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'relevanssi_action_links' );
142
  function relevanssi_admin_init() {
143
  global $relevanssi_variables;
144
 
145
+ require_once $relevanssi_variables['plugin_dir'] . 'lib/admin-ajax.php';
146
 
147
  add_action( 'admin_enqueue_scripts', 'relevanssi_add_admin_scripts' );
148
  add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'relevanssi_action_links' );
lib/interface.php CHANGED
@@ -229,6 +229,9 @@ function update_relevanssi_options() {
229
  $index_taxonomies_list = array();
230
  $index_terms_list = array();
231
  foreach ( $_REQUEST as $key => $value ) {
 
 
 
232
  if ( 'relevanssi_weight_' === substr( $key, 0, strlen( 'relevanssi_weight_' ) ) ) {
233
  $type = substr( $key, strlen( 'relevanssi_weight_' ) );
234
  $post_type_weights[ $type ] = $value;
@@ -726,7 +729,7 @@ function relevanssi_options_form() {
726
  <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=excerpts" class="nav-tab <?php echo 'excerpts' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Excerpts and highlights', 'relevanssi' ); ?></a>
727
  <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=synonyms" class="nav-tab <?php echo 'synonyms' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Synonyms', 'relevanssi' ); ?></a>
728
  <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=stopwords" class="nav-tab <?php echo 'stopwords' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Stopwords', 'relevanssi' ); ?></a>
729
- <?php if ( function_exists( 'relevanssi_form_importexport' ) ) : ?>
730
  <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=importexport" class="nav-tab <?php echo 'importexport' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Import / Export options', 'relevanssi' ); ?></a>
731
  <?php endif; ?>
732
  </h2>
229
  $index_taxonomies_list = array();
230
  $index_terms_list = array();
231
  foreach ( $_REQUEST as $key => $value ) {
232
+ if ( empty( $value ) ) {
233
+ $value = 0;
234
+ }
235
  if ( 'relevanssi_weight_' === substr( $key, 0, strlen( 'relevanssi_weight_' ) ) ) {
236
  $type = substr( $key, strlen( 'relevanssi_weight_' ) );
237
  $post_type_weights[ $type ] = $value;
729
  <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=excerpts" class="nav-tab <?php echo 'excerpts' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Excerpts and highlights', 'relevanssi' ); ?></a>
730
  <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=synonyms" class="nav-tab <?php echo 'synonyms' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Synonyms', 'relevanssi' ); ?></a>
731
  <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=stopwords" class="nav-tab <?php echo 'stopwords' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Stopwords', 'relevanssi' ); ?></a>
732
+ <?php if ( RELEVANSSI_PREMIUM ) : ?>
733
  <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=importexport" class="nav-tab <?php echo 'importexport' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Import / Export options', 'relevanssi' ); ?></a>
734
  <?php endif; ?>
735
  </h2>
lib/search.php CHANGED
@@ -399,8 +399,7 @@ function relevanssi_search( $args ) {
399
  // Go get the count from the options, but run the full query if it's not available.
400
  $doc_count = get_option( 'relevanssi_doc_count' );
401
  if ( ! $doc_count || $doc_count < 1 ) {
402
- $doc_count = $wpdb->get_var( "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi" ); // WPCS: unprepared SQL ok, Relevanssi table name.
403
- update_option( 'relevanssi_doc_count', $doc_count );
404
  }
405
 
406
  $total_hits = 0;
@@ -619,7 +618,6 @@ function relevanssi_search( $args ) {
619
  if ( ! empty( $post_type_weights['category'] ) ) {
620
  $cat = $post_type_weights['category'];
621
  }
622
-
623
  $include_these_posts = array();
624
  $df_counts = array();
625
 
@@ -728,14 +726,13 @@ function relevanssi_search( $args ) {
728
  if ( ! empty( $match->taxonomy_detail ) ) {
729
  relevanssi_taxonomy_score( $match, $post_type_weights );
730
  } else {
731
- // This shouldn't really happen, but let's still have a backup.
732
  $tag_weight = 1;
733
- if ( isset( $post_type_weights['post_tag'] ) ) {
734
  $tag_weight = $post_type_weights['post_tag'];
735
  }
736
 
737
  $category_weight = 1;
738
- if ( isset( $post_type_weights['category'] ) ) {
739
  $category_weight = $post_type_weights['category'];
740
  }
741
 
@@ -1657,20 +1654,21 @@ function relevanssi_do_query( &$query ) {
1657
  // Manipulating the array with array_unique() for example may mess with that.
1658
  $hits = array_values( $hits_filters_applied[0] );
1659
 
1660
- $query->found_posts = count( $hits );
 
1661
  if ( ! isset( $query->query_vars['posts_per_page'] ) || 0 === $query->query_vars['posts_per_page'] ) {
1662
  // Assume something sensible to prevent "division by zero error".
1663
  $query->query_vars['posts_per_page'] = -1;
1664
  }
1665
  if ( -1 === $query->query_vars['posts_per_page'] ) {
1666
- $query->max_num_pages = count( $hits );
1667
  } else {
1668
- $query->max_num_pages = ceil( count( $hits ) / $query->query_vars['posts_per_page'] );
1669
  }
1670
 
1671
  $update_log = get_option( 'relevanssi_log_queries' );
1672
  if ( 'on' === $update_log ) {
1673
- relevanssi_update_log( $q, count( $hits ) );
1674
  }
1675
 
1676
  $make_excerpts = get_option( 'relevanssi_excerpts' );
@@ -1685,7 +1683,7 @@ function relevanssi_do_query( &$query ) {
1685
  }
1686
 
1687
  if ( ! isset( $query->query_vars['posts_per_page'] ) || -1 === $query->query_vars['posts_per_page'] ) {
1688
- $search_high_boundary = count( $hits );
1689
  } else {
1690
  $search_high_boundary = $search_low_boundary + $query->query_vars['posts_per_page'] - 1;
1691
  }
@@ -1695,8 +1693,8 @@ function relevanssi_do_query( &$query ) {
1695
  $search_low_boundary += $query->query_vars['offset'];
1696
  }
1697
 
1698
- if ( $search_high_boundary > count( $hits ) ) {
1699
- $search_high_boundary = count( $hits );
1700
  }
1701
 
1702
  for ( $i = $search_low_boundary; $i <= $search_high_boundary; $i++ ) {
@@ -1729,7 +1727,6 @@ function relevanssi_do_query( &$query ) {
1729
  $post->original_excerpt = $post->post_excerpt;
1730
  $post->post_excerpt = relevanssi_do_excerpt( $post, $q );
1731
  }
1732
-
1733
  if ( 'on' === get_option( 'relevanssi_show_matches' ) && empty( $fields ) ) {
1734
  $post_id = $post->ID;
1735
  if ( 'user' === $post->post_type ) {
@@ -2007,9 +2004,11 @@ function relevanssi_process_tax_query_row( $row, $is_sub_row, $global_relation,
2007
  }
2008
  $kids = get_term_children( $t_id, $row['taxonomy'] );
2009
  foreach ( $kids as $kid ) {
2010
- $term = get_term_by( 'id', $kid, $row['taxonomy'] );
2011
  $kid_term_tax_id = relevanssi_get_term_tax_id( $kid, $row['taxonomy'] );
2012
- $term_tax_id[] = $kid_term_tax_id;
 
 
 
2013
  }
2014
  }
2015
  }
399
  // Go get the count from the options, but run the full query if it's not available.
400
  $doc_count = get_option( 'relevanssi_doc_count' );
401
  if ( ! $doc_count || $doc_count < 1 ) {
402
+ $doc_count = relevanssi_update_doc_count();
 
403
  }
404
 
405
  $total_hits = 0;
618
  if ( ! empty( $post_type_weights['category'] ) ) {
619
  $cat = $post_type_weights['category'];
620
  }
 
621
  $include_these_posts = array();
622
  $df_counts = array();
623
 
726
  if ( ! empty( $match->taxonomy_detail ) ) {
727
  relevanssi_taxonomy_score( $match, $post_type_weights );
728
  } else {
 
729
  $tag_weight = 1;
730
+ if ( isset( $post_type_weights['post_tag'] ) && is_numeric( $post_type_weights['post_tag'] ) ) {
731
  $tag_weight = $post_type_weights['post_tag'];
732
  }
733
 
734
  $category_weight = 1;
735
+ if ( isset( $post_type_weights['category'] ) && is_numeric( $post_type_weights['category'] ) ) {
736
  $category_weight = $post_type_weights['category'];
737
  }
738
 
1654
  // Manipulating the array with array_unique() for example may mess with that.
1655
  $hits = array_values( $hits_filters_applied[0] );
1656
 
1657
+ $hits_count = count( $hits );
1658
+ $query->found_posts = $hits_count;
1659
  if ( ! isset( $query->query_vars['posts_per_page'] ) || 0 === $query->query_vars['posts_per_page'] ) {
1660
  // Assume something sensible to prevent "division by zero error".
1661
  $query->query_vars['posts_per_page'] = -1;
1662
  }
1663
  if ( -1 === $query->query_vars['posts_per_page'] ) {
1664
+ $query->max_num_pages = $hits_count;
1665
  } else {
1666
+ $query->max_num_pages = ceil( $hits_count / $query->query_vars['posts_per_page'] );
1667
  }
1668
 
1669
  $update_log = get_option( 'relevanssi_log_queries' );
1670
  if ( 'on' === $update_log ) {
1671
+ relevanssi_update_log( $q, $hits_count );
1672
  }
1673
 
1674
  $make_excerpts = get_option( 'relevanssi_excerpts' );
1683
  }
1684
 
1685
  if ( ! isset( $query->query_vars['posts_per_page'] ) || -1 === $query->query_vars['posts_per_page'] ) {
1686
+ $search_high_boundary = $hits_count;
1687
  } else {
1688
  $search_high_boundary = $search_low_boundary + $query->query_vars['posts_per_page'] - 1;
1689
  }
1693
  $search_low_boundary += $query->query_vars['offset'];
1694
  }
1695
 
1696
+ if ( $search_high_boundary > $hits_count ) {
1697
+ $search_high_boundary = $hits_count;
1698
  }
1699
 
1700
  for ( $i = $search_low_boundary; $i <= $search_high_boundary; $i++ ) {
1727
  $post->original_excerpt = $post->post_excerpt;
1728
  $post->post_excerpt = relevanssi_do_excerpt( $post, $q );
1729
  }
 
1730
  if ( 'on' === get_option( 'relevanssi_show_matches' ) && empty( $fields ) ) {
1731
  $post_id = $post->ID;
1732
  if ( 'user' === $post->post_type ) {
2004
  }
2005
  $kids = get_term_children( $t_id, $row['taxonomy'] );
2006
  foreach ( $kids as $kid ) {
 
2007
  $kid_term_tax_id = relevanssi_get_term_tax_id( $kid, $row['taxonomy'] );
2008
+ if ( $kid_term_tax_id ) {
2009
+ // In some weird cases, this may be null. See: https://wordpress.org/support/topic/childrens-of-chosen-product_cat-not-showing-up/.
2010
+ $term_tax_id[] = $kid_term_tax_id;
2011
+ }
2012
  }
2013
  }
2014
  }
lib/tabs/overview-tab.php CHANGED
@@ -79,6 +79,14 @@ function relevanssi_overview_tab() {
79
  <p><?php printf( esc_html__( '%1$sRelevanssi knowledge base%2$s has lots of information about advanced Relevanssi use, including plenty of code samples.', 'relevanssi' ), "<a href='https://www.relevanssi.com/knowledge-base/'>", '</a>' ); ?></p>
80
  </td>
81
  </tr>
 
 
 
 
 
 
 
 
82
  <tr>
83
  <th scope="row">
84
  <?php esc_html_e( 'Relevanssi on Facebook', 'relevanssi' ); ?>
79
  <p><?php printf( esc_html__( '%1$sRelevanssi knowledge base%2$s has lots of information about advanced Relevanssi use, including plenty of code samples.', 'relevanssi' ), "<a href='https://www.relevanssi.com/knowledge-base/'>", '</a>' ); ?></p>
80
  </td>
81
  </tr>
82
+ <tr>
83
+ <th scope="row"><?php esc_html_e( 'Do you like Relevanssi?', 'relevanssi' ); ?></th>
84
+ <td>
85
+ <p><?php esc_html_e( 'If you do, the best way to show your appreciation is to spread the word and perhaps give us a good review on WordPress.org.', 'relevanssi' ); ?></p>
86
+ <?php // Translators: %1$s opens the link, %2$s closes the link. ?>
87
+ <p><?php printf( esc_html__( 'If you like Relevanssi, leaving a five-star review on WordPress.org will help others discover Relevanssi. %1$sYou can add your review here%2$s.', 'relevanssi' ), "<a href='https://wordpress.org/support/plugin/relevanssi/reviews/#new-post'>", '</a>' ); ?></p>
88
+ </td>
89
+ </tr>
90
  <tr>
91
  <th scope="row">
92
  <?php esc_html_e( 'Relevanssi on Facebook', 'relevanssi' ); ?>
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: search, relevance, better search
5
  Requires at least: 4.0
6
  Tested up to: 5.0
7
  Requires PHP: 5.6
8
- Stable tag: 4.0.10.1
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -44,7 +44,7 @@ Do note that using Relevanssi may require large amounts (hundreds of megabytes)
44
  * Disable indexing of post content and post titles with a simple filter hook.
45
 
46
  = Premium features (only in Relevanssi Premium) =
47
- * Indexing PDF content.
48
  * Improved spelling correction in "Did you mean?" suggestions.
49
  * Searching across multiple sites in the same multisite installation.
50
  * Search and index user profiles.
@@ -130,6 +130,20 @@ Each document database is full of useless words. All the little words that appea
130
 
131
  == Changelog ==
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  = 4.0.10.1 =
134
  * The privacy features caused an error notice with certain Relevanssi configurations, and the plugin required WP 4.9.6.
135
 
@@ -173,6 +187,9 @@ Each document database is full of useless words. All the little words that appea
173
 
174
  == Upgrade notice ==
175
 
 
 
 
176
  = 4.0.10.1 =
177
  * Privacy feature bug fix.
178
 
5
  Requires at least: 4.0
6
  Tested up to: 5.0
7
  Requires PHP: 5.6
8
+ Stable tag: 4.0.11
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
44
  * Disable indexing of post content and post titles with a simple filter hook.
45
 
46
  = Premium features (only in Relevanssi Premium) =
47
+ * Indexing attachment content (PDF, Office, Open Office).
48
  * Improved spelling correction in "Did you mean?" suggestions.
49
  * Searching across multiple sites in the same multisite installation.
50
  * Search and index user profiles.
130
 
131
  == Changelog ==
132
 
133
+ = 4.0.11 =
134
+ * Home page links were getting the highlight parameter even though they shouldn't. This has been fixed.
135
+ * Added support for WP JV Post Reading Groups.
136
+ * Improved handling of HTML entities.
137
+ * Events Made Easy Calendar shortcodes are now removed when building excerpts.
138
+ * `set_time_limit()` was removed from the indexing; it's no longer necessary, and it can break the indexing on sites that don't allow the use of the function.
139
+ * `relevanssi_post_title_before_tokenize` filter was moved a bit so that it's the last thing that runs before tokenizing.
140
+ * Disabled shortcodes are handled better in the indexing: the shortcode names won't be indexed anymore like they were before.
141
+ * Made sure there won't be a warning for non-numeric values when searching.
142
+ * New filter: `relevanssi_clean_excerpt` lets you remove unwanted highlights from excerpts.
143
+ * Highlighting works better with `pre` and `code` tags.
144
+ * New filter: `relevanssi_comment_author_to_index` lets you filter comment author names before indexing.
145
+ * `relevanssi_comment_content_to_index` doesn't include the comment author name anymore.
146
+
147
  = 4.0.10.1 =
148
  * The privacy features caused an error notice with certain Relevanssi configurations, and the plugin required WP 4.9.6.
149
 
187
 
188
  == Upgrade notice ==
189
 
190
+ = 4.0.11 =
191
+ * Several small improvements, new filters and highlighting fixes.
192
+
193
  = 4.0.10.1 =
194
  * Privacy feature bug fix.
195
 
relevanssi.php CHANGED
@@ -13,7 +13,7 @@
13
  * Plugin Name: Relevanssi
14
  * Plugin URI: https://www.relevanssi.com/
15
  * Description: This plugin replaces WordPress search with a relevance-sorting search.
16
- * Version: 4.0.10.1
17
  * Author: Mikko Saari
18
  * Author URI: http://www.mikkosaari.fi/
19
  * Text Domain: relevanssi
13
  * Plugin Name: Relevanssi
14
  * Plugin URI: https://www.relevanssi.com/
15
  * Description: This plugin replaces WordPress search with a relevance-sorting search.
16
+ * Version: 4.0.11
17
  * Author: Mikko Saari
18
  * Author URI: http://www.mikkosaari.fi/
19
  * Text Domain: relevanssi