Relevanssi – A Better Search - Version 4.11.0

Version Description

  • New feature: New filter hook relevanssi_rendered_block filters Gutenberg block content after the block has been rendered with render_block().
  • New feature: New filter hook relevanssi_log_query can be used to filter the search query before it's logged. This can be used to log instead the query that includes synonyms (available as a parameter to the filter hook).
  • New feature: New filter hook relevanssi_add_all_results can be used to make Relevanssi add a list of all result IDs found to $query->relevanssi_all_results. Just make this hook return true.
  • New feature: New filter hook relevanssi_acceptable_hooks can be used to adjust where in WP admin the Relevanssi admin javascripts are enqueued.
  • New feature: Support for All-in-One SEO. Posts marked as 'Robots No Index' are not indexed by Relevanssi.
  • New feature: New setting in advanced indexing settings to control whether Relevanssi respects the SEO plugin 'noindex' setting or not.
  • Changed behaviour: Type hinting has been added to Relevanssi functions, which may cause errors if the filter functions are sloppy with data types.
  • Changed behaviour: relevanssi_the_title() now supports the same parameters as the_title(), so you can just replace the_title() with it and keep everything else the same. The old behaviour is still supported.
  • Changed behaviour: Relevanssi no longer logs queries with the added synonyms. You can use the relevanssi_log_query filter hook to return to the previous behaviour of logging the synonyms too. Thanks to Jan Willem Oostendorp.
  • Changed behaviour: When using ACF and custom fields indexing set to 'all', Relevanssi will no longer index the meta fields (where the content begins with field_).
  • Minor fix: The Oxygen compatibility made it impossible to index other custom fields than the Oxygen ct_builder_shortcodes. This has been improved now.
  • Minor fix: Old legacy scripts that caused Javascript warnings on admin pages have been removed.
  • Minor fix: In some cases, having less than or greater than symbols in PDF content would block that PDF content from being indexed.
Download this release

Release Info

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

Code changes from version 4.10.2 to 4.11.0

changelog.txt CHANGED
@@ -1,3 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  = 4.7.2 =
2
  * Minor fix: Media Library searches failed if Relevanssi was enabled in the WP admin, but the `attachment` post type wasn't indexed. Relevanssi will no longer block the default Media Library search in these cases.
3
  * Minor fix: Adds more backwards compatibility for the `relevanssi_indexing_restriction` change, there's now an alert on indexing tab if there's a problem.
1
+ = 4.8.3 =
2
+ * New feature: Both `relevanssi_fuzzy_query` and `relevanssi_term_where` now get the current search term as a parameter.
3
+ * Minor fix: Relevanssi database tables don't have PRIMARY keys, only UNIQUE keys. In case this is a problem (for example on Digital Ocean servers), deactivate and activate Relevanssi to fix the problem.
4
+ * Minor fix: When `posts_per_page` was set to -1, the `max_num_pages` was incorrectly set to the number of posts found. It should, of course, be 1.
5
+ * Minor fix: Excluding from logs didn't work if user IDs had spaces between them ('user_a, user_b'). This is now fixed for good, the earlier fix didn't work.
6
+
7
+ = 4.8.2 =
8
+ * New feature: New filter hook `relevanssi_term_where` lets you filter the term WHERE conditional for the search query.
9
+ * Minor fix: Doing the document count updates asynchronously caused problems in some cases (eg. importing posts). Now the document count is only updated after a full indexing and once per week.
10
+ * Minor fix: Phrase matching has been improved to make it possible to search for phrases that include characters like the ampersand.
11
+
12
+ = 4.8.1 =
13
+ * Major fix: Changes in WooCommerce 4.4.0 broke the Relevanssi searches. This makes the WooCommerce search work again.
14
+ * Minor fix: Excluding from logs didn't work if user IDs had spaces between them ('user_a, user_b'). Now the extra spaces don't matter.
15
+ * Minor fix: The asynchronous doc count action in the previous version could cause an infinite loop with the Snitch logger plugin. This is prevented now: the async action doesn't run after indexing unless a post is actually indexed.
16
+ * Minor fix: Relevanssi indexing procedure was triggered for autosaved drafts, causing possible problems with the asynchronous doc count action.
17
+ * Minor fix: The `relevanssi_index_custom_fields` filter hook was not applied when doing phrase matching, thus phrases could not be found when they were in custom fields added with the filter.
18
+
19
+ = 4.8.0 =
20
+ * Changed behaviour: Relevanssi now requires PHP 7.
21
+ * Changed behaviour: Relevanssi now sorts strings with `strnatcasecmp()` instead of `strcasecmp()`, leading to a more natural results with strings that include numbers.
22
+ * Changed behaviour: Relevanssi init is now moved from priority 10 to priority 1 on the `init` hook to avoid problems with missing TablePress compatibility.
23
+ * New feature: New filter hook `relevanssi_get_approved_comments_args` filters the arguments to `get_approved_comments` in comment indexing. This can be used to index custom comment types, for example.
24
+ * New feature: Content wrapped in the `noindex` tags is no longer used for excerpts.
25
+ * New feature: The `[et_pb_fullwidth_code]` shortcode is now removed completely, including the contents, when Relevanssi is indexing and building excerpts.
26
+ * Major fix: Relevanssi didn't index new comments when they were added; when a post was indexed or the whole index rebuilt, comment content was included. We don't know how long this bug has existed, but it is now fixed. Rebuild the index to get all comment content included in the index.
27
+ * Minor fix: Autoload has been disabled for several options that are not needed often.
28
+ * Minor fix: Phrase matching did not work correctly in visible custom fields.
29
+ * Minor fix: TablePress support could cause halting errors if posts were inserted before Relevanssi has loaded itself (on `init` priority 10). These errors will no longer happen.
30
+ * Minor fix: The doc count update, which is a heavy task, is now moved to an asynchronous action to avoid slowing down the site for users.
31
+ * Minor fix: Relevanssi only updates doc count on `relevanssi_insert_edit()` when the post is indexed.
32
+
33
  = 4.7.2 =
34
  * Minor fix: Media Library searches failed if Relevanssi was enabled in the WP admin, but the `attachment` post type wasn't indexed. Relevanssi will no longer block the default Media Library search in these cases.
35
  * Minor fix: Adds more backwards compatibility for the `relevanssi_indexing_restriction` change, there's now an alert on indexing tab if there's a problem.
