Relevanssi – A Better Search - Version 4.13.1

Version Description

  • New feature: Adds compatibility for WP-Members plugin, preventing blocked posts from showing up in the search results.
  • New feature: New function relevanssi_get_attachment_suffix() can be used to return the attachment file suffix based on a post object or a post ID.
  • Minor fix: Improves the Oxygen compatibility. Now also the [oxygen] shortcode tags are removed.
Download this release

Release Info

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

Code changes from version 4.13.0 to 4.13.1

lib/common.php CHANGED
@@ -1667,3 +1667,41 @@ function relevanssi_replace_synonyms_in_terms( array $terms ) : array {
1667
  $terms
1668
  );
1669
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1667
  $terms
1668
  );
1669
  }
1670
+
1671
+ /**
1672
+ * Replaces stemmed words in an array with their original counterparts.
1673
+ *
1674
+ * @param array $terms An array of words where to replace.
1675
+ * @param array $all_terms An array of all words to stem. Default $terms.
1676
+ *
1677
+ * @return array An array of words with stemmed words replaced with their
1678
+ * originals.
1679
+ */
1680
+ function relevanssi_replace_stems_in_terms( array $terms, array $all_terms = null ) : array {
1681
+ if ( ! $all_terms ) {
1682
+ $all_terms = $terms;
1683
+ }
1684
+ $term_for_stem = array();
1685
+ foreach ( $all_terms as $term ) {
1686
+ $term_and_stem = relevanssi_tokenize( $term, false, -1 );
1687
+ foreach ( array_keys( $term_and_stem ) as $word ) {
1688
+ if ( $word === $term ) {
1689
+ continue;
1690
+ }
1691
+ $term_for_stem[ $word ] = $term;
1692
+ }
1693
+ }
1694
+
1695
+ if ( empty( $term_for_stem ) ) {
1696
+ return $terms;
1697
+ }
1698
+
1699
+ return array_unique(
1700
+ array_map(
1701
+ function ( $term ) use ( $term_for_stem ) {
1702
+ return $term_for_stem[ $term ] ?? $term;
1703
+ },
1704
+ $terms
1705
+ )
1706
+ );
1707
+ }
lib/compatibility/oxygen.php CHANGED
@@ -77,7 +77,10 @@ function relevanssi_oxygen_compatibility( $value, $field, $post_id ) {
77
  );
78
 
79
  $content = preg_replace(
80
- '/\[\/?ct_.*?\]/',
 
 
 
81
  ' ',
82
  /**
83
  * Filters the Oxygen Builder section content before the
@@ -97,11 +100,9 @@ function relevanssi_oxygen_compatibility( $value, $field, $post_id ) {
97
 
98
  $page_content .= $content;
99
  }
100
- if ( 'on' === get_option( 'relevanssi_expand_shortcodes' ) ) {
101
- $page_content = do_shortcode( $page_content );
102
- } else {
103
- $page_content = strip_shortcodes( $page_content );
104
- }
105
  $value[0] = $page_content;
106
  }
107
  return $value;
77
  );
78
 
79
  $content = preg_replace(
80
+ array(
81
+ '/\[oxygen.*?\]/',
82
+ '/\[\/?ct_.*?\]/',
83
+ ),
84
  ' ',
85
  /**
86
  * Filters the Oxygen Builder section content before the
100
 
101
  $page_content .= $content;
102
  }
103
+
104
+ $page_content = relevanssi_do_shortcode( $page_content );
105
+
 
 
106
  $value[0] = $page_content;
107
  }
108
  return $value;
lib/compatibility/wp-members.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * /lib/compatibility/wp-members.php
4
+ *
5
+ * WP-Members compatibility features.
6
+ *
7
+ * @package Relevanssi
8
+ * @author Mikko Saari
9
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
10
+ * @see https://www.relevanssi.com/
11
+ */
12
+
13
+ add_filter( 'relevanssi_post_ok', 'relevanssi_wpmembers_compatibility', 10, 2 );
14
+
15
+ /**
16
+ * Checks whether the post type is blocked.
17
+ *
18
+ * Allows all logged-in users to see posts. For non-logged-in users, checks if
19
+ * the post is blocked by the _wpmem_block custom field, or if the post type is
20
+ * blocked in the $wpmem global.
21
+ *
22
+ * @param bool $post_ok Whether the user is allowed to see the post.
23
+ * @param int $post_id The post ID.
24
+ *
25
+ * @return bool
26
+ */
27
+ function relevanssi_wpmembers_compatibility( bool $post_ok, int $post_id ) : bool {
28
+ global $wpmem;
29
+
30
+ if ( is_user_logged_in() ) {
31
+ return $post_ok;
32
+ }
33
+
34
+ $post_meta = get_post_meta( $post_id, '_wpmem_block', true );
35
+ $post_type = $wpmem->block[ relevanssi_get_post_type( $post_id ) ];
36
+
37
+ if ( '1' === $post_meta ) {
38
+ $post_ok = false;
39
+ } elseif ( '1' === $post_type && '0' !== $post_meta ) {
40
+ $post_ok = false;
41
+ }
42
+
43
+ return $post_ok;
44
+ }
lib/indexing.php CHANGED
@@ -721,9 +721,10 @@ function relevanssi_update_child_posts( $new_status, $old_status, $post ) {
721
  /**
722
  * Filters the attachment and revision post types.
723
  *
724
- * If you want attachment indexing to cover other post types than just
725
- * attachment, you need to include the new post type in the array with
726
- * this filter.
 
727
  *
728
  * @param array Array of post types, default 'attachment' and 'revision'.
729
  */
@@ -1476,35 +1477,7 @@ function relevanssi_index_content( &$insert_data, $post_object, $min_word_length
1476
  }
1477
  }
1478
 
1479
- if ( 'on' === get_option( 'relevanssi_expand_shortcodes' ) ) {
1480
- // TablePress support.
1481
- if ( function_exists( 'relevanssi_enable_tablepress_shortcodes' ) ) {
1482
- $tablepress_controller = relevanssi_enable_tablepress_shortcodes();
1483
- }
1484
-
1485
- relevanssi_disable_shortcodes();
1486
-
1487
- /**
1488
- * This needs to be global here, otherwise the safety mechanism doesn't
1489
- * work correctly.
1490
- */
1491
- global $post;
1492
-
1493
- $global_post_before_shortcode = null;
1494
- if ( isset( $post ) ) {
1495
- $global_post_before_shortcode = $post;
1496
- }
1497
-
1498
- $contents = do_shortcode( $contents );
1499
-
1500
- if ( $global_post_before_shortcode ) {
1501
- $post = $global_post_before_shortcode; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1502
- }
1503
-
1504
- unset( $tablepress_controller );
1505
- } else {
1506
- $contents = strip_shortcodes( $contents );
1507
- }
1508
 
1509
  remove_shortcode( 'noindex' );
1510
  add_shortcode( 'noindex', 'relevanssi_noindex_shortcode' );
721
  /**
722
  * Filters the attachment and revision post types.
723
  *
724
+ * When Relevanssi indexes posts, it also looks at the child posts. However,
725
+ * if the indexed post is a revision or an attachment, the child posts are
726
+ * not checked. You may extend this behaviour to other post types with this
727
+ * hook.
728
  *
729
  * @param array Array of post types, default 'attachment' and 'revision'.
730
  */
1477
  }