lib/common.php CHANGED
@@ -301,7 +301,7 @@ function relevanssi_remove_punct( $a ) {
301
  $a = preg_replace( '/<(\d|\s)/', '\1', $a );
302
 
303
  $a = html_entity_decode( $a, ENT_QUOTES );
304
- $a = preg_replace( '/<[^>]*>/', ' ', $a );
305
 
306
  $punct_options = get_option( 'relevanssi_punctuation' );
307
 
@@ -719,11 +719,8 @@ function relevanssi_get_post_type( $post_id ) {
719
  global $relevanssi_post_array;
720
 
721
  $original_id = $post_id;
722
- $blog_id = -1;
723
- if ( function_exists( 'get_current_blog_id' ) ) {
724
- $blog_id = get_current_blog_id();
725
- $post_id = $blog_id . '|' . $post_id;
726
- }
727
 
728
  if ( isset( $relevanssi_post_array[ $post_id ] ) ) {
729
  return $relevanssi_post_array[ $post_id ]->post_type;
@@ -819,50 +816,6 @@ function relevanssi_add_synonyms( $query ) {
819
  return $query;
820
  }
821
 
822
-
823
- /**
824
- * Prints out post title with highlighting.
825
- *
826
- * Uses the global $post object. Reads the highlighted title from
827
- * $post->post_highlighted_title.
828
- *
829
- * @global object $post The global post object.
830
- *
831
- * @param boolean $echo If true, echo out the title. Default true.
832
- *
833
- * @return string If $echo is false, returns the title with highlights.
834
- */
835
- function relevanssi_the_title( $echo = true ) {
836
- global $post;
837
- if ( empty( $post->post_highlighted_title ) ) {
838
- $post->post_highlighted_title = $post->post_title;
839
- }
840
- if ( $echo ) {
841
- echo $post->post_highlighted_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
842
- }
843
- return $post->post_highlighted_title;
844
- }
845
-
846
- /**
847
- * Returns the post title with highlighting.
848
- *
849
- * Reads the highlighted title from $post->post_highlighted_title.
850
- *
851
- * @param int $post_id The post ID.
852
- *
853
- * @return string The post title with highlights.
854
- */
855
- function relevanssi_get_the_title( $post_id ) {
856
- $post = relevanssi_get_post( $post_id );
857
- if ( ! is_object( $post ) ) {
858
- return null;
859
- }
860
- if ( empty( $post->post_highlighted_title ) ) {
861
- $post->post_highlighted_title = $post->post_title;
862
- }
863
- return $post->post_highlighted_title;
864
- }
865
-
866
  /**
867
  * Updates the 'relevanssi_doc_count' option.
868
  *
@@ -1124,6 +1077,17 @@ function relevanssi_get_forbidden_post_types() {
1124
  'slides', // Qoda slides.
1125
  'carousels', // Qoda carousels.
1126
  'pretty-link', // Pretty Links.
 
 
 
 
 
 
 
 
 
 
 
1127
  );
1128
  }
1129
 
@@ -1148,7 +1112,9 @@ function relevanssi_get_forbidden_taxonomies() {
1148
  /**
1149
  * Filters out unwanted custom fields.
1150
  *
1151
- * Added to the relevanssi_custom_field_value filter hook.
 
 
1152
  *
1153
  * @see relevanssi_index_custom_fields()
1154
  *
@@ -1165,6 +1131,17 @@ function relevanssi_filter_custom_fields( $values, $field ) {
1165
  if ( isset( $unwanted_custom_fields[ $field ] ) ) {
1166
  $values = array();
1167
  }
 
 
 
 
 
 
 
 
 
 
 
1168
  return $values;
1169
  }
1170
 
@@ -1517,7 +1494,7 @@ function relevanssi_generate_list_of_custom_fields( $post_id, $custom_fields = n
1517
  }
1518
 
1519
  if ( ! is_array( $custom_fields ) ) {
1520
- return array();
1521
  }
1522
 
1523
  $custom_fields = array_unique( $custom_fields );
301
  $a = preg_replace( '/&lt;(\d|\s)/', '\1', $a );
302
 
303
  $a = html_entity_decode( $a, ENT_QUOTES );
304
+ $a = preg_replace( '/<[!a-z]*>/', ' ', $a );
305
 
306
  $punct_options = get_option( 'relevanssi_punctuation' );
307
 
719
  global $relevanssi_post_array;
720
 
721
  $original_id = $post_id;
722
+ $blog_id = get_current_blog_id();
723
+ $post_id = $blog_id . '|' . $post_id;
 
 
 
724
 
725
  if ( isset( $relevanssi_post_array[ $post_id ] ) ) {
726
  return $relevanssi_post_array[ $post_id ]->post_type;
816
  return $query;
817
  }
818
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
819
  /**
820
  * Updates the 'relevanssi_doc_count' option.
821
  *
1077
  'slides', // Qoda slides.
1078
  'carousels', // Qoda carousels.
1079
  'pretty-link', // Pretty Links.
1080
+ 'fusion_tb_layout', // Fusion Builder.
1081
+ 'fusion_tb_section', // Fusion Builder.
1082
+ 'fusion_form', // Fusion Builder.
1083
+ 'fusion_icons', // Fusion Builder.
1084
+ 'fusion_template', // Fusion Builder.
1085
+ 'fusion_element', // Fusion Builder.
1086
+ 'acfe-dbt', // ACF Extended.
1087
+ 'acfe-form', // ACF Extended.
1088
+ 'acfe-dop', // ACF Extended.
1089
+ 'acfe-dpt', // ACF Extended.
1090
+ 'acfe-dt', // ACF Extended.
1091
  );
1092
  }
1093
 
1112
  /**
1113
  * Filters out unwanted custom fields.
1114
  *
1115
+ * Added to the relevanssi_custom_field_value filter hook. This function removes
1116
+ * visible custom fields that are known to contain unwanted content and also
1117
+ * removes ACF meta fields (fields where content begins with `field_`).
1118
  *
1119
  * @see relevanssi_index_custom_fields()
1120
  *
1131
  if ( isset( $unwanted_custom_fields[ $field ] ) ) {
1132
  $values = array();
1133
  }
1134
+
1135
+ $values = array_map(
1136
+ function( $value ) {
1137
+ if ( is_string( $value ) && 'field_' === substr( $value, 0, 6 ) ) {
1138
+ return '';
1139
+ }
1140
+ return $value;
1141
+ },
1142
+ $values
1143
+ );
1144
+
1145
  return $values;
1146
  }
1147
 
1494
  }
1495
 
1496
  if ( ! is_array( $custom_fields ) ) {
1497
+ $custom_fields = array();
1498
  }
1499
 
1500
  $custom_fields = array_unique( $custom_fields );
lib/compatibility/acf.php CHANGED
@@ -76,9 +76,9 @@ function relevanssi_index_acf( &$insert_data, $post_id, $field_name, $field_valu
76
  * string. If your custom field values are arrays or objects, use this
77
  * filter hook to convert them into strings.
78
  *
79
- * @param any The ACF field value.
80
- * @param string The ACF field name.
81
- * @param int The post ID.
82
  *
83
  * @return string|int The field value.
84
  */
76
  * string. If your custom field values are arrays or objects, use this
77
  * filter hook to convert them into strings.
78
  *
79
+ * @param mixed $field_content The ACF field value.
80
+ * @param string $field_name The ACF field name.
81
+ * @param int $post_id The post ID.
82
  *
83
  * @return string|int The field value.
84
  */
lib/compatibility/aioseo.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * /lib/compatibility/aioseo.php
4
+ *
5
+ * All-in-One SEO noindex filtering function.
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_do_not_index', 'relevanssi_aioseo_noindex', 10, 2 );
14
+ add_filter( 'relevanssi_indexing_restriction', 'relevanssi_aioseo_exclude' );
15
+ add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_aioseo_form', 20 );
16
+ add_action( 'relevanssi_indexing_options', 'relevanssi_aioseo_options' );
17
+
18
+ /**
19
+ * Blocks indexing of posts marked "noindex" in the All-in-One SEO settings.
20
+ *
21
+ * Attaches to the 'relevanssi_do_not_index' filter hook.
22
+ *
23
+ * @param boolean $do_not_index True, if the post shouldn't be indexed.
24
+ * @param integer $post_id The post ID number.
25
+ *
26
+ * @return string|boolean If the post shouldn't be indexed, this returns
27
+ * 'aioseo_seo'. The value may also be a boolean.
28
+ */
29
+ function relevanssi_aioseo_noindex( bool $do_not_index, int $post_id ) {
30
+ $noindex_posts = relevanssi_aioseo_get_noindex_posts();
31
+ if ( in_array( $post_id, $noindex_posts, true ) ) {
32
+ $do_not_index = 'All-in-One SEO';
33
+ }
34
+ return $do_not_index;
35
+ }
36
+
37
+ /**
38
+ * Excludes the "noindex" posts from Relevanssi indexing.
39
+ *
40
+ * Adds a MySQL query restriction that blocks posts that have the aioseo SEO
41
+ * "noindex" setting set to "1" from indexing.
42
+ *
43
+ * @param array $restriction An array with two values: 'mysql' for the MySQL
44
+ * query restriction to modify, 'reason' for the reason of restriction.
45
+ */
46
+ function relevanssi_aioseo_exclude( array $restriction ) {
47
+ global $wpdb;
48
+
49
+ $restriction['mysql'] .= " AND post.ID NOT IN (SELECT post_id FROM
50
+ {$wpdb->prefix}aioseo_posts WHERE robots_noindex = '1' ) ";
51
+ $restriction['reason'] .= ' All-in-One SEO';
52
+
53
+ return $restriction;
54
+ }
55
+
56
+ /**
57
+ * Fetches the post IDs where robots_noindex is set to 1 in the aioseo_posts
58
+ * table.
59
+ *
60
+ * @return array An array of post IDs.
61
+ */
62
+ function relevanssi_aioseo_get_noindex_posts() {
63
+ global $wpdb, $relevanssi_aioseo_noindex_cache;
64
+ if ( ! empty( $relevanssi_aioseo_noindex_cache ) ) {
65
+ return $relevanssi_aioseo_noindex_cache;
66
+ }
67
+ $relevanssi_aioseo_noindex_cache = $wpdb->get_col( "SELECT post_id FROM {$wpdb->prefix}aioseo_posts WHERE 'robots_noindex' = '1'" );
68
+ return $relevanssi_aioseo_noindex_cache;
69
+ }
70
+
71
+ /**
72
+ * Prints out the form fields for disabling the feature.
73
+ */
74
+ function relevanssi_aioseo_form() {
75
+ $seo_noindex = get_option( 'relevanssi_seo_noindex' );
76
+ $seo_noindex = relevanssi_check( $seo_noindex );
77
+
78
+ ?>
79
+ <tr>
80
+ <th scope="row">
81
+ <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use All-in-One SEO noindex', 'relevanssi' ); ?></label>
82
+ </th>
83
+ <td>
84
+ <label for='relevanssi_seo_noindex'>
85
+ <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
86
+ <?php esc_html_e( 'Use All-in-One SEO noindex.', 'relevanssi' ); ?>
87
+ </label>
88
+ <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in All-in-One SEO settings.', 'relevanssi' ); ?></p>
89
+ </td>
90
+ </tr>
91
+ <?php
92
+ }
93
+
94
+ /**
95
+ * Saves the SEO No index option.
96
+ *
97
+ * @param array $request An array of option values from the request.
98
+ */
99
+ function relevanssi_aioseo_options( array $request ) {
100
+ relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
101
+ }
lib/compatibility/gutenberg.php CHANGED
@@ -79,7 +79,20 @@ function relevanssi_gutenberg_block_rendering( $content ) {
79
  ! isset( $block['attrs']['className'] )
80
  || false === strstr( $block['attrs']['className'], 'relevanssi_noindex' )
81
  ) {
82
- $output .= render_block( $block );
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
84
  }
85
 
79
  ! isset( $block['attrs']['className'] )
80
  || false === strstr( $block['attrs']['className'], 'relevanssi_noindex' )
81
  ) {
82
+ /**
83
+ * Filters the Gutenberg block after it is rendered.
84
+ *
85
+ * The value is the output from render_block( $block ). Feel free to
86
+ * modify it as you wish.
87
+ *
88
+ * @see render_block
89
+ *
90
+ * @param string The rendered block content.
91
+ * @param array $block The Gutenberg block being rendered.
92
+ *
93
+ * @return string The filtered block content.
94
+ */
95
+ $output .= apply_filters( 'relevanssi_rendered_block', render_block( $block ), $block );
96
  }
97
  }
98
 
lib/compatibility/oxygen.php CHANGED
@@ -12,7 +12,8 @@
12
 
13
  add_filter( 'relevanssi_custom_field_value', 'relevanssi_oxygen_compatibility', 10, 3 );
14
  add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_oxygen' );
15
- add_filter( 'pre_option_relevanssi_index_fields', 'relevanssi_oxygen_fix_none_setting' );
 
16
  /**
17
  * Cleans up the Oxygen Builder custom field for Relevanssi consumption.
18
  *
@@ -116,7 +117,9 @@ function relevanssi_add_oxygen( $fields ) {
116
  if ( ! is_array( $fields ) ) {
117
  $fields = array();
118
  }
119
- $fields[] = 'ct_builder_shortcodes';
 
 
120
  return $fields;
121
  }
122
 
@@ -124,7 +127,9 @@ function relevanssi_add_oxygen( $fields ) {
124
  * Makes sure the Oxygen builder shortcode is included in the index, even when
125
  * the custom field setting is set to 'none'.
126
  *
127
- * @param string $value The custom field indexing setting value.
 
 
128
  *
129
  * @return string If value is undefined, it's set to 'ct_builder_shortcodes'.
130
  */
@@ -132,5 +137,6 @@ function relevanssi_oxygen_fix_none_setting( $value ) {
132
  if ( ! $value ) {
133
  $value = 'ct_builder_shortcodes';
134
  }
 
135
  return $value;
136
  }
12
 
13
  add_filter( 'relevanssi_custom_field_value', 'relevanssi_oxygen_compatibility', 10, 3 );
14
  add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_oxygen' );
15
+ add_filter( 'option_relevanssi_index_fields', 'relevanssi_oxygen_fix_none_setting' );
16
+
17
  /**
18
  * Cleans up the Oxygen Builder custom field for Relevanssi consumption.
19
  *
117
  if ( ! is_array( $fields ) ) {
118
  $fields = array();
119
  }
120
+ if ( ! in_array( 'ct_builder_shortcodes', $fields, true ) ) {
121
+ $fields[] = 'ct_builder_shortcodes';
122
+ }
123
  return $fields;
124
  }
125
 
127
  * Makes sure the Oxygen builder shortcode is included in the index, even when
128
  * the custom field setting is set to 'none'.
129
  *
130
+ * @param string $value The custom field indexing setting value. The parameter
131
+ * is ignored, Relevanssi disables this filter and then checks the option to
132
+ * see what the value is.
133
  *
134
  * @return string If value is undefined, it's set to 'ct_builder_shortcodes'.
135
  */
137
  if ( ! $value ) {
138
  $value = 'ct_builder_shortcodes';
139
  }
140
+
141
  return $value;
142
  }
lib/compatibility/rankmath.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  add_filter( 'relevanssi_do_not_index', 'relevanssi_rankmath_noindex', 10, 2 );
13
  add_filter( 'relevanssi_indexing_restriction', 'relevanssi_rankmath_exclude' );
 
 
14
 
15
  /**
16
  * Blocks indexing of posts marked "noindex" in the Rank Math settings.
@@ -57,3 +59,35 @@ function relevanssi_rankmath_exclude( $restriction ) {
57
  $restriction['reason'] .= ' Rank Math';
58
  return $restriction;
59
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  add_filter( 'relevanssi_do_not_index', 'relevanssi_rankmath_noindex', 10, 2 );
13
  add_filter( 'relevanssi_indexing_restriction', 'relevanssi_rankmath_exclude' );
14
+ add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_rankmath_form', 20 );
15
+ add_action( 'relevanssi_indexing_options', 'relevanssi_rankmath_options' );
16
 
17
  /**
18
  * Blocks indexing of posts marked "noindex" in the Rank Math settings.
59
  $restriction['reason'] .= ' Rank Math';
60
  return $restriction;
61
  }
62
+
63
+ /**
64
+ * Prints out the form fields for disabling the feature.
65
+ */
66
+ function relevanssi_rankmath_form() {
67
+ $seo_noindex = get_option( 'relevanssi_seo_noindex' );
68
+ $seo_noindex = relevanssi_check( $seo_noindex );
69
+
70
+ ?>
71
+ <tr>
72
+ <th scope="row">
73
+ <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use Rank Math SEO noindex', 'relevanssi' ); ?></label>
74
+ </th>
75
+ <td>
76
+ <label for='relevanssi_seo_noindex'>
77
+ <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
78
+ <?php esc_html_e( 'Use Rank Math SEO noindex.', 'relevanssi' ); ?>
79
+ </label>
80
+ <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in Rank Math SEO settings.', 'relevanssi' ); ?></p>
81
+ </td>
82
+ </tr>
83
+ <?php
84
+ }
85
+
86
+ /**
87
+ * Saves the SEO No index option.
88
+ *
89
+ * @param array $request An array of option values from the request.
90
+ */
91
+ function relevanssi_rankmath_options( array $request ) {
92
+ relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
93
+ }
lib/compatibility/seoframework.php CHANGED
@@ -12,6 +12,8 @@
12
 
13
  add_filter( 'relevanssi_do_not_index', 'relevanssi_seoframework_noindex', 10, 2 );
14
  add_filter( 'relevanssi_indexing_restriction', 'relevanssi_seoframework_exclude' );
 
 
15
 
16
  /**
17
  * Blocks indexing of posts marked "Exclude this page from all search queries
@@ -52,3 +54,35 @@ function relevanssi_seoframework_exclude( $restriction ) {
52
  $restriction['reason'] .= ' SEO Framework';
53
  return $restriction;
54
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  add_filter( 'relevanssi_do_not_index', 'relevanssi_seoframework_noindex', 10, 2 );
14
  add_filter( 'relevanssi_indexing_restriction', 'relevanssi_seoframework_exclude' );
15
+ add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_seoframework_form', 20 );
16
+ add_action( 'relevanssi_indexing_options', 'relevanssi_seoframework_options' );
17
 
18
  /**
19
  * Blocks indexing of posts marked "Exclude this page from all search queries
54
  $restriction['reason'] .= ' SEO Framework';
55
  return $restriction;
56
  }
57
+
58
+ /**
59
+ * Prints out the form fields for disabling the feature.
60
+ */
61
+ function relevanssi_seoframework_form() {
62
+ $seo_noindex = get_option( 'relevanssi_seo_noindex' );
63
+ $seo_noindex = relevanssi_check( $seo_noindex );
64
+
65
+ ?>
66
+ <tr>
67
+ <th scope="row">
68
+ <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use SEO Framework noindex', 'relevanssi' ); ?></label>
69
+ </th>
70
+ <td>
71
+ <label for='relevanssi_seo_noindex'>
72
+ <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
73
+ <?php esc_html_e( 'Use SEO Framework noindex.', 'relevanssi' ); ?>
74
+ </label>
75
+ <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in SEO Framework settings.', 'relevanssi' ); ?></p>
76
+ </td>
77
+ </tr>
78
+ <?php
79
+ }
80
+
81
+ /**
82
+ * Saves the SEO No index option.
83
+ *
84
+ * @param array $request An array of option values from the request.
85
+ */
86
+ function relevanssi_seoframework_options( array $request ) {
87
+ relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
88
+ }
lib/compatibility/seopress.php CHANGED
@@ -13,6 +13,8 @@
13
 
14
  add_filter( 'relevanssi_do_not_index', 'relevanssi_seopress_noindex', 10, 2 );
15
  add_filter( 'relevanssi_indexing_restriction', 'relevanssi_seopress_exclude' );
 
 
16
 
17
  /**
18
  * Blocks indexing of posts marked "noindex" in the SEOPress settings.
@@ -58,3 +60,35 @@ function relevanssi_seopress_exclude( $restriction ) {
58
  $restriction['reason'] .= 'SEOPress';
59
  return $restriction;
60
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  add_filter( 'relevanssi_do_not_index', 'relevanssi_seopress_noindex', 10, 2 );
15
  add_filter( 'relevanssi_indexing_restriction', 'relevanssi_seopress_exclude' );
16
+ add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_seopress_form', 20 );
17
+ add_action( 'relevanssi_indexing_options', 'relevanssi_seopress_options' );
18
 
19
  /**
20
  * Blocks indexing of posts marked "noindex" in the SEOPress settings.
60
  $restriction['reason'] .= 'SEOPress';
61
  return $restriction;
62
  }
63
+
64
+ /**
65
+ * Prints out the form fields for disabling the feature.
66
+ */
67
+ function relevanssi_seopress_form() {
68
+ $seo_noindex = get_option( 'relevanssi_seo_noindex' );
69
+ $seo_noindex = relevanssi_check( $seo_noindex );
70
+
71
+ ?>
72
+ <tr>
73
+ <th scope="row">
74
+ <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use SEOPress noindex', 'relevanssi' ); ?></label>
75
+ </th>
76
+ <td>
77
+ <label for='relevanssi_seo_noindex'>
78
+ <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
79
+ <?php esc_html_e( 'Use SEOPress noindex.', 'relevanssi' ); ?>
80
+ </label>
81
+ <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in SEOPress settings.', 'relevanssi' ); ?></p>
82
+ </td>
83
+ </tr>
84
+ <?php
85
+ }
86
+
87
+ /**
88
+ * Saves the SEO No index option.
89
+ *
90
+ * @param array $request An array of option values from the request.
91
+ */
92
+ function relevanssi_seopress_options( array $request ) {
93
+ relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
94
+ }
lib/compatibility/yoast-seo.php CHANGED
@@ -12,6 +12,8 @@
12
 
13
  add_filter( 'relevanssi_do_not_index', 'relevanssi_yoast_noindex', 10, 2 );
14
  add_filter( 'relevanssi_indexing_restriction', 'relevanssi_yoast_exclude' );
 
 
15
 
16
  /**
17
  * Blocks indexing of posts marked "noindex" in the Yoast SEO settings.
@@ -58,3 +60,35 @@ function relevanssi_yoast_exclude( $restriction ) {
58
  $restriction['reason'] .= ' Yoast SEO';
59
  return $restriction;
60
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  add_filter( 'relevanssi_do_not_index', 'relevanssi_yoast_noindex', 10, 2 );
14
  add_filter( 'relevanssi_indexing_restriction', 'relevanssi_yoast_exclude' );
15
+ add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_yoast_form', 20 );
16
+ add_action( 'relevanssi_indexing_options', 'relevanssi_yoast_options' );
17
 
18
  /**
19
  * Blocks indexing of posts marked "noindex" in the Yoast SEO settings.
60
  $restriction['reason'] .= ' Yoast SEO';
61
  return $restriction;
62
  }
63
+
64
+ /**
65
+ * Prints out the form fields for disabling the feature.
66
+ */
67
+ function relevanssi_yoast_form() {
68
+ $seo_noindex = get_option( 'relevanssi_seo_noindex' );
69
+ $seo_noindex = relevanssi_check( $seo_noindex );
70
+
71
+ ?>
72
+ <tr>
73
+ <th scope="row">
74
+ <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use Yoast SEO noindex', 'relevanssi' ); ?></label>
75
+ </th>
76
+ <td>
77
+ <label for='relevanssi_seo_noindex'>
78
+ <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
79
+ <?php esc_html_e( 'Use Yoast SEO noindex.', 'relevanssi' ); ?>
80
+ </label>
81
+ <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in Yoast SEO settings.', 'relevanssi' ); ?></p>
82
+ </td>
83
+ </tr>
84
+ <?php
85
+ }
86
+
87
+ /**
88
+ * Saves the SEO No index option.
89
+ *
90
+ * @param array $request An array of option values from the request.
91
+ */
92
+ function relevanssi_yoast_options( array $request ) {
93
+ relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
94
+ }
lib/indexing.php CHANGED
@@ -1437,8 +1437,8 @@ function relevanssi_index_content( &$insert_data, $post_object, $min_word_length
1437
  *
1438
  * @author Alexander Gieg
1439
  *
1440
- * @param string The additional content.
1441
- * @param object $post_object The post object.
1442
  */
1443
  $additional_content = trim( apply_filters( 'relevanssi_content_to_index', '', $post_object ) );
1444
  if ( ! empty( $additional_content ) ) {
1437
  *
1438
  * @author Alexander Gieg
1439
  *
1440
+ * @param string The additional content.
1441
+ * @param WP_Post $post_object The post object.
1442
  */
1443
  $additional_content = trim( apply_filters( 'relevanssi_content_to_index', '', $post_object ) );
1444
  if ( ! empty( $additional_content ) ) {
lib/init.php CHANGED
@@ -178,6 +178,10 @@ function relevanssi_init() {
178
  require_once 'compatibility/yoast-seo.php';
179
  }
180
 
 
 
 
 
181
  if ( function_exists( 'seopress_get_toggle_titles_option' ) && '1' === seopress_get_toggle_titles_option() ) {
182
  require_once 'compatibility/seopress.php';
183
  }
178
  require_once 'compatibility/yoast-seo.php';
179
  }
180
 
181
+ if ( defined( 'AIOSEO_DIR' ) ) {
182
+ require_once 'compatibility/aioseo.php';
183
+ }
184
+
185
  if ( function_exists( 'seopress_get_toggle_titles_option' ) && '1' === seopress_get_toggle_titles_option() ) {
186
  require_once 'compatibility/seopress.php';
187
  }
lib/install.php CHANGED
@@ -119,6 +119,7 @@ function _relevanssi_install() {
119
  )
120
  );
121
  add_option( 'relevanssi_respect_exclude', 'on' );
 
122
  add_option( 'relevanssi_show_matches', '' );
123
  add_option( 'relevanssi_show_matches_text', '(Search hits: %body% in body, %title% in title, %categories% in categories, %tags% in tags, %taxonomies% in other taxonomies, %comments% in comments. Score: %score%)' );
124
  add_option( 'relevanssi_stopwords', array() );
119
  )
120
  );
121
  add_option( 'relevanssi_respect_exclude', 'on' );
122
+ add_option( 'relevanssi_seo_noindex', 'on' );
123
  add_option( 'relevanssi_show_matches', '' );
124
  add_option( 'relevanssi_show_matches_text', '(Search hits: %body% in body, %title% in title, %categories% in categories, %tags% in tags, %taxonomies% in other taxonomies, %comments% in comments. Score: %score%)' );
125
  add_option( 'relevanssi_stopwords', array() );
lib/interface.php CHANGED
@@ -121,11 +121,6 @@ function relevanssi_search_stats() {
121
  }
122
  }
123
 
124
- wp_enqueue_style( 'dashboard' );
125
- wp_print_styles( 'dashboard' );
126
- wp_enqueue_script( 'dashboard' );
127
- wp_print_scripts( 'dashboard' );
128
-
129
  printf( "<div class='wrap'><h2>%s</h2>", esc_html( $options_txt ) );
130
 