1478
  }
1479
 
1480
+ $contents = relevanssi_do_shortcode( $contents );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1481
 
1482
  remove_shortcode( 'noindex' );
1483
  add_shortcode( 'noindex', 'relevanssi_noindex_shortcode' );
lib/init.php CHANGED
@@ -152,103 +152,7 @@ function relevanssi_init() {
152
  wp_schedule_event( time(), 'weekly', 'relevanssi_update_counts' );
153
  }
154
 
155
- if ( function_exists( 'icl_object_id' ) && ! function_exists( 'pll_is_translated_post_type' ) ) {
156
- require_once 'compatibility/wpml.php';
157
- }
158
-
159
- if ( class_exists( 'Polylang', false ) ) {
160
- require_once 'compatibility/polylang.php';
161
- }
162
-
163
- if ( class_exists( 'WooCommerce', false ) ) {
164
- require_once 'compatibility/woocommerce.php';
165
- }
166
-
167
- if ( class_exists( 'acf', false ) ) {
168
- require_once 'compatibility/acf.php';
169
- }
170
-
171
- if ( class_exists( 'Obenland_Wp_Search_Suggest', false ) ) {
172
- require_once 'compatibility/wp-search-suggest.php';
173
- }
174
-
175
- if ( function_exists( 'do_blocks' ) ) {
176
- require_once 'compatibility/gutenberg.php';
177
- }
178
-
179
- if ( defined( 'WPFD_VERSION' ) ) {
180
- require_once 'compatibility/wp-file-download.php';
181
- }
182
-
183
- if ( defined( 'WPSEO_FILE' ) ) {
184
- require_once 'compatibility/yoast-seo.php';
185
- }
186
-
187
- if ( defined( 'AIOSEO_DIR' ) ) {
188
- require_once 'compatibility/aioseo.php';
189
- }
190
-
191
- if ( function_exists( 'seopress_get_toggle_titles_option' ) && '1' === seopress_get_toggle_titles_option() ) {
192
- require_once 'compatibility/seopress.php';
193
- }
194
-
195
- if ( defined( 'THE_SEO_FRAMEWORK_VERSION' ) ) {
196
- require_once 'compatibility/seoframework.php';
197
- }
198
-
199
- if ( class_exists( 'RankMath', false ) ) {
200
- require_once 'compatibility/rankmath.php';
201
- }
202
-
203
- if ( function_exists( 'members_content_permissions_enabled' ) ) {
204
- require_once 'compatibility/members.php';
205
- }
206
-
207
- if ( defined( 'GROUPS_CORE_VERSION' ) ) {
208
- require_once 'compatibility/groups.php';
209
- }
210
-
211
- if ( class_exists( 'MeprUpdateCtrl', false ) && MeprUpdateCtrl::is_activated() ) {
212
- require_once 'compatibility/memberpress.php';
213
- }
214
-
215
- if ( defined( 'SIMPLE_WP_MEMBERSHIP_VER' ) ) {
216
- require_once 'compatibility/simplemembership.php';
217
- }
218
-
219
- if ( function_exists( 'wp_jv_prg_user_can_see_a_post' ) ) {
220
- require_once 'compatibility/wpjvpostreadinggroups.php';
221
- }
222
-
223
- if ( function_exists( 'rcp_user_can_access' ) ) {
224
- require_once 'compatibility/restrictcontentpro.php';
225
- }
226
-
227
- // phpcs:disable WordPress.NamingConventions.ValidVariableName
228
- global $userAccessManager;
229
- if ( isset( $userAccessManager ) ) {
230
- require_once 'compatibility/useraccessmanager.php';
231
- }
232
- // phpcs:enable WordPress.NamingConventions.ValidVariableName
233
-
234
- if ( function_exists( 'pmpro_has_membership_access' ) ) {
235
- require_once 'compatibility/paidmembershippro.php';
236
- }
237
-
238
- // Always required, the functions check if TablePress is active.
239
- require_once 'compatibility/tablepress.php';
240
-
241
- if ( defined( 'NINJA_TABLES_VERSION' ) ) {
242
- require_once 'compatibility/ninjatables.php';
243
- }
244
-
245
- if ( defined( 'PRLI_PLUGIN_NAME' ) ) {
246
- require_once 'compatibility/pretty-links.php';
247
- }
248
-
249
- if ( defined( 'CT_VERSION' ) ) {
250
- require_once 'compatibility/oxygen.php';
251
- }
252
 
253
  if ( ! is_array( get_option( 'relevanssi_stopwords' ) ) ) {
254
  // Version 2.12 / 4.10 changes stopwords option from a string to an
@@ -558,3 +462,40 @@ function relevanssi_export_log_check() {
558
  relevanssi_export_log();
559
  }
560
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  wp_schedule_event( time(), 'weekly', 'relevanssi_update_counts' );
153
  }
154
 
155
+ relevanssi_load_compatibility_code();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  if ( ! is_array( get_option( 'relevanssi_stopwords' ) ) ) {
158
  // Version 2.12 / 4.10 changes stopwords option from a string to an
462
  relevanssi_export_log();
463
  }
464
  }