131
  if ( 'on' === get_option( 'relevanssi_log_queries' ) ) {
@@ -145,11 +140,6 @@ function relevanssi_admin_search_page() {
145
 
146
  $options_txt = __( 'Admin Search', 'relevanssi' );
147
 
148
- wp_enqueue_style( 'dashboard' );
149
- wp_print_styles( 'dashboard' );
150
- wp_enqueue_script( 'dashboard' );
151
- wp_print_scripts( 'dashboard' );
152
-
153
  printf( "<div class='wrap'><h2>%s</h2>", esc_html( $options_txt ) );
154
 
155
  require_once dirname( $relevanssi_variables['file'] ) . '/lib/tabs/search-page.php';
@@ -409,11 +399,6 @@ function relevanssi_date_queries( $days, $title, $version = 'good' ) {
409
  function relevanssi_options_form() {
410
  global $relevanssi_variables, $wpdb;
411
 
412
- wp_enqueue_style( 'dashboard' );
413
- wp_print_styles( 'dashboard' );
414
- wp_enqueue_script( 'dashboard' );
415
- wp_print_scripts( 'dashboard' );
416
-
417
  echo "<div class='postbox-container'>";
418
  echo "<form method='post'>";
419
 
@@ -576,7 +561,18 @@ function relevanssi_add_admin_scripts( $hook ) {
576
  'settings_page_relevanssi/relevanssi',
577
  'dashboard_page_relevanssi_admin_search',
578
  );
579
- if ( ! in_array( $hook, $acceptable_hooks, true ) ) {
 
 
 
 
 
 
 
 
 
 
 
580
  return;
581
  }
582
 
121
  }
122
  }
123
 
 
 
 
 
 
124
  printf( "<div class='wrap'><h2>%s</h2>", esc_html( $options_txt ) );
125
 
126
  if ( 'on' === get_option( 'relevanssi_log_queries' ) ) {
140
 
141
  $options_txt = __( 'Admin Search', 'relevanssi' );
142
 
 
 
 
 
 
143
  printf( "<div class='wrap'><h2>%s</h2>", esc_html( $options_txt ) );
144
 
145
  require_once dirname( $relevanssi_variables['file'] ) . '/lib/tabs/search-page.php';
399
  function relevanssi_options_form() {
400
  global $relevanssi_variables, $wpdb;
401
 
 
 
 
 
 
402
  echo "<div class='postbox-container'>";
403
  echo "<form method='post'>";
404
 
561
  'settings_page_relevanssi/relevanssi',
562
  'dashboard_page_relevanssi_admin_search',
563
  );
564
+ /**
565
+ * Filters the hooks where Relevanssi scripts are enqueued.
566
+ *
567
+ * By default Relevanssi only enqueues the Relevanssi admin javascript on
568
+ * specific admin page hooks to avoid polluting the admin. If you want to
569
+ * move things around, this means the javascript bits won't work. You can
570
+ * introduce new hooks with this filter hook.
571
+ *
572
+ * @param array An array of page hook strings where Relevanssi scripts are
573
+ * added.
574
+ */
575
+ if ( ! in_array( $hook, apply_filters( 'relevanssi_acceptable_hooks', $acceptable_hooks ), true ) ) {
576
  return;
577
  }
578
 
lib/log.php CHANGED
@@ -31,7 +31,17 @@ function relevanssi_update_log( $query, $hits ) {
31
  $user_agent = '';
32
  if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
33
  $user_agent = $_SERVER['HTTP_USER_AGENT'];
34
- $bots = array( 'Google' => 'Mediapartners-Google' );
 
 
 
 
 
 
 
 
 
 
35
 
36
  /**
37
  * Filters the bots Relevanssi should block from logs.
@@ -284,6 +294,10 @@ function relevanssi_export_log() {
284
  $data = $wpdb->get_results( 'SELECT * FROM ' . $relevanssi_variables['log_table'], ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
285
  ob_start();
286
  $df = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions
 
 
 
 
287
  fputcsv( $df, array_keys( reset( $data ) ) );
288
  foreach ( $data as $row ) {
289
  fputcsv( $df, $row );
31
  $user_agent = '';
32
  if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
33
  $user_agent = $_SERVER['HTTP_USER_AGENT'];
34
+ $bots = array(
35
+ 'Google 1' => 'Mediapartners-Google',
36
+ 'Google 2' => 'Googlebot',
37
+ 'Bing' => 'Bingbot',
38
+ 'Yahoo' => 'Slurp',
39
+ 'DuckDuckGo' => 'DuckDuckBot',
40
+ 'Baidu' => 'Baiduspider',
41
+ 'Yandex' => 'YandexBot',
42
+ 'Sogou' => 'Sogou',
43
+ 'Exalead' => 'Exabot',
44
+ );
45
 
46
  /**
47
  * Filters the bots Relevanssi should block from logs.
294
  $data = $wpdb->get_results( 'SELECT * FROM ' . $relevanssi_variables['log_table'], ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
295
  ob_start();
296
  $df = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions
297
+ if ( empty( $data ) ) {
298
+ fputcsv( $df, array( __( 'No search keywords logged.', 'relevanssi' ) ) );
299
+ die();
300
+ }
301
  fputcsv( $df, array_keys( reset( $data ) ) );
302
  foreach ( $data as $row ) {
303
  fputcsv( $df, $row );
lib/options.php CHANGED
@@ -38,6 +38,7 @@ function update_relevanssi_options() {
38
  if ( RELEVANSSI_PREMIUM ) {
39
  update_option( 'relevanssi_index_terms', array_keys( $index_terms_list ), false );
40
  }
 
41
  }
42
 
43
  if ( 'searching' === $_REQUEST['tab'] ) {
38
  if ( RELEVANSSI_PREMIUM ) {
39
  update_option( 'relevanssi_index_terms', array_keys( $index_terms_list ), false );
40
  }
41
+ do_action( 'relevanssi_indexing_options', $_REQUEST );
42
  }
43
 
44
  if ( 'searching' === $_REQUEST['tab'] ) {
lib/search.php CHANGED
@@ -821,6 +821,7 @@ function relevanssi_search( $args ) {
821
  'term_hits' => $term_hits,
822
  'query' => $q,
823
  'doc_weights' => $doc_weight,
 
824
  );
825
 
826
  return $return;
@@ -873,14 +874,9 @@ function relevanssi_do_query( &$query ) {
873
  $return = relevanssi_search( $search_params );
874
  }
875
 
876
- $hits = array();
877
- if ( isset( $return['hits'] ) ) {
878
- $hits = $return['hits'];
879
- }
880
- $q = '';
881
- if ( isset( $return['query'] ) ) {
882
- $q = $return['query'];
883
- }
884
 
885
  $filter_data = array( $hits, $q );
886
  /**
@@ -915,7 +911,26 @@ function relevanssi_do_query( &$query ) {
915
 
916
  $update_log = get_option( 'relevanssi_log_queries' );
917
  if ( 'on' === $update_log ) {
918
- relevanssi_update_log( $q, $hits_count );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
919
  }
920
 
921
  $make_excerpts = get_option( 'relevanssi_excerpts' );
@@ -1008,6 +1023,16 @@ function relevanssi_do_query( &$query ) {
1008
  $query->posts = $posts;
1009
  $query->post_count = count( $posts );
1010
 
 
 
 
 
 
 
 
 
 
 
1011
  $relevanssi_active = false;
1012
 
1013
  return $posts;
821
  'term_hits' => $term_hits,
822
  'query' => $q,
823
  'doc_weights' => $doc_weight,
824
+ 'query_no_synonyms' => $q_no_synonyms,
825
  );
826
 
827
  return $return;
874
  $return = relevanssi_search( $search_params );
875
  }
876
 
877
+ $hits = $return['hits'] ?? array();
878
+ $q = $return['query'] ?? '';
879
+ $q_no_synonyms = $return['query_no_synonyms'] ?? '';
 
 
 
 
 
880
 
881
  $filter_data = array( $hits, $q );
882
  /**
911
 
912
  $update_log = get_option( 'relevanssi_log_queries' );
913
  if ( 'on' === $update_log ) {
914
+ /**
915
+ * Filters the query.
916
+ *
917
+ * By default, Relevanssi logs the original query without the added
918
+ * synonyms. This filter hook gets the query with the synonyms added as
919
+ * a second parameter, so if you wish, you can log the query with the
920
+ * synonyms added.
921
+ *
922
+ * @param string $q_no_synonyms The query string without synonyms.
923
+ * @param string $q The query string with synonyms.
924
+ * @param WP_Query $query The WP_Query that triggered the
925
+ * logging.
926
+ */
927
+ $query_string = apply_filters(
928
+ 'relevanssi_log_query',
929
+ $q_no_synonyms,
930
+ $q,
931
+ $query
932
+ );
933
+ relevanssi_update_log( $query_string, $hits_count );
934
  }
935
 
936
  $make_excerpts = get_option( 'relevanssi_excerpts' );
1023
  $query->posts = $posts;
1024
  $query->post_count = count( $posts );
1025
 
1026
+ /**
1027
+ * If true, Relevanssi adds a list of all post IDs found in the query
1028
+ * object in $query->relevanssi_all_results.
1029
+ *
1030
+ * @param boolean If true, enable the feature. Default false.
1031
+ */
1032
+ if ( apply_filters( 'relevanssi_add_all_results', false ) ) {
1033
+ $query->relevanssi_all_results = wp_list_pluck( $hits, 'ID' );
1034
+ }
1035
+
1036
  $relevanssi_active = false;
1037
 
1038
  return $posts;
lib/sorting.php CHANGED
@@ -154,7 +154,7 @@ function relevanssi_get_compare_values( $key, $item_1, $item_2 ) {
154
  if ( 'meta_value' === $key || 'meta_value_num' === $key ) {
155
  global $wp_query;
156
  // Get the name of the field from the global WP_Query.
157
- $key = $wp_query->query_vars['meta_key'];
158
 
159
  if ( empty( $key ) ) {
160
  // If empty, try the Relevanssi meta_query.
154
  if ( 'meta_value' === $key || 'meta_value_num' === $key ) {
155
  global $wp_query;
156
  // Get the name of the field from the global WP_Query.
157
+ $key = $wp_query->query_vars['meta_key'] ?? null;
158
 
159
  if ( empty( $key ) ) {
160
  // If empty, try the Relevanssi meta_query.
lib/tabs/indexing-tab.php CHANGED
@@ -427,29 +427,13 @@ function relevanssi_indexing_tab() {
427
  </tr>
428
 
429
  <?php
430
- if ( function_exists( 'relevanssi_form_disable_shortcodes' ) ) {
431
- relevanssi_form_disable_shortcodes();
432
- }
433
  ?>
434
 
435
  </table>
436
 
437
  <?php
438
- if ( function_exists( 'relevanssi_form_index_users' ) ) {
439
- relevanssi_form_index_users();
440
- }
441
- if ( function_exists( 'relevanssi_form_index_synonyms' ) ) {
442
- relevanssi_form_index_synonyms();
443
- }
444
- if ( function_exists( 'relevanssi_form_index_taxonomies' ) ) {
445
- relevanssi_form_index_taxonomies();
446
- }
447
- if ( function_exists( 'relevanssi_form_index_post_type_archives' ) ) {
448
- relevanssi_form_index_post_type_archives();
449
- }
450
- if ( function_exists( 'relevanssi_form_index_pdf_parent' ) ) {
451
- relevanssi_form_index_pdf_parent();
452
- }
453
  ?>
454
 
455
  <h2><?php esc_html_e( 'Advanced indexing settings', 'relevanssi' ); ?></h2>
@@ -528,15 +512,7 @@ function relevanssi_indexing_tab() {
528
  </td>
529
  </tr>
530
  <?php
531
- if ( function_exists( 'relevanssi_form_thousands_separator' ) ) {
532
- relevanssi_form_thousands_separator();
533
- }
534
- if ( function_exists( 'relevanssi_form_mysql_columns' ) ) {
535
- relevanssi_form_mysql_columns();
536
- }
537
- if ( function_exists( 'relevanssi_form_internal_links' ) ) {
538
- relevanssi_form_internal_links();
539
- }
540
  ?>
541
 
542
  </table>
427
  </tr>
428
 
429
  <?php
430
+ do_action( 'relevanssi_indexing_tab_shortcodes' );
 
 
431
  ?>
432
 
433
  </table>
434
 
435
  <?php
436
+ do_action( 'relevanssi_indexing_tab' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  ?>
438
 
439
  <h2><?php esc_html_e( 'Advanced indexing settings', 'relevanssi' ); ?></h2>
512
  </td>
513
  </tr>
514
  <?php
515
+ do_action( 'relevanssi_indexing_tab_advanced' );
 
 
 
 
 
 
 
 
516
  ?>
517
 
518
  </table>
lib/tabs/overview-tab.php CHANGED
@@ -106,7 +106,7 @@ function relevanssi_overview_tab() {
106
  <td>
107
  <p><a href="https://www.relevanssi.com/buy-premium"><?php esc_html_e( 'Buy Relevanssi Premium now', 'relevanssi' ); ?></a> –
108
  <?php // Translators: %1$s is the coupon code, %2$s is the year it expires. ?>
109
- <?php printf( esc_html__( 'use coupon code %1$s for 20%% discount (valid at least until the end of %2$s)', 'relevanssi' ), '<strong>FREE2020</strong>', '2020' ); ?></p>
110
  <p><?php esc_html_e( 'Here are some improvements Relevanssi Premium offers:', 'relevanssi' ); ?></p>
111
  <ul class="relevanssi_ul">
112
  <li><?php esc_html_e( 'PDF content indexing', 'relevanssi' ); ?></li>
106
  <td>
107
  <p><a href="https://www.relevanssi.com/buy-premium"><?php esc_html_e( 'Buy Relevanssi Premium now', 'relevanssi' ); ?></a> –
108
  <?php // Translators: %1$s is the coupon code, %2$s is the year it expires. ?>
109
+ <?php printf( esc_html__( 'use coupon code %1$s for 20%% discount (valid at least until the end of %2$s)', 'relevanssi' ), '<strong>FREE2021</strong>', '2021' ); ?></p>
110
  <p><?php esc_html_e( 'Here are some improvements Relevanssi Premium offers:', 'relevanssi' ); ?></p>
111
  <ul class="relevanssi_ul">
112
  <li><?php esc_html_e( 'PDF content indexing', 'relevanssi' ); ?></li>
lib/uninstall.php CHANGED
@@ -100,6 +100,7 @@ function relevanssi_uninstall_free() {
100
  delete_option( 'relevanssi_post_type_weights' );
101
  delete_option( 'relevanssi_punctuation' );
102
  delete_option( 'relevanssi_respect_exclude' );
 
103
  delete_option( 'relevanssi_show_matches' );
104
  delete_option( 'relevanssi_show_matches_text' );
105
  delete_option( 'relevanssi_show_post_controls' );
100
  delete_option( 'relevanssi_post_type_weights' );
101
  delete_option( 'relevanssi_punctuation' );
102
  delete_option( 'relevanssi_respect_exclude' );
103
+ delete_option( 'relevanssi_seo_noindex' );
104
  delete_option( 'relevanssi_show_matches' );
105
  delete_option( 'relevanssi_show_matches_text' );
106
  delete_option( 'relevanssi_show_post_controls' );
lib/utils.php CHANGED
@@ -30,7 +30,7 @@ function get_relevanssi_taxonomy_walker() {
30
  *
31
  * @param string $string String to trim.
32
  */
33
- function relevanssi_array_walk_trim( &$string ) {
34
  $string = relevanssi_mb_trim( $string );
35
  }
36
 
@@ -42,7 +42,7 @@ function relevanssi_array_walk_trim( &$string ) {
42
  * @return string If the option is 'on', returns 'checked', otherwise returns an
43
  * empty string.
44
  */
45
- function relevanssi_check( $option ) {
46
  $checked = '';
47
  if ( 'on' === $option ) {
48
  $checked = 'checked';
@@ -60,7 +60,7 @@ function relevanssi_check( $option ) {
60
  *
61
  * @return string The HTML code, with tags closed.
62
  */
63
- function relevanssi_close_tags( $html ) {
64
  $result = array();
65
  preg_match_all(
66
  '#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU',
@@ -95,7 +95,7 @@ function relevanssi_close_tags( $html ) {
95
  *
96
  * @param string $notice The notice to print out.
97
  */
98
- function relevanssi_debug_echo( $notice ) {
99
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
100
  WP_CLI::log( $notice );
101
  } else {
@@ -125,7 +125,7 @@ function relevanssi_flatten_array( array $array ) {
125
  *
126
  * @return array $closing_tags Array of closing tags.
127
  */
128
- function relevanssi_generate_closing_tags( $tags ) {
129
  $closing_tags = array();
130
  foreach ( $tags as $tag ) {
131
  $a = str_replace( '<', '</', $tag );
@@ -140,8 +140,8 @@ function relevanssi_generate_closing_tags( $tags ) {
140
  /**
141
  * Returns a post object based on ID, **type**id notation or an object.
142
  *
143
- * @param int|string|WP_Post The source identified to parse, either a post ID
144
- * integer, a **type**id string or a post object.
145
  *
146
  * @return array An array containing the actual object in 'object' and the
147
  * format of the original value in 'format'. The value can be 'object', 'id'
@@ -184,12 +184,12 @@ function relevanssi_get_an_object( $source ) {
184
  *
185
  * @return string The locale or the language code.
186
  */
187
- function relevanssi_get_current_language( $locale = true ) {
188
  $current_language = get_locale();
189
  if ( ! $locale ) {
190
  $current_language = substr( $current_language, 0, 2 );
191
  }
192
- if ( function_exists( 'pll_current_language' ) ) {
193
  global $post;
194
 
195
  if ( isset( $post ) ) {
@@ -267,12 +267,18 @@ function relevanssi_get_permalink() {
267
  * Tries to fetch the post from the Relevanssi post cache. If that doesn't work,
268
  * gets the post using get_post().
269
  *
270
- * @param int $post_id The post ID.
271
- * @param int $blog_id The blog ID, default -1.
 
 
 
272
  *
273
  * @return object The post object.
274
  */
275
- function relevanssi_get_post( $post_id, $blog_id = -1 ) {
 
 
 
276
  if ( function_exists( 'relevanssi_premium_get_post' ) ) {
277
  return relevanssi_premium_get_post( $post_id, $blog_id );
278
  }
@@ -330,7 +336,7 @@ function relevanssi_get_post_object( $post_id ) {
330
  *
331
  * @return int Term taxonomy ID.
332
  */
333
- function relevanssi_get_term_tax_id( $term_id, $taxonomy ) {
334
  global $wpdb;
335
  return $wpdb->get_var(
336
  $wpdb->prepare(
@@ -347,11 +353,14 @@ function relevanssi_get_term_tax_id( $term_id, $taxonomy ) {
347
  * Fetches the taxonomy from wp_term_taxonomy based on term_id.
348
  *
349
  * @global object $wpdb The WordPress database interface.
 
350
  * @param int $term_id The term ID.
 
351
  * @deprecated Will be removed in future versions.
 
352
  * @return string $taxonomy The term taxonomy.
353
  */
354
- function relevanssi_get_term_taxonomy( $term_id ) {
355
  global $wpdb;
356
 
357
  $taxonomy = $wpdb->get_var( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
@@ -364,15 +373,40 @@ function relevanssi_get_term_taxonomy( $term_id ) {
364
  * Replacement for get_the_tags() that does the same, but applies Relevanssi
365
  * search term highlighting on the results.
366
  *
367
- * @param string $before What is printed before the tags, default null.
368
  * @param string $separator The separator between items, default ', '.
369
  * @param string $after What is printed after the tags, default ''.
370
  * @param int $post_id The post ID. Default current post ID (in the Loop).
371
  */
372
- function relevanssi_get_the_tags( $before = null, $separator = ', ', $after = '', $post_id = null ) {
373
  return relevanssi_the_tags( $before, $separator, $after, false, $post_id );
374
  }
375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  /**
377
  * Returns an imploded string if the option exists and is an array, an empty
378
  * string otherwise.
@@ -383,7 +417,7 @@ function relevanssi_get_the_tags( $before = null, $separator = ', ', $after = ''
383
  *
384
  * @return string Imploded string or an empty string.
385
  */
386
- function relevanssi_implode( $request, $option, $glue = ',' ) {
387
  if ( isset( $request[ $option ] ) && is_array( $request[ $option ] ) ) {
388
  return implode( $glue, $request[ $option ] );
389
  }
@@ -398,7 +432,7 @@ function relevanssi_implode( $request, $option, $glue = ',' ) {
398
  *
399
  * @return int|null Integer value of the option, or null.
400
  */
401
- function relevanssi_intval( $request, $option ) {
402
  if ( isset( $request[ $option ] ) ) {
403
  return intval( $request[ $option ] );
404
  }
@@ -421,7 +455,7 @@ function relevanssi_intval( $request, $option ) {
421
  *
422
  * @return WP_Error|array The wp_remote_post() response or WP_Error on failure.
423
  */
424
- function relevanssi_launch_ajax_action( $action, $payload_args = array() ) {
425
  $cookies = array();
426
  foreach ( $_COOKIE as $name => $value ) {
427
  $cookies[] = "$name=" . rawurlencode(
@@ -456,7 +490,7 @@ function relevanssi_launch_ajax_action( $action, $payload_args = array() ) {
456
  * @return string|null A legal value or the default value, null if the option
457
  * isn't set.
458
  */
459
- function relevanssi_legal_value( $request, $option, $values, $default ) {
460
  $value = null;
461
  if ( isset( $request[ $option ] ) ) {
462
  $value = $default;
@@ -480,11 +514,11 @@ function relevanssi_legal_value( $request, $option, $values, $default ) {
480
  * @return int $val Returns < 0 if str1 is less than str2; > 0 if str1 is
481
  * greater than str2, and 0 if they are equal.
482
  */
483
- function relevanssi_mb_strcasecmp( $str1, $str2, $encoding = null ) {
484
  if ( ! function_exists( 'mb_internal_encoding' ) ) {
485
  return strnatcasecmp( $str1, $str2 );
486
  } else {
487
- if ( null === $encoding ) {
488
  $encoding = mb_internal_encoding();
489
  }
490
  return strnatcmp( mb_strtoupper( $str1, $encoding ), mb_strtoupper( $str2, $encoding ) );
@@ -501,7 +535,7 @@ function relevanssi_mb_strcasecmp( $str1, $str2, $encoding = null ) {
501
  *
502
  * @return string Trimmed string.
503
  */
504
- function relevanssi_mb_trim( $string ) {
505
  $string = str_replace( chr( 194 ) . chr( 160 ), '', $string );
506
  $string = str_replace( "\0", '', $string );
507
  $string = preg_replace( '/(^\s+)|(\s+$)/us', '', $string );
@@ -516,7 +550,7 @@ function relevanssi_mb_trim( $string ) {
516
  *
517
  * @return string 'on' or 'off'.
518
  */
519
- function relevanssi_off_or_on( $request, $option ) {
520
  if ( isset( $request[ $option ] ) && 'off' !== $request[ $option ] ) {
521
  return 'on';
522
  }
@@ -530,7 +564,7 @@ function relevanssi_off_or_on( $request, $option ) {
530
  *
531
  * @return string The cleaned string.
532
  */
533
- function relevanssi_remove_quotes( $string ) {
534
  return str_replace( array( '”', '“', '"' ), '', $string );
535
  }
536
 
@@ -545,7 +579,7 @@ function relevanssi_remove_quotes( $string ) {
545
  *
546
  * @return array The same array with quotes removed from the keys.
547
  */
548
- function relevanssi_remove_quotes_from_array_keys( $array ) {
549
  $array = array_keys( $array );
550
  array_walk(
551
  $array,
@@ -606,7 +640,7 @@ function relevanssi_return_off() {
606
  *
607
  * @return int|object|WP_Post The post object in the desired format.
608
  */
609
- function relevanssi_return_value( $post, $return_value ) {
610
  if ( 'id' === $return_value ) {
611
  return $post->ID;
612
  } elseif ( 'id=>parent' === $return_value ) {
@@ -624,7 +658,7 @@ function relevanssi_return_value( $post, $return_value ) {
624
  *
625
  * @return string Sanitized hex string, or an empty string.
626
  */
627
- function relevanssi_sanitize_hex_color( $color ) {
628
  if ( '' === $color ) {
629
  return '';
630
  }
@@ -650,7 +684,7 @@ function relevanssi_sanitize_hex_color( $color ) {
650
  * @return string If the option matches the value, returns 'selected', otherwise
651
  * returns an empty string.
652
  */
653
- function relevanssi_select( $option, $value ) {
654
  $selected = '';
655
  if ( $option === $value ) {
656
  $selected = 'selected';
@@ -668,7 +702,7 @@ function relevanssi_select( $option, $value ) {
668
  *
669
  * @return string The processed text.
670
  */
671
- function relevanssi_strip_invisibles( $text ) {
672
  $text = preg_replace(
673
  array(
674
  '@<style[^>]*?>.*?</style>@siu',
@@ -700,7 +734,7 @@ function relevanssi_strip_invisibles( $text ) {
700
  *
701
  * @return string The content without tags.
702
  */
703
- function relevanssi_strip_tags( $content ) {
704
  $content = relevanssi_strip_invisibles( $content );
705
  $content = preg_replace( '/(<\/[^>]+?>)(<[^>\/][^>]*?>)/', '$1 $2', $content );
706
  return strip_tags(
@@ -723,7 +757,7 @@ function relevanssi_strip_tags( $content ) {
723
  * @return mixed False, if no result or $offset outside the length of $haystack,
724
  * otherwise the position (which can be non-false 0!).
725
  */
726
- function relevanssi_stripos( $haystack, $needle, $offset = 0 ) {
727
  if ( $offset > relevanssi_strlen( $haystack ) ) {
728
  return false;
729
  }
@@ -790,7 +824,7 @@ function relevanssi_stripos( $haystack, $needle, $offset = 0 ) {
790
  *
791
  * @return int The length of the string.
792
  */
793
- function relevanssi_strlen( $s ) {
794
  if ( function_exists( 'mb_strlen' ) ) {
795
  return mb_strlen( $s );
796
  }
@@ -807,7 +841,7 @@ function relevanssi_strlen( $s ) {
807
  *
808
  * @return string $string The string in lowercase.
809
  */
810
- function relevanssi_strtolower( $string ) {
811
  if ( ! function_exists( 'mb_strtolower' ) ) {
812
  return strtolower( $string );
813
  } else {
@@ -821,17 +855,17 @@ function relevanssi_strtolower( $string ) {
821
  * If multibyte string functions are available, returns mb_substr() and falls
822
  * back to substr() if multibyte functions are not available.
823
  *
824
- * @param string $string The source string.
825
- * @param int $start If start is non-negative, the returned string will
826
  * start at the start'th position in str, counting from zero. If start is
827
  * negative, the returned string will start at the start'th character from the
828
  * end of string.
829
- * @param int $length Maximum number of characters to use from string. If
830
  * omitted or null is passed, extract all characters to the end of the string.
831
  *
832
  * @return string $string The string in lowercase.
833
  */
834
- function relevanssi_substr( $string, $start, $length = null ) {
835
  if ( ! function_exists( 'mb_substr' ) ) {
836
  return substr( $string, $start, $length );
837
  } else {
@@ -872,13 +906,13 @@ function relevanssi_the_permalink() {
872
  * Replacement for the_tags() that does the same, but applies Relevanssi search term
873
  * highlighting on the results.
874
  *
875
- * @param string $before What is printed before the tags, default null.
876
  * @param string $separator The separator between items, default ', '.
877
  * @param string $after What is printed after the tags, default ''.
878
  * @param boolean $echo If true, echo, otherwise return the result. Default true.
879
  * @param int $post_id The post ID. Default current post ID (in the Loop).
880
  */
881
- function relevanssi_the_tags( $before = null, $separator = ', ', $after = '', $echo = true, $post_id = null ) {
882
  $tag_list = get_the_tag_list( $before, $separator, $after, $post_id );
883
  $found = preg_match_all( '~<a href=".*?" rel="tag">(.*?)</a>~', $tag_list, $matches );
884
  if ( $found ) {
@@ -902,6 +936,50 @@ function relevanssi_the_tags( $before = null, $separator = ', ', $after = '', $e
902
  }
903
  }
904
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
905
  /**
906
  * Turns off options, ie. sets them to "off".
907
  *
@@ -911,7 +989,7 @@ function relevanssi_the_tags( $before = null, $separator = ', ', $after = '', $e
911
  * @param array $request The _REQUEST array, passed as reference.
912
  * @param array $options An array of option names.
913
  */
914
- function relevanssi_turn_off_options( &$request, $options ) {
915
  array_walk(
916
  $options,
917
  function( $option ) use ( &$request ) {
@@ -932,7 +1010,7 @@ function relevanssi_turn_off_options( &$request, $options ) {
932
  * @param boolean $positive If true, replace negative values and zeroes with
933
  * $default.
934
  */
935
- function relevanssi_update_floatval( $request, $option, $autoload = true, $default = 0, $positive = false ) {
936
  if ( isset( $request[ $option ] ) ) {
937
  $value = floatval( $request[ $option ] );
938
  if ( ! $value ) {
@@ -953,7 +1031,7 @@ function relevanssi_update_floatval( $request, $option, $autoload = true, $defau
953
  * @param boolean $autoload Should the option autoload, default true.
954
  * @param int $default The default value if intval() fails, default 0.
955
  */
956
- function relevanssi_update_intval( $request, $option, $autoload = true, $default = 0 ) {
957
  if ( isset( $request[ $option ] ) ) {
958
  $value = intval( $request[ $option ] );
959
  if ( ! $value ) {
@@ -972,7 +1050,7 @@ function relevanssi_update_intval( $request, $option, $autoload = true, $default
972
  * @param string $default The default value.
973
  * @param boolean $autoload Should the option autoload, default true.
974
  */
975
- function relevanssi_update_legal_value( $request, $option, $values, $default, $autoload = true ) {
976
  if ( isset( $request[ $option ] ) ) {
977
  $value = $default;
978
  if ( in_array( $request[ $option ], $values, true ) ) {
@@ -989,7 +1067,7 @@ function relevanssi_update_legal_value( $request, $option, $values, $default, $a
989
  * @param string $option The key to check.
990
  * @param boolean $autoload Should the option autoload, default true.
991
  */
992
- function relevanssi_update_off_or_on( $request, $option, $autoload = true ) {
993
  relevanssi_update_legal_value(
994
  $request,
995
  $option,
@@ -1006,7 +1084,7 @@ function relevanssi_update_off_or_on( $request, $option, $autoload = true ) {
1006
  * @param string $option The key to check.
1007
  * @param boolean $autoload Should the option autoload, default true.
1008
  */
1009
- function relevanssi_update_sanitized( $request, $option, $autoload = true ) {
1010
  if ( isset( $request[ $option ] ) ) {
1011
  $value = sanitize_text_field( wp_unslash( $request[ $option ] ) );
1012
  update_option( $option, $value, $autoload );
30
  *
31
  * @param string $string String to trim.
32
  */
33
+ function relevanssi_array_walk_trim( string &$string ) {
34
  $string = relevanssi_mb_trim( $string );
35
  }
36
 
42
  * @return string If the option is 'on', returns 'checked', otherwise returns an
43
  * empty string.
44
  */
45
+ function relevanssi_check( string $option ) {
46
  $checked = '';
47
  if ( 'on' === $option ) {
48
  $checked = 'checked';
60
  *
61
  * @return string The HTML code, with tags closed.
62
  */
63
+ function relevanssi_close_tags( string $html ) {
64
  $result = array();
65
  preg_match_all(
66
  '#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU',
95
  *
96
  * @param string $notice The notice to print out.
97
  */
98
+ function relevanssi_debug_echo( string $notice ) {
99
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
100
  WP_CLI::log( $notice );
101
  } else {
125
  *
126
  * @return array $closing_tags Array of closing tags.
127
  */
128
+ function relevanssi_generate_closing_tags( array $tags ) {
129
  $closing_tags = array();
130
  foreach ( $tags as $tag ) {
131
  $a = str_replace( '<', '</', $tag );
140
  /**
141
  * Returns a post object based on ID, **type**id notation or an object.
142
  *
143
+ * @param int|string|WP_Post $source The source identified to parse, either a
144
+ * post ID integer, a **type**id string or a post object.
145
  *
146
  * @return array An array containing the actual object in 'object' and the
147
  * format of the original value in 'format'. The value can be 'object', 'id'
184
  *
185
  * @return string The locale or the language code.
186
  */
187
+ function relevanssi_get_current_language( bool $locale = true ) {
188
  $current_language = get_locale();
189
  if ( ! $locale ) {
190
  $current_language = substr( $current_language, 0, 2 );
191
  }
192
+ if ( class_exists( 'Polylang', false ) ) {
193
  global $post;
194
 
195
  if ( isset( $post ) ) {
267
  * Tries to fetch the post from the Relevanssi post cache. If that doesn't work,
268
  * gets the post using get_post().
269
  *
270
+ * @param int|string $post_id The post ID. Usually an integer post ID, but can
271
+ * also be a string (u_<user ID>, p_<post type name> or
272
+ * **<taxonomy>**<term ID>).
273
+ * @param int $blog_id The blog ID, default -1. If -1, will be replaced
274
+ * with the actual current blog ID from get_current_blog_id().
275
  *
276
  * @return object The post object.
277
  */
278
+ function relevanssi_get_post( $post_id, int $blog_id = -1 ) {
279
+ if ( -1 === $blog_id ) {
280
+ $blog_id = get_current_blog_id();
281
+ }
282
  if ( function_exists( 'relevanssi_premium_get_post' ) ) {
283
  return relevanssi_premium_get_post( $post_id, $blog_id );
284
  }
336
  *
337
  * @return int Term taxonomy ID.
338
  */
339
+ function relevanssi_get_term_tax_id( int $term_id, string $taxonomy ) {
340
  global $wpdb;
341
  return $wpdb->get_var(
342
  $wpdb->prepare(
353
  * Fetches the taxonomy from wp_term_taxonomy based on term_id.
354
  *
355
  * @global object $wpdb The WordPress database interface.
356
+ *
357
  * @param int $term_id The term ID.
358
+ *
359
  * @deprecated Will be removed in future versions.
360
+ *
361
  * @return string $taxonomy The term taxonomy.
362
  */
363
+ function relevanssi_get_term_taxonomy( int $term_id ) {
364
  global $wpdb;
365
 
366
  $taxonomy = $wpdb->get_var( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
373
  * Replacement for get_the_tags() that does the same, but applies Relevanssi
374
  * search term highlighting on the results.
375
  *
376
+ * @param string $before What is printed before the tags, default ''.
377
  * @param string $separator The separator between items, default ', '.
378
  * @param string $after What is printed after the tags, default ''.
379
  * @param int $post_id The post ID. Default current post ID (in the Loop).
380
  */
381
+ function relevanssi_get_the_tags( string $before = '', string $separator = ', ', string $after = '', int $post_id = 0 ) {
382
  return relevanssi_the_tags( $before, $separator, $after, false, $post_id );
383
  }
384
 
385
+ /**
386
+ * Returns the post title with highlighting.
387
+ *
388
+ * Reads the highlighted title from $post->post_highlighted_title. Uses the
389
+ * relevanssi_get_post() to fecth the post.
390
+ *
391
+ * @uses relevanssi_get_post()
392
+ *
393
+ * @param int|WP_Post $post The post ID or a post object.
394
+ *
395
+ * @return string The post title with highlights.
396
+ */
397
+ function relevanssi_get_the_title( $post ) {
398
+ if ( is_numeric( $post ) ) {
399
+ $post = relevanssi_get_post( $post );
400
+ }
401
+ if ( ! is_object( $post ) ) {
402
+ return null;
403
+ }
404
+ if ( empty( $post->post_highlighted_title ) ) {
405
+ $post->post_highlighted_title = $post->post_title;
406
+ }
407
+ return $post->post_highlighted_title;
408
+ }
409
+
410
  /**
411
  * Returns an imploded string if the option exists and is an array, an empty
412
  * string otherwise.
417
  *
418
  * @return string Imploded string or an empty string.
419
  */
420
+ function relevanssi_implode( array $request, string $option, string $glue = ',' ) {
421
  if ( isset( $request[ $option ] ) && is_array( $request[ $option ] ) ) {
422
  return implode( $glue, $request[ $option ] );
423
  }
432
  *
433
  * @return int|null Integer value of the option, or null.
434
  */
435
+ function relevanssi_intval( array $request, string $option ) {
436
  if ( isset( $request[ $option ] ) ) {
437
  return intval( $request[ $option ] );
438
  }
455
  *
456
  * @return WP_Error|array The wp_remote_post() response or WP_Error on failure.
457
  */
458
+ function relevanssi_launch_ajax_action( string $action, array $payload_args = array() ) {
459
  $cookies = array();
460
  foreach ( $_COOKIE as $name => $value ) {
461
  $cookies[] = "$name=" . rawurlencode(
490
  * @return string|null A legal value or the default value, null if the option
491
  * isn't set.
492
  */
493
+ function relevanssi_legal_value( array $request, string $option, array $values, string $default ) {
494
  $value = null;
495
  if ( isset( $request[ $option ] ) ) {
496
  $value = $default;
514
  * @return int $val Returns < 0 if str1 is less than str2; > 0 if str1 is
515
  * greater than str2, and 0 if they are equal.
516
  */
517
+ function relevanssi_mb_strcasecmp( string $str1, string $str2, string $encoding = '' ) {
518
  if ( ! function_exists( 'mb_internal_encoding' ) ) {
519
  return strnatcasecmp( $str1, $str2 );
520
  } else {
521
+ if ( empty( $encoding ) ) {
522
  $encoding = mb_internal_encoding();
523
  }
524
  return strnatcmp( mb_strtoupper( $str1, $encoding ), mb_strtoupper( $str2, $encoding ) );
535
  *
536
  * @return string Trimmed string.
537
  */
538
+ function relevanssi_mb_trim( string $string ) {
539
  $string = str_replace( chr( 194 ) . chr( 160 ), '', $string );
540
  $string = str_replace( "\0", '', $string );
541
  $string = preg_replace( '/(^\s+)|(\s+$)/us', '', $string );
550
  *
551
  * @return string 'on' or 'off'.
552
  */
553
+ function relevanssi_off_or_on( array $request, string $option ) {
554
  if ( isset( $request[ $option ] ) && 'off' !== $request[ $option ] ) {
555
  return 'on';
556
  }
564
  *
565
  * @return string The cleaned string.
566
  */
567
+ function relevanssi_remove_quotes( string $string ) {
568
  return str_replace( array( '”', '“', '"' ), '', $string );
569
  }
570
 
579
  *
580
  * @return array The same array with quotes removed from the keys.
581
  */
582
+ function relevanssi_remove_quotes_from_array_keys( array $array ) {
583
  $array = array_keys( $array );
584
  array_walk(
585
  $array,
640
  *
641
  * @return int|object|WP_Post The post object in the desired format.
642
  */
643
+ function relevanssi_return_value( WP_Post $post, string $return_value ) {
644
  if ( 'id' === $return_value ) {
645
  return $post->ID;
646
  } elseif ( 'id=>parent' === $return_value ) {
658
  *
659
  * @return string Sanitized hex string, or an empty string.
660
  */
661
+ function relevanssi_sanitize_hex_color( string $color ) {
662
  if ( '' === $color ) {
663
  return '';
664
  }
684
  * @return string If the option matches the value, returns 'selected', otherwise
685
  * returns an empty string.
686
  */
687
+ function relevanssi_select( string $option, string $value ) {
688
  $selected = '';
689
  if ( $option === $value ) {
690
  $selected = 'selected';
702
  *
703
  * @return string The processed text.
704
  */
705
+ function relevanssi_strip_invisibles( string $text ) {
706
  $text = preg_replace(
707
  array(
708
  '@<style[^>]*?>.*?</style>@siu',
734
  *
735
  * @return string The content without tags.
736
  */
737
+ function relevanssi_strip_tags( string $content ) {
738
  $content = relevanssi_strip_invisibles( $content );
739
  $content = preg_replace( '/(<\/[^>]+?>)(<[^>\/][^>]*?>)/', '$1 $2', $content );
740
  return strip_tags(
757
  * @return mixed False, if no result or $offset outside the length of $haystack,
758
  * otherwise the position (which can be non-false 0!).
759
  */
760
+ function relevanssi_stripos( string $haystack, string $needle, int $offset = 0 ) {
761
  if ( $offset > relevanssi_strlen( $haystack ) ) {
762
  return false;
763
  }
824
  *
825
  * @return int The length of the string.
826
  */
827
+ function relevanssi_strlen( string $s ) {
828
  if ( function_exists( 'mb_strlen' ) ) {
829
  return mb_strlen( $s );
830
  }
841
  *
842
  * @return string $string The string in lowercase.
843
  */
844
+ function relevanssi_strtolower( string $string ) {
845
  if ( ! function_exists( 'mb_strtolower' ) ) {
846
  return strtolower( $string );
847
  } else {
855
  * If multibyte string functions are available, returns mb_substr() and falls
856
  * back to substr() if multibyte functions are not available.
857
  *
858
+ * @param string $string The source string.
859
+ * @param int $start If start is non-negative, the returned string will
860
  * start at the start'th position in str, counting from zero. If start is
861
  * negative, the returned string will start at the start'th character from the
862
  * end of string.
863
+ * @param int|null $length Maximum number of characters to use from string. If
864
  * omitted or null is passed, extract all characters to the end of the string.
865
  *
866
  * @return string $string The string in lowercase.
867
  */
868
+ function relevanssi_substr( string $string, int $start, $length = null ) {
869
  if ( ! function_exists( 'mb_substr' ) ) {
870
  return substr( $string, $start, $length );
871
  } else {
906
  * Replacement for the_tags() that does the same, but applies Relevanssi search term
907
  * highlighting on the results.
908
  *
909
+ * @param string $before What is printed before the tags, default ''.
910
  * @param string $separator The separator between items, default ', '.
911
  * @param string $after What is printed after the tags, default ''.
912
  * @param boolean $echo If true, echo, otherwise return the result. Default true.
913
  * @param int $post_id The post ID. Default current post ID (in the Loop).
914
  */
915
+ function relevanssi_the_tags( string $before = '', string $separator = ', ', string $after = '', bool $echo = true, int $post_id = 0 ) {
916
  $tag_list = get_the_tag_list( $before, $separator, $after, $post_id );
917
  $found = preg_match_all( '~<a href=".*?" rel="tag">(.*?)</a>~', $tag_list, $matches );
918
  if ( $found ) {
936
  }
937
  }
938
 
939
+ /**
940
+ * Prints out post title with highlighting.
941
+ *
942
+ * Uses the global $post object. Reads the highlighted title from
943
+ * $post->post_highlighted_title. This used to accept one parameter, the
944
+ * `$echo` boolean, but in 2.12.3 / 4.10.3 the function signature was matched
945
+ * to copy `the_title()` function in WordPress core. The original behaviour is
946
+ * still supported: `relevanssi_the_title()` without arguments works exactly as
947
+ * before and `relevanssi_the_title( false )` returns the title.
948
+ *
949
+ * @global object $post The global post object.
950
+ *
951
+ * @param boolean|string $before Markup to prepend to the title. Can also be a
952
+ * boolean for whether to echo or return the title.
953
+ * @param string $after Markup to append to the title.
954
+ * @param boolean $echo Whether to echo or return the title. Default
955
+ * true for echo.
956
+ *
957
+ * @return void|string Void if $echo argument is true, current post title with
958
+ * highlights if $echo is false.
959
+ */
960
+ function relevanssi_the_title( $before = true, string $after = '', bool $echo = true ) {
961
+ if ( true === $before ) {
962
+ $before = '';
963
+ $echo = true;
964
+ } elseif ( false === $before ) {
965
+ $before = '';
966
+ $echo = false;
967
+ }
968
+ global $post;
969
+ if ( empty( $post->post_highlighted_title ) ) {
970
+ $post->post_highlighted_title = $post->post_title;
971
+ }
972
+ if ( relevanssi_strlen( $post->post_highlighted_title ) === 0 ) {
973
+ return;
974
+ }
975
+ $title = $before . $post->post_highlighted_title . $after;
976
+ if ( $echo ) {
977
+ echo $title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
978
+ } else {
979
+ return $title;
980
+ }
981
+ }
982
+
983
  /**
984
  * Turns off options, ie. sets them to "off".
985
  *
989
  * @param array $request The _REQUEST array, passed as reference.
990
  * @param array $options An array of option names.
991
  */
992
+ function relevanssi_turn_off_options( array &$request, array $options ) {
993
  array_walk(
994
  $options,
995
  function( $option ) use ( &$request ) {
1010
  * @param boolean $positive If true, replace negative values and zeroes with
1011
  * $default.
1012
  */
1013
+ function relevanssi_update_floatval( array $request, string $option, bool $autoload = true, int $default = 0, bool $positive = false ) {
1014
  if ( isset( $request[ $option ] ) ) {
1015
  $value = floatval( $request[ $option ] );
1016
  if ( ! $value ) {
1031
  * @param boolean $autoload Should the option autoload, default true.
1032
  * @param int $default The default value if intval() fails, default 0.
1033
  */
1034
+ function relevanssi_update_intval( array $request, string $option, bool $autoload = true, int $default = 0 ) {
1035
  if ( isset( $request[ $option ] ) ) {
1036
  $value = intval( $request[ $option ] );
1037
  if ( ! $value ) {
1050
  * @param string $default The default value.
1051
  * @param boolean $autoload Should the option autoload, default true.
1052
  */
1053
+ function relevanssi_update_legal_value( array $request, string $option, array $values, string $default, bool $autoload = true ) {
1054
  if ( isset( $request[ $option ] ) ) {
1055
  $value = $default;
1056
  if ( in_array( $request[ $option ], $values, true ) ) {
1067
  * @param string $option The key to check.
1068
  * @param boolean $autoload Should the option autoload, default true.
1069
  */
1070
+ function relevanssi_update_off_or_on( array $request, string $option, bool $autoload = true ) {
1071
  relevanssi_update_legal_value(
1072
  $request,
1073
  $option,
1084
  * @param string $option The key to check.
1085
  * @param boolean $autoload Should the option autoload, default true.
1086
  */
1087
+ function relevanssi_update_sanitized( array $request, string $option, bool $autoload = true ) {
1088
  if ( isset( $request[ $option ] ) ) {
1089
  $value = sanitize_text_field( wp_unslash( $request[ $option ] ) );
1090
  update_option( $option, $value, $autoload );
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.6.1
7
  Requires PHP: 7.0
8
- Stable tag: 4.10.2
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -133,6 +133,21 @@ Each document database is full of useless words. All the little words that appea
133
  * John Calahan for extensive 4.0 beta testing.
134
 
135
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  = 4.10.2 =
137
  * New feature: You can force Relevanssi to be active by setting the query variable `relevanssi` to `true`. Thanks to Jan Willem Oostendorp.
138
  * Changed behaviour: Relevanssi has been moved from `the_posts` filter to `posts_pre_query`. This change doesn't do much, but increases performance slightly as WordPress needs to do less useless work, as now the default query is no longer run. Thanks to Jan Willem Oostendorp.
@@ -178,39 +193,10 @@ Each document database is full of useless words. All the little words that appea
178
  * Minor fix: The "Respect 'exclude_from_search'" setting did not work if no post type parameter was included in the search parameters.
179
  * Minor fix: The category inclusion and exclusion setting checkboxes on the Searching tab didn't work. The setting was saved, but the checkboxes wouldn't appear.
180
 
181
- = 4.8.3 =
182
- * New feature: Both `relevanssi_fuzzy_query` and `relevanssi_term_where` now get the current search term as a parameter.
183
- * Minor fix: Relevanssi database tables don't have PRIMARY keys, only UNIQUE keys. In case this is a problem (for example on Digital Ocean servers), deactivate and activate Relevanssi to fix the problem.
184
- * Minor fix: When `posts_per_page` was set to -1, the `max_num_pages` was incorrectly set to the number of posts found. It should, of course, be 1.
185
- * Minor fix: Excluding from logs didn't work if user IDs had spaces between them ('user_a, user_b'). This is now fixed for good, the earlier fix didn't work.
186
-
187
- = 4.8.2 =
188
- * New feature: New filter hook `relevanssi_term_where` lets you filter the term WHERE conditional for the search query.
189
- * Minor fix: Doing the document count updates asynchronously caused problems in some cases (eg. importing posts). Now the document count is only updated after a full indexing and once per week.
190
- * Minor fix: Phrase matching has been improved to make it possible to search for phrases that include characters like the ampersand.
191
-
192
- = 4.8.1 =
193
- * Major fix: Changes in WooCommerce 4.4.0 broke the Relevanssi searches. This makes the WooCommerce search work again.
194
- * Minor fix: Excluding from logs didn't work if user IDs had spaces between them ('user_a, user_b'). Now the extra spaces don't matter.
195
- * Minor fix: The asynchronous doc count action in the previous version could cause an infinite loop with the Snitch logger plugin. This is prevented now: the async action doesn't run after indexing unless a post is actually indexed.
196
- * Minor fix: Relevanssi indexing procedure was triggered for autosaved drafts, causing possible problems with the asynchronous doc count action.
197
- * Minor fix: The `relevanssi_index_custom_fields` filter hook was not applied when doing phrase matching, thus phrases could not be found when they were in custom fields added with the filter.
198
-
199
- = 4.8.0 =
200
- * Changed behaviour: Relevanssi now requires PHP 7.
201
- * Changed behaviour: Relevanssi now sorts strings with `strnatcasecmp()` instead of `strcasecmp()`, leading to a more natural results with strings that include numbers.
202
- * Changed behaviour: Relevanssi init is now moved from priority 10 to priority 1 on the `init` hook to avoid problems with missing TablePress compatibility.
203
- * New feature: New filter hook `relevanssi_get_approved_comments_args` filters the arguments to `get_approved_comments` in comment indexing. This can be used to index custom comment types, for example.
204
- * New feature: Content wrapped in the `noindex` tags is no longer used for excerpts.
205
- * New feature: The `[et_pb_fullwidth_code]` shortcode is now removed completely, including the contents, when Relevanssi is indexing and building excerpts.
206
- * Major fix: Relevanssi didn't index new comments when they were added; when a post was indexed or the whole index rebuilt, comment content was included. We don't know how long this bug has existed, but it is now fixed. Rebuild the index to get all comment content included in the index.
207
- * Minor fix: Autoload has been disabled for several options that are not needed often.
208
- * Minor fix: Phrase matching did not work correctly in visible custom fields.
209
- * Minor fix: TablePress support could cause halting errors if posts were inserted before Relevanssi has loaded itself (on `init` priority 10). These errors will no longer happen.
210
- * Minor fix: The doc count update, which is a heavy task, is now moved to an asynchronous action to avoid slowing down the site for users.
211
- * Minor fix: Relevanssi only updates doc count on `relevanssi_insert_edit()` when the post is indexed.
212
-
213
  == Upgrade notice ==
 
 
 
214
  = 4.10.2 =
215
  * Switch from `the_posts` to `posts_pre_query`, bug fixes.
216
 
@@ -224,16 +210,4 @@ Each document database is full of useless words. All the little words that appea
224
  * Bug fixing, better Oxygen Builder compatibility.
225
 
226
  = 4.9.0 =
227
- * New debugging feature, lots of minor fixes.
228
-
229
- = 4.8.3 =
230
- * Small bug fixes and database improvements.
231
-
232
- = 4.8.2 =
233
- * Performance and phrase search improvements.
234
-
235
- = 4.8.1 =
236
- * WooCommerce 4.4 compatibility, other minor fixes.
237
-
238
- = 4.8.0 =
239
- * Fixes a major bug in comment indexing, if you include comments in the index rebuild the index after updating.
5
  Requires at least: 4.9
6
  Tested up to: 5.6.1
7
  Requires PHP: 7.0
8
+ Stable tag: 4.11.0
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
133
  * John Calahan for extensive 4.0 beta testing.
134
 
135
  == Changelog ==
136
+ = 4.11.0 =
137
+ * New feature: New filter hook `relevanssi_rendered_block` filters Gutenberg block content after the block has been rendered with `render_block()`.
138
+ * New feature: New filter hook `relevanssi_log_query` can be used to filter the search query before it's logged. This can be used to log instead the query that includes synonyms (available as a parameter to the filter hook).
139
+ * New feature: New filter hook `relevanssi_add_all_results` can be used to make Relevanssi add a list of all result IDs found to `$query->relevanssi_all_results`. Just make this hook return `true`.
140
+ * New feature: New filter hook `relevanssi_acceptable_hooks` can be used to adjust where in WP admin the Relevanssi admin javascripts are enqueued.
141
+ * New feature: Support for All-in-One SEO. Posts marked as 'Robots No Index' are not indexed by Relevanssi.
142
+ * New feature: New setting in advanced indexing settings to control whether Relevanssi respects the SEO plugin 'noindex' setting or not.
143
+ * Changed behaviour: Type hinting has been added to Relevanssi functions, which may cause errors if the filter functions are sloppy with data types.
144
+ * Changed behaviour: `relevanssi_the_title()` now supports the same parameters as `the_title()`, so you can just replace `the_title()` with it and keep everything else the same. The old behaviour is still supported.
145
+ * Changed behaviour: Relevanssi no longer logs queries with the added synonyms. You can use the `relevanssi_log_query` filter hook to return to the previous behaviour of logging the synonyms too. Thanks to Jan Willem Oostendorp.
146
+ * Changed behaviour: When using ACF and custom fields indexing set to 'all', Relevanssi will no longer index the meta fields (where the content begins with `field_`).
147
+ * Minor fix: The Oxygen compatibility made it impossible to index other custom fields than the Oxygen `ct_builder_shortcodes`. This has been improved now.
148
+ * Minor fix: Old legacy scripts that caused Javascript warnings on admin pages have been removed.
149
+ * Minor fix: In some cases, having less than or greater than symbols in PDF content would block that PDF content from being indexed.
150
+
151
  = 4.10.2 =
152
  * New feature: You can force Relevanssi to be active by setting the query variable `relevanssi` to `true`. Thanks to Jan Willem Oostendorp.
153
  * Changed behaviour: Relevanssi has been moved from `the_posts` filter to `posts_pre_query`. This change doesn't do much, but increases performance slightly as WordPress needs to do less useless work, as now the default query is no longer run. Thanks to Jan Willem Oostendorp.
193
  * Minor fix: The "Respect 'exclude_from_search'" setting did not work if no post type parameter was included in the search parameters.
194
  * Minor fix: The category inclusion and exclusion setting checkboxes on the Searching tab didn't work. The setting was saved, but the checkboxes wouldn't appear.
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  == Upgrade notice ==
197
+ = 4.11.0 =
198
+ * New filter hooks, bug fixes.
199
+
200
  = 4.10.2 =
201
  * Switch from `the_posts` to `posts_pre_query`, bug fixes.
202
 
210
  * Bug fixing, better Oxygen Builder compatibility.
211
 
212
  = 4.9.0 =
213
+ * New debugging feature, lots of minor fixes.
 
 
 
 
 
 
 
 
 
 
 
 
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.10.2
17
  * Author: Mikko Saari
18
  * Author URI: http://www.mikkosaari.fi/
19
  * Text Domain: relevanssi
@@ -22,7 +22,7 @@
22
  */
23
 
24
  /**
25
- * Copyright 2020 Mikko Saari (email: mikko@mikkosaari.fi)
26
  * This file is part of Relevanssi, a search plugin for WordPress.
27
  *
28
  * Relevanssi is free software: you can redistribute it and/or modify
@@ -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.10.2';
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.11.0
17
  * Author: Mikko Saari
18
  * Author URI: http://www.mikkosaari.fi/
19
  * Text Domain: relevanssi
22
  */
23
 
24
  /**
25
+ * Copyright 2021 Mikko Saari (email: mikko@mikkosaari.fi)
26
  * This file is part of Relevanssi, a search plugin for WordPress.
27
  *
28
  * Relevanssi is free software: you can redistribute it and/or modify
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.11.0';
71
 
72
  require_once 'lib/admin-ajax.php';
73
  require_once 'lib/common.php';