465
+
466
+ /**
467
+ * Loads in the Relevanssi plugin compatibility code.
468
+ */
469
+ function relevanssi_load_compatibility_code() {
470
+ class_exists( 'acf', false ) && require_once 'compatibility/acf.php';
471
+ class_exists( 'MeprUpdateCtrl', false ) && MeprUpdateCtrl::is_activated() && require_once 'compatibility/memberpress.php';
472
+ class_exists( 'Obenland_Wp_Search_Suggest', false ) && require_once 'compatibility/wp-search-suggest.php';
473
+ class_exists( 'Polylang', false ) && require_once 'compatibility/polylang.php';
474
+ class_exists( 'RankMath', false ) && require_once 'compatibility/rankmath.php';
475
+ class_exists( 'WooCommerce', false ) && require_once 'compatibility/woocommerce.php';
476
+ defined( 'AIOSEO_DIR' ) && require_once 'compatibility/aioseo.php';
477
+ defined( 'CT_VERSION' ) && require_once 'compatibility/oxygen.php';
478
+ defined( 'GROUPS_CORE_VERSION' ) && require_once 'compatibility/groups.php';
479
+ defined( 'NINJA_TABLES_VERSION' ) && require_once 'compatibility/ninjatables.php';
480
+ defined( 'PRLI_PLUGIN_NAME' ) && require_once 'compatibility/pretty-links.php';
481
+ defined( 'SIMPLE_WP_MEMBERSHIP_VER' ) && require_once 'compatibility/simplemembership.php';
482
+ defined( 'THE_SEO_FRAMEWORK_VERSION' ) && require_once 'compatibility/seoframework.php';
483
+ defined( 'WPFD_VERSION' ) && require_once 'compatibility/wp-file-download.php';
484
+ defined( 'WPMEM_VERSION' ) && require_once 'compatibility/wp-members.php';
485
+ defined( 'WPSEO_FILE' ) && require_once 'compatibility/yoast-seo.php';
486
+ function_exists( 'do_blocks' ) && require_once 'compatibility/gutenberg.php';
487
+ function_exists( 'icl_object_id' ) && ! function_exists( 'pll_is_translated_post_type' ) && require_once 'compatibility/wpml.php';
488
+ function_exists( 'members_content_permissions_enabled' ) && require_once 'compatibility/members.php';
489
+ function_exists( 'pmpro_has_membership_access' ) && require_once 'compatibility/paidmembershippro.php';
490
+ function_exists( 'rcp_user_can_access' ) && require_once 'compatibility/restrictcontentpro.php';
491
+ function_exists( 'seopress_get_toggle_titles_option' ) && '1' === seopress_get_toggle_titles_option() && require_once 'compatibility/seopress.php';
492
+ function_exists( 'wp_jv_prg_user_can_see_a_post' ) && require_once 'compatibility/wpjvpostreadinggroups.php';
493
+
494
+ // phpcs:disable WordPress.NamingConventions.ValidVariableName
495
+ global $userAccessManager;
496
+ isset( $userAccessManager ) && require_once 'compatibility/useraccessmanager.php';
497
+ // phpcs:enable WordPress.NamingConventions.ValidVariableName
498
+
499
+ // Always required, the functions check if TablePress is active.
500
+ require_once 'compatibility/tablepress.php';
501
+ }
lib/search.php CHANGED
@@ -136,22 +136,28 @@ function relevanssi_search( $args ) {
136
  */
137
  $remove_stopwords = apply_filters( 'relevanssi_remove_stopwords_in_titles', true );
138
 
139
- $terms = relevanssi_tokenize( $q, $remove_stopwords, $min_length );
140
- $terms = array_keys( $terms ); // Don't care about tf in query.
141
 
142
- $terms_without_synonyms = relevanssi_tokenize( $q_no_synonyms, $remove_stopwords, $min_length );
143
- $terms_without_synonyms = array_keys( $terms_without_synonyms );
 
 
 
 
 
 
 
144
 
145
  if ( function_exists( 'relevanssi_process_terms' ) ) {
146
- $process_terms_results = relevanssi_process_terms( $terms, $q );
147
  $query_restrictions .= $process_terms_results['query_restrictions'];
148
- $terms = $process_terms_results['terms'];
149
  }
150
 
151
  $no_terms = false;
152
- if ( count( $terms ) < 1 && empty( $q ) ) {
153
- $no_terms = true;
154
- $terms[] = 'term';
155
  }
156
 
157
  /**
@@ -197,7 +203,7 @@ function relevanssi_search( $args ) {
197
 
198
  do {
199
  $df_counts = relevanssi_generate_df_counts(
200
- $terms,
201
  array(
202
  'no_terms' => $no_terms,
203
  'operator' => $operator,
@@ -346,23 +352,28 @@ function relevanssi_search( $args ) {
346
  $no_matches = $params['no_matches'];
347
  } while ( $search_again );
348
 
349
- $strip_stops = true;
350
- $temp_terms_without_stops = array_keys( relevanssi_tokenize( implode( ' ', $terms ), $strip_stops ) );
351
- $terms_without_stops = array();
352
- foreach ( $temp_terms_without_stops as $temp_term ) {
353
- if ( relevanssi_strlen( $temp_term ) >= $min_length ) {
354
- array_push( $terms_without_stops, $temp_term );
 
 
355
  }
356
- }
357
- $total_terms = count( $terms_without_stops );
358
 
359
- $temp_terms_without_stops_synonyms = array_keys( relevanssi_tokenize( implode( ' ', $terms_without_synonyms ), $strip_stops ) );
360
- $terms_without_stops_synonyms = array();
361
- foreach ( $temp_terms_without_stops_synonyms as $temp_term ) {
362
- if ( relevanssi_strlen( $temp_term ) >= $min_length ) {
363
- array_push( $terms_without_stops_synonyms, $temp_term );
 
364
  }
 
 
 
365
  }
 
366
 
367
  if ( isset( $doc_weight ) ) {
368
  /**
@@ -387,15 +398,24 @@ function relevanssi_search( $args ) {
387
  // doc didn't match all terms, so it's discarded.
388
  continue;
389
  }
 
 
390
  if ( count( $doc_terms[ $doc ] ) < $total_terms ) {
391
- $missing_terms[ $doc ] = array_diff(
392
- array_values( $terms_without_stops_synonyms ),
393
- array_keys( $doc_terms[ $doc ] )
394
- );
395
- if ( count( $missing_terms[ $doc ] ) === count( $terms_without_stops_synonyms ) ) {
 
 
 
 
 
 
 
396
  $missing_terms[ $doc ] = array_diff(
397
- array_values( $terms_without_stops_synonyms ),
398
- relevanssi_replace_synonyms_in_terms( array_keys( $doc_terms[ $doc ] ) )
399
  );
400
  }
401
  }
136
  */
137
  $remove_stopwords = apply_filters( 'relevanssi_remove_stopwords_in_titles', true );
138
 
139
+ $terms['terms'] = array_keys( relevanssi_tokenize( $q, $remove_stopwords, $min_length ) );
 
140
 
141
+ $terms['original_terms'] = $q_no_synonyms !== $q
142
+ ? array_keys( relevanssi_tokenize( $q_no_synonyms, $remove_stopwords, $min_length ) )
143
+ : $terms['terms'];
144
+
145
+ if ( has_filter( 'relevanssi_stemmer' ) ) {
146
+ do_action( 'relevanssi_disable_stemmer' );
147
+ $terms['original_terms'] = array_keys( relevanssi_tokenize( $q_no_synonyms, $remove_stopwords, $min_length ) );
148
+ do_action( 'relevanssi_enable_stemmer' );
149
+ }
150
 
151
  if ( function_exists( 'relevanssi_process_terms' ) ) {
152
+ $process_terms_results = relevanssi_process_terms( $terms['terms'], $q );
153
  $query_restrictions .= $process_terms_results['query_restrictions'];
154
+ $terms['terms'] = $process_terms_results['terms'];
155
  }
156
 
157
  $no_terms = false;
158
+ if ( count( $terms['terms'] ) < 1 && empty( $q ) ) {
159
+ $no_terms = true;
160
+ $terms['terms'] = array( 'term' );
161
  }
162
 
163
  /**
203
 
204
  do {
205
  $df_counts = relevanssi_generate_df_counts(
206
+ $terms['terms'],
207
  array(
208
  'no_terms' => $no_terms,
209
  'operator' => $operator,
352
  $no_matches = $params['no_matches'];
353
  } while ( $search_again );
354
 
355
+ if ( ! $remove_stopwords ) {
356
+ $strip_stops = true;
357
+ $terms['no_stops'] = array_keys( relevanssi_tokenize( implode( ' ', $terms['terms'] ), $strip_stops, $min_length ) );
358
+
359
+ if ( $q !== $q_no_synonyms ) {
360
+ $terms['original_terms_no_stops'] = array_keys( relevanssi_tokenize( implode( ' ', $terms['original_terms'] ), $strip_stops, $min_length ) );
361
+ } else {
362
+ $terms['original_terms_no_stops'] = $terms['no_stops'];
363
  }
 
 
364
 
365
+ if ( has_filter( 'relevanssi_stemmer' ) ) {
366
+ do_action( 'relevanssi_disable_stemmer' );
367
+ $terms['original_terms_no_stops'] = array_keys( relevanssi_tokenize( implode( ' ', $terms['original_terms'] ), $strip_stops, $min_length ) );
368
+ do_action( 'relevanssi_enable_stemmer' );
369
+ } else {
370
+ $terms['original_terms_no_stops'] = $terms['no_stops'];
371
  }
372
+ } else {
373
+ $terms['no_stops'] = $terms['terms'];
374
+ $terms['original_terms_no_stops'] = $terms['original_terms'];
375
  }
376
+ $total_terms = count( $terms['no_stops'] );
377
 
378
  if ( isset( $doc_weight ) ) {
379
  /**
398
  // doc didn't match all terms, so it's discarded.
399
  continue;
400
  }
401
+ $doc_terms_for_doc = array_keys( $doc_terms[ $doc ] );
402
+ $original_terms = array_values( $terms['original_terms_no_stops'] );
403
  if ( count( $doc_terms[ $doc ] ) < $total_terms ) {
404
+ if ( $q !== $q_no_synonyms ) {
405
+ $missing_terms[ $doc ] = array_diff(
406
+ $original_terms,
407
+ relevanssi_replace_synonyms_in_terms( $doc_terms_for_doc )
408
+ );
409
+ if ( count( $missing_terms[ $doc ] ) + count( relevanssi_replace_stems_in_terms( $doc_terms_for_doc ) ) !== count( $terms['original_terms'] ) ) {
410
+ $missing_terms[ $doc ] = array_diff(
411
+ $original_terms,
412
+ $doc_terms_for_doc
413
+ );
414
+ }
415
+ } else {
416
  $missing_terms[ $doc ] = array_diff(
417
+ $original_terms,
418
+ $doc_terms_for_doc
419
  );
420
  }
421
  }
lib/sorting.php CHANGED
@@ -13,9 +13,9 @@
13
  /**
14
  * Gets the next key-direction pair from the orderby array.
15
  *
16
- * Fetches a key-direction pair from the orderby array. Converts key names to match
17
- * the post object parameters when necessary and seeds the random generator, if
18
- * required.
19
  *
20
  * @param array $orderby An array of key-direction pairs.
21
  *
@@ -182,11 +182,11 @@ function relevanssi_get_compare_values( $key, $item_1, $item_2 ) {
182
  /**
183
  * Adds in a missing sorting value.
184
  *
185
- * In some cases the sorting method may not have values for all posts
186
- * (for example when sorting by 'menu_order'). If you still want to use
187
- * a sorting method like this, you can use this function to fill in a
188
- * value (in the case of 'menu_order', for example, one could use
189
- * PHP_INT_MAX.)
190
  *
191
  * @param string $key1 The value to filter.
192
  * @param string $key The name of the key.
@@ -256,8 +256,8 @@ function relevanssi_get_compare_values( $key, $item_1, $item_2 ) {
256
  * date comparisons and 'string' for string comparison, everything else is
257
  * considered a numeric comparison.
258
  *
259
- * @return int $val Returns < 0 if key1 is less than key2; > 0 if key1 is greater
260
- * than key2, and 0 if they are equal.
261
  */
262
  function relevanssi_compare_values( $key1, $key2, $compare ) {
263
  $val = 0;
@@ -284,21 +284,22 @@ function relevanssi_compare_values( $key1, $key2, $compare ) {
284
  /**
285
  * Compares two values using order array from a filter.
286
  *
287
- * Compares two sorting keys using a sorted array that contains value => order pairs.
288
- * Uses the 'relevanssi_comparison_order' filter to get the sorting guidance array.
 
289
  *
290
  * @param string $key1 The first key.
291
  * @param string $key2 The second key.
292
  *
293
- * @return int $val Returns < 0 if key1 is less than key2; > 0 if key1 is greater
294
- * than key2, and 0 if they are equal.
295
  */
296
  function relevanssi_filter_compare( $key1, $key2 ) {
297
  /**
298
  * Provides the sorting order for the filter.
299
  *
300
- * The array should contain the possible key values as keys and their order in
301
- * the values, like this:
302
  *
303
  * $order = array(
304
  * 'post' => 0,
@@ -306,15 +307,16 @@ function relevanssi_filter_compare( $key1, $key2 ) {
306
  * 'book' => 2,
307
  * );
308
  *
309
- * This would sort posts first, pages second, books third. Values that do not
310
- * appear in the array are sorted last.
311
  *
312
  * @param array Sorting guidance array.
313
  */
314
  $order = apply_filters( 'relevanssi_comparison_order', array() );
315
 
316
- // Set the default values so that if the key is not found in the array, it's last.
317
- $max_key = max( $order );
 
318
 
319
  $val_1 = isset( $order[ $key1 ] ) ? $order[ $key1 ] : $max_key + 1;
320
  $val_2 = isset( $order[ $key2 ] ) ? $order[ $key2 ] : $max_key + 1;
@@ -325,8 +327,8 @@ function relevanssi_filter_compare( $key1, $key2 ) {
325
  /**
326
  * Compares values using multiple levels of sorting keys.
327
  *
328
- * Comparison function for usort() using multiple levels of sorting methods. If one
329
- * level produces a tie, the sort will get a next level of sorting methods.
330
  *
331
  * @global array $relevanssi_keys An array of sorting keys by level.
332
  * @global array $relevanssi_dirs An array of sorting directions by level.
@@ -372,16 +374,21 @@ function relevanssi_cmp_function( $a, $b ) {
372
  /**
373
  * Sorts post objects.
374
  *
375
- * Sorts post objects using multiple levels of sorting methods. This function was
376
- * originally written by Matthew Hood and published in the PHP manual comments.
 
 
377
  * The actual sorting is handled by relevanssi_cmp_function().
378
  *
 
 
379
  * @global array $relevanssi_keys An array of sorting keys by level.
380
  * @global array $relevanssi_dirs An array of sorting directions by level.
381
  * @global array $relevanssi_compares An array of comparison methods by level.
382
  * @global array $relevanssi_meta_query The meta query array.
383
  *
384
- * @param array $data The posts to sort are in $data[0], used as a reference.
 
385
  * @param array $orderby The array of orderby rules with directions.
386
  * @param array $meta_query The meta query array, in case it's needed for meta
387
  * query based sorting.
@@ -412,11 +419,13 @@ function relevanssi_object_sort( &$data, $orderby, $meta_query ) {
412
  * A sorting function that sorts strings by length. Uses relevanssi_strlen() to
413
  * count the string length.
414
  *
 
 
415
  * @param string $a String A.
416
  * @param string $b String B.
417
  *
418
- * @return int Negative value, if string A is longer; zero, if strings are equally
419
- * long; positive, if string B is longer.
420
  */
421
  function relevanssi_strlen_sort( $a, $b ) {
422
  return relevanssi_strlen( $b ) - relevanssi_strlen( $a );
13
  /**
14
  * Gets the next key-direction pair from the orderby array.
15
  *
16
+ * Fetches a key-direction pair from the orderby array. Converts key names to
17
+ * match the post object parameters when necessary and seeds the random
18
+ * generator, if required.
19
  *
20
  * @param array $orderby An array of key-direction pairs.
21
  *
182
  /**
183
  * Adds in a missing sorting value.
184
  *
185
+ * In some cases the sorting method may not have values for all
186
+ * posts (for example when sorting by 'menu_order'). If you still
187
+ * want to use a sorting method like this, you can use this function
188
+ * to fill in a value (in the case of 'menu_order', for example, one
189
+ * could use PHP_INT_MAX.)
190
  *
191
  * @param string $key1 The value to filter.
192
  * @param string $key The name of the key.
256
  * date comparisons and 'string' for string comparison, everything else is
257
  * considered a numeric comparison.
258
  *
259
+ * @return int $val Returns < 0 if key1 is less than key2; > 0 if key1 is
260
+ * greater than key2, and 0 if they are equal.
261
  */
262
  function relevanssi_compare_values( $key1, $key2, $compare ) {
263
  $val = 0;
284
  /**
285
  * Compares two values using order array from a filter.
286
  *
287
+ * Compares two sorting keys using a sorted array that contains value => order
288
+ * pairs. Uses the 'relevanssi_comparison_order' filter to get the sorting
289
+ * guidance array.
290
  *
291
  * @param string $key1 The first key.
292
  * @param string $key2 The second key.
293
  *
294
+ * @return int $val Returns < 0 if key1 is less than key2; > 0 if key1 is
295
+ * greater than key2, and 0 if they are equal.
296
  */
297
  function relevanssi_filter_compare( $key1, $key2 ) {
298
  /**
299
  * Provides the sorting order for the filter.
300
  *
301
+ * The array should contain the possible key values as keys and their order
302
+ * in the values, like this:
303
  *
304
  * $order = array(
305
  * 'post' => 0,
307
  * 'book' => 2,
308
  * );
309
  *
310
+ * This would sort posts first, pages second, books third. Values that do
311
+ * not appear in the array are sorted last.
312
  *
313
  * @param array Sorting guidance array.
314
  */
315
  $order = apply_filters( 'relevanssi_comparison_order', array() );
316
 
317
+ // Set the default values so that if the key is not found in the array, it's
318
+ // last.
319
+ $max_key = ! empty( $order ) ? max( $order ) : 0;
320
 
321
  $val_1 = isset( $order[ $key1 ] ) ? $order[ $key1 ] : $max_key + 1;
322
  $val_2 = isset( $order[ $key2 ] ) ? $order[ $key2 ] : $max_key + 1;
327
  /**
328
  * Compares values using multiple levels of sorting keys.
329
  *
330
+ * Comparison function for usort() using multiple levels of sorting methods. If
331
+ * one level produces a tie, the sort will get a next level of sorting methods.
332
  *
333
  * @global array $relevanssi_keys An array of sorting keys by level.
334
  * @global array $relevanssi_dirs An array of sorting directions by level.
374
  /**
375
  * Sorts post objects.
376
  *
377
+ * Sorts post objects using multiple levels of sorting methods. This function
378
+ * was originally written by Matthew Hood and published in the PHP manual
379
+ * comments.
380
+ *
381
  * The actual sorting is handled by relevanssi_cmp_function().
382
  *
383
+ * @see relevanssi_cmp_function()
384
+ *
385
  * @global array $relevanssi_keys An array of sorting keys by level.
386
  * @global array $relevanssi_dirs An array of sorting directions by level.
387
  * @global array $relevanssi_compares An array of comparison methods by level.
388
  * @global array $relevanssi_meta_query The meta query array.
389
  *
390
+ * @param array $data The posts to sort are in $data[0], used as a
391
+ * reference.
392
  * @param array $orderby The array of orderby rules with directions.
393
  * @param array $meta_query The meta query array, in case it's needed for meta
394
  * query based sorting.
419
  * A sorting function that sorts strings by length. Uses relevanssi_strlen() to
420
  * count the string length.
421
  *
422
+ * @see relevanssi_strlen()
423
+ *
424
  * @param string $a String A.
425
  * @param string $b String B.
426
  *
427
+ * @return int Negative value, if string A is longer; zero, if strings are
428
+ * equally long; positive, if string B is longer.
429
  */
430
  function relevanssi_strlen_sort( $a, $b ) {
431
  return relevanssi_strlen( $b ) - relevanssi_strlen( $a );
lib/utils.php CHANGED
@@ -114,6 +114,52 @@ function relevanssi_debug_echo( string $notice ) {
114
  }
115
  }
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  /**
118
  * Recursively flattens a multidimensional array to produce a string.
119
  *
@@ -151,6 +197,8 @@ function relevanssi_generate_closing_tags( array $tags ) {
151
  /**
152
  * Returns a post object based on ID, **type**id notation or an object.
153
  *
 
 
154
  * @param int|string|WP_Post $source The source identified to parse, either a
155
  * post ID integer, a **type**id string or a post object.
156
  *
@@ -182,6 +230,28 @@ function relevanssi_get_an_object( $source ) {
182
  );
183
  }
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  /**
186
  * Returns the locale or language code.
187
  *
@@ -208,12 +278,12 @@ function relevanssi_get_current_language( bool $locale = true ) {
208
  global $post;
209
 
210
  if ( isset( $post ) ) {
211
- if ( isset( $post->term_id ) ) {
212
  $current_language = pll_get_term_language( $post->term_id, $locale ? 'locale' : 'slug' );
213
- } elseif ( ! isset( $post->user_id ) ) {
214
  $current_language = pll_get_post_language( $post->ID, $locale ? 'locale' : 'slug' );
215
  }
216
- } else {
217
  $current_language = pll_current_language( $locale ? 'locale' : 'slug' );
218
  }
219
  }
@@ -266,6 +336,8 @@ function relevanssi_get_current_language( bool $locale = true ) {
266
  * Uses get_permalink() to get the permalink, then adds the 'highlight'
267
  * parameter if necessary using relevanssi_add_highlight().
268
  *
 
 
269
  * @return string The permalink.
270
  */
271
  function relevanssi_get_permalink() {
@@ -355,6 +427,11 @@ function relevanssi_get_post_meta_for_all_posts( array $post_ids, string $field
355
  /**
356
  * Returns an object based on ID.
357
  *
 
 
 
 
 
358
  * @param int|string $post_id An ID, either an integer post ID or a
359
  * **type**id string for terms and users.
360
  *
@@ -428,6 +505,8 @@ function relevanssi_get_term_taxonomy( int $term_id ) {
428
  * Replacement for get_the_tags() that does the same, but applies Relevanssi
429
  * search term highlighting on the results.
430
  *
 
 
431
  * @param string $before What is printed before the tags, default ''.
432
  * @param string $separator The separator between items, default ', '.
433
  * @param string $after What is printed after the tags, default ''.
@@ -443,7 +522,7 @@ function relevanssi_get_the_tags( string $before = '', string $separator = ', ',
443
  * Reads the highlighted title from $post->post_highlighted_title. Uses the
444
  * relevanssi_get_post() to fecth the post.
445
  *
446
- * @uses relevanssi_get_post()
447
  *
448
  * @param int|WP_Post $post The post ID or a post object.
449
  *
@@ -466,6 +545,8 @@ function relevanssi_get_the_title( $post ) {
466
  * Returns an imploded string if the option exists and is an array, an empty
467
  * string otherwise.
468
  *
 
 
469
  * @param array $request An array of option values.
470
  * @param string $option The key to check.
471
  * @param string $glue The glue string for implode(), default ','.
@@ -482,6 +563,8 @@ function relevanssi_implode( array $request, string $option, string $glue = ','
482
  /**
483
  * Returns the intval of the option if it exists, null otherwise.
484
  *
 
 
485
  * @param array $request An array of option values.
486
  * @param string $option The key to check.
487
  *
@@ -537,10 +620,10 @@ function relevanssi_launch_ajax_action( string $action, array $payload_args = ar
537
  /**
538
  * Returns a legal value.
539
  *
540
- * @param array $request An array of option values.
541
- * @param string $option The key to check.
542
- * @param array $values The legal values.
543
- * @param string $default The default value.
544
  *
545
  * @return string|null A legal value or the default value, null if the option
546
  * isn't set.
@@ -559,8 +642,13 @@ function relevanssi_legal_value( array $request, string $option, array $values,
559
  /**
560
  * Multibyte friendly case-insensitive string comparison.
561
  *
562
- * If multibyte string functions are available, do strcmp() after using
563
- * mb_strtoupper() to both strings. Otherwise use strcasecmp().
 
 
 
 
 
564
  *
565
  * @param string $str1 First string to compare.
566
  * @param string $str2 Second string to compare.
@@ -630,6 +718,8 @@ function relevanssi_remove_quotes( string $string ) {
630
  * of (term => hits). The number of hits is not needed, so this function
631
  * discards it as a side effect.
632
  *
 
 
633
  * @param array $array An array to process.
634
  *
635
  * @return array The same array with quotes removed from the keys.
@@ -718,6 +808,9 @@ function relevanssi_return_off() {
718
  /**
719
  * Gets a post object, returns ID, ID=>parent or the post object.
720
  *
 
 
 
721
  * @param object $post The post object.
722
  * @param string $return_value The value to return, possible values are 'id'
723
  * for returning the ID and 'id=>parent' for returning the ID=>parent object,
@@ -836,7 +929,8 @@ function relevanssi_strip_invisibles( $text ) {
836
  * are not stuck together after the tags are removed. The function also removes
837
  * invisible content.
838
  *
839
- * @see relevanssi_strip_invisibles
 
840
  *
841
  * @param string|null $content The content.
842
  *
@@ -1017,6 +1111,8 @@ function relevanssi_substr( $string, int $start, $length = null ) {
1017
  * Prints out the post excerpt from $post->post_excerpt, unless the post is
1018
  * protected. Only works in the Loop.
1019
  *
 
 
1020
  * @global $post The global post object.
1021
  */
1022
  function relevanssi_the_excerpt() {
@@ -1033,6 +1129,8 @@ function relevanssi_the_excerpt() {
1033
  *
1034
  * Uses get_permalink() to get the permalink, then adds the 'highlight'
1035
  * parameter if necessary using relevanssi_add_highlight(), then echoes it out.
 
 
1036
  */
1037
  function relevanssi_the_permalink() {
1038
  echo esc_url( relevanssi_get_permalink() );
114
  }
115
  }
116
 
117
+ /**
118
+ * Runs do_shortcode() on content, but safeguards the global $post to make sure
119
+ * it isn't changed by the shortcodes. If shortcode expansion is disabled in
120
+ * Relevanssi settings, runs strip_shortcodes() on the content.
121
+ *
122
+ * @uses relevanssi_disable_shortcodes() Disables problem shortcodes.
123
+ * @see do_shortcode() Expands shortcodes.
124
+ * @see strip_shortcodes() Strips off shortcodes.
125
+ *
126
+ * @param string $content The content where the shortcodes are expanded.
127
+ *
128
+ * @return string
129
+ */
130
+ function relevanssi_do_shortcode( string $content ) : string {
131
+ if ( 'on' === get_option( 'relevanssi_expand_shortcodes' ) ) {
132
+ // TablePress support.
133
+ if ( function_exists( 'relevanssi_enable_tablepress_shortcodes' ) ) {
134
+ $tablepress_controller = relevanssi_enable_tablepress_shortcodes();
135
+ }
136
+
137
+ relevanssi_disable_shortcodes();
138
+
139
+ /**
140
+ * This needs to be global here, otherwise the safety mechanism doesn't
141
+ * work correctly.
142
+ */
143
+ global $post;
144
+
145
+ $global_post_before_shortcode = null;
146
+ if ( isset( $post ) ) {
147
+ $global_post_before_shortcode = $post;
148
+ }
149
+
150
+ $content = do_shortcode( $content );
151
+
152
+ if ( $global_post_before_shortcode ) {
153
+ $post = $global_post_before_shortcode; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
154
+ }
155
+
156
+ unset( $tablepress_controller );
157
+ } else {
158
+ $content = strip_shortcodes( $content );
159
+ }
160
+ return $content;
161
+ }
162
+
163
  /**
164
  * Recursively flattens a multidimensional array to produce a string.
165
  *
197
  /**
198
  * Returns a post object based on ID, **type**id notation or an object.
199
  *
200
+ * @uses relevanssi_get_post_object() Fetches post objects.
201
+ *
202
  * @param int|string|WP_Post $source The source identified to parse, either a
203
  * post ID integer, a **type**id string or a post object.
204
  *
230
  );
231
  }
232
 
233
+ /**
234
+ * Returns the attachment filename suffix.
235
+ *
236
+ * Reads the filename from $post->guid and returns the file suffix.
237
+ *
238
+ * @param WP_Post|int $post The post object or post ID.
239
+ * @return string The suffix if it is found, an empty string otherwise.
240
+ */
241
+ function relevanssi_get_attachment_suffix( $post ) : string {
242
+ if ( ! is_object( $post ) ) {
243
+ $post = relevanssi_get_post( $post );
244
+ if ( ! $post ) {
245
+ return '';
246
+ }
247
+ }
248
+ if ( 'attachment' !== $post->post_type ) {
249
+ return '';
250
+ }
251
+ list( , $type ) = explode( '.', basename( $post->guid ) );
252
+ return $type;
253
+ }
254
+
255
  /**
256
  * Returns the locale or language code.
257
  *
278
  global $post;
279
 
280
  if ( isset( $post ) ) {
281
+ if ( isset( $post->term_id ) && function_exists( 'pll_get_term_language' ) ) {
282
  $current_language = pll_get_term_language( $post->term_id, $locale ? 'locale' : 'slug' );
283
+ } elseif ( ! isset( $post->user_id ) && function_exists( 'pll_get_post_language' ) ) {
284
  $current_language = pll_get_post_language( $post->ID, $locale ? 'locale' : 'slug' );
285
  }
286
+ } elseif ( function_exists( 'pll_current_language' ) ) {
287
  $current_language = pll_current_language( $locale ? 'locale' : 'slug' );
288
  }
289
  }
336
  * Uses get_permalink() to get the permalink, then adds the 'highlight'
337
  * parameter if necessary using relevanssi_add_highlight().
338
  *
339
+ * @see get_permalink()
340
+ *
341
  * @return string The permalink.
342
  */
343
  function relevanssi_get_permalink() {
427
  /**
428
  * Returns an object based on ID.
429
  *
430
+ * Wrapper to handle non-post cases (terms, user profiles). Regular posts are
431
+ * passed on to relevanssi_get_post().
432
+ *
433
+ * @uses relevanssi_get_post() Used to fetch regular posts.
434
+ *
435
  * @param int|string $post_id An ID, either an integer post ID or a
436
  * **type**id string for terms and users.
437
  *
505
  * Replacement for get_the_tags() that does the same, but applies Relevanssi
506
  * search term highlighting on the results.
507
  *
508
+ * @uses relevanssi_the_tags() Does the actual work.
509
+ *
510
  * @param string $before What is printed before the tags, default ''.
511
  * @param string $separator The separator between items, default ', '.
512
  * @param string $after What is printed after the tags, default ''.
522
  * Reads the highlighted title from $post->post_highlighted_title. Uses the
523
  * relevanssi_get_post() to fecth the post.
524
  *
525
+ * @uses relevanssi_get_post() Fetches post objects.
526
  *
527
  * @param int|WP_Post $post The post ID or a post object.
528
  *
545
  * Returns an imploded string if the option exists and is an array, an empty
546
  * string otherwise.
547
  *
548
+ * @see implode()
549
+ *
550
  * @param array $request An array of option values.
551
  * @param string $option The key to check.
552
  * @param string $glue The glue string for implode(), default ','.
563
  /**
564
  * Returns the intval of the option if it exists, null otherwise.
565
  *
566
+ * @see intval()
567
+ *
568
  * @param array $request An array of option values.
569
  * @param string $option The key to check.
570
  *
620
  /**
621
  * Returns a legal value.
622
  *
623
+ * @param array $request An array of option values.
624
+ * @param string $option The key to check.
625
+ * @param array $values The legal values.
626
+ * @param string $default The default value.
627
  *
628
  * @return string|null A legal value or the default value, null if the option
629
  * isn't set.
642
  /**
643
  * Multibyte friendly case-insensitive string comparison.
644
  *
645
+ * If multibyte string functions are available, do strnatcmp() after using
646
+ * mb_strtoupper() to both strings. Otherwise use strnatcasecmp().
647
+ *
648
+ * @see strnatcasecmp() Falls back to this if multibyte functions are
649
+ * not available.
650
+ * @see strnatcmp() Used to compare the strings.
651
+ * @see mb_strtoupper() Used to convert strings to uppercase.
652
  *
653
  * @param string $str1 First string to compare.
654
  * @param string $str2 Second string to compare.
718
  * of (term => hits). The number of hits is not needed, so this function
719
  * discards it as a side effect.
720
  *
721
+ * @uses relevanssi_remove_quotes() This does the actual work.
722
+ *
723
  * @param array $array An array to process.
724
  *
725
  * @return array The same array with quotes removed from the keys.
808
  /**
809
  * Gets a post object, returns ID, ID=>parent or the post object.
810
  *
811
+ * @uses relevanssi_return_id_type() Used to return ID=>type results.
812
+ * @uses relevanssi_return_id_parent() Used to return ID=>parent results.
813
+ *
814
  * @param object $post The post object.
815
  * @param string $return_value The value to return, possible values are 'id'
816
  * for returning the ID and 'id=>parent' for returning the ID=>parent object,
929
  * are not stuck together after the tags are removed. The function also removes
930
  * invisible content.
931
  *
932
+ * @uses relevanssi_strip_invisibles() Used to remove scripts and other tags.
933
+ * @see strip_tags() Used to remove tags.
934
  *
935
  * @param string|null $content The content.
936
  *
1111
  * Prints out the post excerpt from $post->post_excerpt, unless the post is
1112
  * protected. Only works in the Loop.
1113
  *
1114
+ * @see post_password_required() Used to check for password requirements.
1115
+ *
1116
  * @global $post The global post object.
1117
  */
1118
  function relevanssi_the_excerpt() {
1129
  *
1130
  * Uses get_permalink() to get the permalink, then adds the 'highlight'
1131
  * parameter if necessary using relevanssi_add_highlight(), then echoes it out.
1132
+ *
1133
+ * @uses relevanssi_get_permalink() Fetches the current post permalink.
1134
  */
1135
  function relevanssi_the_permalink() {
1136
  echo esc_url( relevanssi_get_permalink() );
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: search, relevance, better search, product search, woocommerce search
5
  Requires at least: 4.9
6
  Tested up to: 5.7.1
7
  Requires PHP: 7.0
8
- Stable tag: 4.13.0
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -131,6 +131,11 @@ Each document database is full of useless words. All the little words that appea
131
  * John Calahan for extensive 4.0 beta testing.
132
 
133
  == Changelog ==
 
 
 
 
 
134
  = 4.13.0 =
135
  * New feature: New filter hook `relevanssi_phrase` filters each phrase before it's used in the MySQL query.
136
  * New feature: Relevanssi can now add Google-style missing term lists to the search results. You can either use the `%missing%` tag in the search results breakdown settings, or you can create your own code: the missing terms are also in `$post->missing_terms`. Relevanssi Premium will also add "Must have" links when there's just one missing term.
@@ -221,6 +226,9 @@ Each document database is full of useless words. All the little words that appea
221
  * Minor fix: Improved Oxygen Builder support makes sure `ct_builder_shortcodes` custom field is always indexed.
222
 
223
  == Upgrade notice ==
 
 
 
224
  = 4.13.0 =
225
  * Lots of new features and bug fixes.
226
 
5
  Requires at least: 4.9
6
  Tested up to: 5.7.1
7
  Requires PHP: 7.0
8
+ Stable tag: 4.13.1
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
131
  * John Calahan for extensive 4.0 beta testing.
132
 
133
  == Changelog ==
134
+ = 4.13.1 =
135
+ * New feature: Adds compatibility for WP-Members plugin, preventing blocked posts from showing up in the search results.
136
+ * New feature: New function `relevanssi_get_attachment_suffix()` can be used to return the attachment file suffix based on a post object or a post ID.
137
+ * Minor fix: Improves the Oxygen compatibility. Now also the [oxygen] shortcode tags are removed.
138
+
139
  = 4.13.0 =
140
  * New feature: New filter hook `relevanssi_phrase` filters each phrase before it's used in the MySQL query.
141
  * New feature: Relevanssi can now add Google-style missing term lists to the search results. You can either use the `%missing%` tag in the search results breakdown settings, or you can create your own code: the missing terms are also in `$post->missing_terms`. Relevanssi Premium will also add "Must have" links when there's just one missing term.
226
  * Minor fix: Improved Oxygen Builder support makes sure `ct_builder_shortcodes` custom field is always indexed.
227
 
228
  == Upgrade notice ==
229
+ = 4.13.1 =
230
+ * Compatibility for WP-Members added.
231
+
232
  = 4.13.0 =
233
  * Lots of new features and bug fixes.
234
 
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.13.0
17
  * Author: Mikko Saari
18
  * Author URI: http://www.mikkosaari.fi/
19
  * Text Domain: relevanssi
@@ -67,7 +67,7 @@ $relevanssi_variables['database_version'] = 6;
67
  $relevanssi_variables['file'] = __FILE__;
68
  $relevanssi_variables['plugin_dir'] = plugin_dir_path( __FILE__ );
69
  $relevanssi_variables['plugin_basename'] = plugin_basename( __FILE__ );
70
- $relevanssi_variables['plugin_version'] = '4.13.0';
71
 
72
  require_once 'lib/admin-ajax.php';
73
  require_once 'lib/common.php';
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.13.1
17
  * Author: Mikko Saari
18
  * Author URI: http://www.mikkosaari.fi/
19
  * Text Domain: relevanssi
67
  $relevanssi_variables['file'] = __FILE__;
68
  $relevanssi_variables['plugin_dir'] = plugin_dir_path( __FILE__ );
69
  $relevanssi_variables['plugin_basename'] = plugin_basename( __FILE__ );
70
+ $relevanssi_variables['plugin_version'] = '4.13.1';
71
 
72
  require_once 'lib/admin-ajax.php';
73
  require_once 'lib/common.php';