Relevanssi – A Better Search - Version 4.3.3

Version Description

  • New feature: New filter hook relevanssi_indexing_adjust can be used to stop Relevanssi from adjusting the number of posts indexed at once during the indexing.
  • New feature: New filter hook relevanssi_acf_field_value filters ACF field values before they are indexed.
  • New feature: New filter hook relevanssi_disabled_shortcodes filters the array containing shortcodes that are disabled when indexing.
  • Removed feature: The relevanssi_indexing_limit option wasn't really used anymore, so it has been removed.
  • Changed behaviour: Indexing exclusions from Yoast SEO and SEOPress are applied in a different way in the indexing, making for a smoother indexing process.
  • Changed behaviour: WP Table Reloaded support has been removed; you really shouldn't be using WP Table Reloaded anymore.
  • Minor fix: Relevanssi won't choke on ACF fields with array or object values anymore.
  • Minor fix: Relevanssi uninstall process left couple of Relevanssi options in the database.
  • Minor fix: WPML language filter didn't work when fields was set to ids or id=>parent.
Download this release

Release Info

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

Code changes from version 4.3.2 to 4.3.3

lib/admin-ajax.php CHANGED
@@ -372,6 +372,9 @@ function relevanssi_admin_search_debugging_info( $query ) {
372
  $result .= '<ul style="list-style: disc; margin-left: 1.5em">';
373
  foreach ( $wp_filter[ $filter ] as $priority => $functions ) {
374
  foreach ( $functions as $function ) {
 
 
 
375
  $result .= "<li>$priority: " . $function['function'] . '</li>';
376
  }
377
  }
372
  $result .= '<ul style="list-style: disc; margin-left: 1.5em">';
373
  foreach ( $wp_filter[ $filter ] as $priority => $functions ) {
374
  foreach ( $functions as $function ) {
375
+ if ( $function['function'] instanceof Closure ) {
376
+ $function['function'] = 'Anonymous function';
377
+ }
378
  $result .= "<li>$priority: " . $function['function'] . '</li>';
379
  }
380
  }
lib/admin_scripts.js CHANGED
@@ -178,6 +178,7 @@ jQuery(document).ready(function($) {
178
  offset: 0,
179
  total_seconds: 0,
180
  limit: relevanssi_params.indexing_limit,
 
181
  extend: true,
182
  security: nonce.indexing_nonce
183
  }
@@ -201,6 +202,7 @@ function process_indexing_step(args) {
201
  total: args.total,
202
  offset: args.offset,
203
  limit: args.limit,
 
204
  extend: args.extend,
205
  security: args.security
206
  },
@@ -251,25 +253,22 @@ function process_indexing_step(args) {
251
  "relevanssi_estimated"
252
  ).innerHTML = estimated_time
253
 
254
- /*console.log("total time: " + total_seconds);
255
- console.log("estimated time: " + Math.round(total_seconds / response.percentage * 100));
256
- console.log("estimated remaining: " + Math.round((total_seconds / response.percentage * 100) - total_seconds));
257
- console.log("estimated formatted: " + estimated_time);
258
- */
259
- if (time_seconds < 2) {
260
- args.limit = args.limit * 2
261
- // current limit can be indexed in less than two seconds; double the limit
262
- } else if (time_seconds < 5) {
263
- args.limit += 5
264
- // current limit can be indexed in less than five seconds; up the limit
265
- } else if (time_seconds > 20) {
266
- args.limit = Math.round(args.limit / 2)
267
- if (args.limit < 1) args.limit = 1
268
- // current limit takes more than twenty seconds; halve the limit
269
- } else if (time_seconds > 10) {
270
- args.limit -= 5
271
- if (args.limit < 1) args.limit = 1
272
- // current limit takes more than ten seconds; reduce the limit
273
  }
274
 
275
  var results_textarea = document.getElementById("results")
@@ -293,6 +292,7 @@ function process_indexing_step(args) {
293
  offset: response.offset,
294
  total_seconds: args.total_seconds,
295
  limit: args.limit,
 
296
  extend: args.extend,
297
  security: args.security
298
  }
178
  offset: 0,
179
  total_seconds: 0,
180
  limit: relevanssi_params.indexing_limit,
181
+ adjust: relevanssi_params.indexing_adjust,
182
  extend: true,
183
  security: nonce.indexing_nonce
184
  }
202
  total: args.total,
203
  offset: args.offset,
204
  limit: args.limit,
205
+ adjust: args.adjust,
206
  extend: args.extend,
207
  security: args.security
208
  },
253
  "relevanssi_estimated"
254
  ).innerHTML = estimated_time
255
 
256
+ if (args.adjust) {
257
+ if (time_seconds < 2) {
258
+ args.limit = args.limit * 2
259
+ // current limit can be indexed in less than two seconds; double the limit
260
+ } else if (time_seconds < 5) {
261
+ args.limit += 5
262
+ // current limit can be indexed in less than five seconds; up the limit
263
+ } else if (time_seconds > 20) {
264
+ args.limit = Math.round(args.limit / 2)
265
+ if (args.limit < 1) args.limit = 1
266
+ // current limit takes more than twenty seconds; halve the limit
267
+ } else if (time_seconds > 10) {
268
+ args.limit -= 5
269
+ if (args.limit < 1) args.limit = 1
270
+ // current limit takes more than ten seconds; reduce the limit
271
+ }
 
 
 
272
  }
273
 
274
  var results_textarea = document.getElementById("results")
292
  offset: response.offset,
293
  total_seconds: args.total_seconds,
294
  limit: args.limit,
295
+ adjust: args.adjust,
296
  extend: args.extend,
297
  security: args.security
298
  }
lib/common.php CHANGED
@@ -1672,3 +1672,14 @@ function relevanssi_get_forbidden_taxonomies() {
1672
  'product_visibility', // WooCommerce.
1673
  );
1674
  }
 
 
 
 
 
 
 
 
 
 
 
1672
  'product_visibility', // WooCommerce.
1673
  );
1674
  }
1675
+
1676
+ /**
1677
+ * Returns "off".
1678
+ *
1679
+ * Useful for returning "off" to filters easily.
1680
+ *
1681
+ * @return string A string with value "off".
1682
+ */
1683
+ function relevanssi_return_off() {
1684
+ return 'off';
1685
+ }
lib/compatibility/acf.php CHANGED
@@ -39,37 +39,74 @@ function relevanssi_acf_relationship_fields( $search_ok ) {
39
  * @param int $post_id The post ID.
40
  * @param string $field_name Name of the field.
41
  * @param string $field_value The field value.
 
 
42
  */
43
  function relevanssi_index_acf( &$insert_data, $post_id, $field_name, $field_value ) {
44
  if ( ! is_admin() ) {
45
  include_once ABSPATH . 'wp-admin/includes/plugin.php'; // Otherwise is_plugin_active() will cause a fatal error.
46
  }
47
  if ( ! function_exists( 'is_plugin_active' ) ) {
48
- return;
49
  }
50
  if ( ! is_plugin_active( 'advanced-custom-fields/acf.php' ) && ! is_plugin_active( 'advanced-custom-fields-pro/acf.php' ) ) {
51
- return;
52
  }
53
  if ( ! function_exists( 'get_field_object' ) ) {
54
- return; // ACF is active, but not loaded.
55
  }
56
 
57
  $field_object = get_field_object( $field_name, $post_id );
58
  if ( ! isset( $field_object['choices'] ) ) {
59
- return; // Not a "select" field.
60
  }
61
  if ( is_array( $field_value ) ) {
62
- return; // Not handled (currently).
63
  }
64
  if ( ! isset( $field_object['choices'][ $field_value ] ) ) {
65
- return; // Value does not exist.
66
  }
67
 
68
- $value = $field_object['choices'][ $field_value ];
69
- if ( $value ) {
70
- if ( ! isset( $insert_data[ $value ]['customfield'] ) ) {
71
- $insert_data[ $value ]['customfield'] = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
- $insert_data[ $value ]['customfield']++;
74
  }
 
 
75
  }
39
  * @param int $post_id The post ID.
40
  * @param string $field_name Name of the field.
41
  * @param string $field_value The field value.
42
+ *
43
+ * @return int Number of tokens indexed.
44
  */
45
  function relevanssi_index_acf( &$insert_data, $post_id, $field_name, $field_value ) {
46
  if ( ! is_admin() ) {
47
  include_once ABSPATH . 'wp-admin/includes/plugin.php'; // Otherwise is_plugin_active() will cause a fatal error.
48
  }
49
  if ( ! function_exists( 'is_plugin_active' ) ) {
50
+ return 0;
51
  }
52
  if ( ! is_plugin_active( 'advanced-custom-fields/acf.php' ) && ! is_plugin_active( 'advanced-custom-fields-pro/acf.php' ) ) {
53
+ return 0;
54
  }
55
  if ( ! function_exists( 'get_field_object' ) ) {
56
+ return 0; // ACF is active, but not loaded.
57
  }
58
 
59
  $field_object = get_field_object( $field_name, $post_id );
60
  if ( ! isset( $field_object['choices'] ) ) {
61
+ return 0; // Not a "select" field.
62
  }
63
  if ( is_array( $field_value ) ) {
64
+ return 0; // Not handled (currently).
65
  }
66
  if ( ! isset( $field_object['choices'][ $field_value ] ) ) {
67
+ return 0; // Value does not exist.
68
  }
69
 
70
+ $n = 0;
71
+
72
+ /**
73
+ * Filters the field value before it is used to save the insert data.
74
+ *
75
+ * The value is used as an array key, so it needs to be an integer or a
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
+ */
85
+ $value = apply_filters(
86
+ 'relevanssi_acf_field_value',
87
+ $field_object['choices'][ $field_value ],
88
+ $field_name,
89
+ $post_id
90
+ );
91
+
92
+ if ( $value && ( is_integer( $value ) || is_string( $value ) ) ) {
93
+ $min_word_length = get_option( 'relevanssi_min_word_length', 3 );
94
+
95
+ /** This filter is documented in lib/indexing.php */
96
+ $value_tokens = apply_filters( 'relevanssi_indexing_tokens', relevanssi_tokenize( $value, true, $min_word_length ), 'custom_field' );
97
+ foreach ( $value_tokens as $token => $count ) {
98
+ $n++;
99
+ if ( ! isset( $insert_data[ $token ]['customfield'] ) ) {
100
+ $insert_data[ $token ]['customfield'] = 0;
101
+ }
102
+ $insert_data[ $token ]['customfield'] += $count;
103
+
104
+ // Premium indexes more detail about custom fields.
105
+ if ( function_exists( 'relevanssi_customfield_detail' ) ) {
106
+ $insert_data = relevanssi_customfield_detail( $insert_data, $token, $count, $field_name );
107
+ }
108
  }
 
109
  }
110
+
111
+ return $n;
112
  }
lib/compatibility/polylang.php CHANGED
@@ -87,9 +87,12 @@ function relevanssi_polylang_filter( $query ) {
87
  * @since 2.1.6
88
  */
89
  function relevanssi_polylang_where_include_terms( $where ) {
90
- $current_language = pll_current_language();
91
- $languages = get_terms( array( 'taxonomy' => 'language' ) );
92
- $language_id = 0;
 
 
 
93
  foreach ( $languages as $language ) {
94
  if ( ! is_wp_error( $language ) && $language instanceof WP_Term && $language->slug === $current_language ) {
95
  $language_id = intval( $language->term_id );
@@ -125,8 +128,11 @@ function relevanssi_polylang_where_include_terms( $where ) {
125
  function relevanssi_polylang_term_filter( $hits ) {
126
  $polylang_allow_all = get_option( 'relevanssi_polylang_all_languages' );
127
  if ( 'on' !== $polylang_allow_all ) {
128
- $current_language = pll_current_language();
129
- $accepted_hits = array();
 
 
 
130
  foreach ( $hits[0] as $hit ) {
131
  if ( -1 === $hit->ID && isset( $hit->term_id ) ) {
132
  $term_id = intval( $hit->term_id );
87
  * @since 2.1.6
88
  */
89
  function relevanssi_polylang_where_include_terms( $where ) {
90
+ $current_language = substr( get_locale(), 0, 2 );
91
+ if ( function_exists( 'pll_current_language' ) ) {
92
+ $current_language = pll_current_language();
93
+ }
94
+ $languages = get_terms( array( 'taxonomy' => 'language' ) );
95
+ $language_id = 0;
96
  foreach ( $languages as $language ) {
97
  if ( ! is_wp_error( $language ) && $language instanceof WP_Term && $language->slug === $current_language ) {
98
  $language_id = intval( $language->term_id );
128
  function relevanssi_polylang_term_filter( $hits ) {
129
  $polylang_allow_all = get_option( 'relevanssi_polylang_all_languages' );
130
  if ( 'on' !== $polylang_allow_all ) {
131
+ $current_language = substr( get_locale(), 0, 2 );
132
+ if ( function_exists( 'pll_current_language' ) ) {
133
+ $current_language = pll_current_language();
134
+ }
135
+ $accepted_hits = array();
136
  foreach ( $hits[0] as $hit ) {
137
  if ( -1 === $hit->ID && isset( $hit->term_id ) ) {
138
  $term_id = intval( $hit->term_id );
lib/compatibility/seopress.php CHANGED
@@ -12,6 +12,7 @@
12
  */
13
 
14
  add_filter( 'relevanssi_do_not_index', 'relevanssi_seopress_noindex', 10, 2 );
 
15
 
16
  /**
17
  * Blocks indexing of posts marked "noindex" in the SEOPress settings.
@@ -30,3 +31,17 @@ function relevanssi_seopress_noindex( $do_not_index, $post_id ) {
30
  }
31
  return $do_not_index;
32
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  */
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.
31
  }
32
  return $do_not_index;
33
  }
34
+
35
+ /**
36
+ * Excludes the "noindex" posts from Relevanssi indexing.
37
+ *
38
+ * Adds a MySQL query restriction that blocks posts that have the SEOPress
39
+ * "noindex" setting set to "1" from indexing.
40
+ *
41
+ * @param string $restriction The MySQL query restriction to modify.
42
+ */
43
+ function relevanssi_seopress_exclude( $restriction ) {
44
+ global $wpdb;
45
+ $restriction .= " AND post.ID NOT IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_seopress_robots_index' AND meta_value = 'yes' ) ";
46
+ return $restriction;
47
+ }
lib/compatibility/tablepress.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * /lib/compatibility/tablepress.php
4
+ *
5
+ * TablePress 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
+ /**
14
+ * Enables TablePress shortcodes for Relevanssi indexing.
15
+ *
16
+ * @return null|object The TablePress controller.
17
+ */
18
+ function relevanssi_enable_tablepress_shortcodes() {
19
+ $my_tablepress_controller = null;
20
+ if ( defined( 'TABLEPRESS_ABSPATH' ) ) {
21
+ if ( ! isset( TablePress::$model_options ) ) {
22
+ include_once TABLEPRESS_ABSPATH . 'classes/class-model.php';
23
+ include_once TABLEPRESS_ABSPATH . 'models/model-options.php';
24
+ TablePress::$model_options = new TablePress_Options_Model();
25
+ }
26
+ $my_tablepress_controller = TablePress::load_controller( 'frontend' );
27
+ $my_tablepress_controller->init_shortcodes();
28
+ }
29
+ return $my_tablepress_controller;
30
+ }
lib/compatibility/wpml.php CHANGED
@@ -33,9 +33,16 @@ function relevanssi_wpml_filter( $data ) {
33
  $current_blog_language = get_bloginfo( 'language' );
34
  $filtered_hits = array();
35
  foreach ( $data[0] as $hit ) {
36
- if ( is_integer( $hit ) ) {
 
37
  // In case "fields" is set to "ids", fetch the post object we need.
38
- $hit = get_post( $hit );
 
 
 
 
 
 
39
  }
40
 
41
  if ( isset( $hit->blog_id ) ) {
@@ -63,16 +70,16 @@ function relevanssi_wpml_filter( $data ) {
63
  // This is a post in a translated post type.
64
  if ( intval( $hit->ID ) === intval( $id ) ) {
65
  // The post exists in the current language, and can be included.
66
- $filtered_hits[] = $hit;
67
  }
68
  } else {
69
  // This is not a translated post type, so include all posts.
70
- $filtered_hits[] = $hit;
71
  }
72
  } elseif ( get_bloginfo( 'language' ) === $current_blog_language ) {
73
  // If there is no WPML but the target blog has identical language with current blog,
74
  // we use the hits. Note en-US is not identical to en-GB!
75
- $filtered_hits[] = $hit;
76
  }
77
 
78
  if ( isset( $hit->blog_id ) ) {
33
  $current_blog_language = get_bloginfo( 'language' );
34
  $filtered_hits = array();
35
  foreach ( $data[0] as $hit ) {
36
+ $original_hit = $hit;
37
+ if ( is_numeric( $hit ) ) {
38
  // In case "fields" is set to "ids", fetch the post object we need.
39
+ $original_hit = $hit;
40
+ $hit = get_post( $hit );
41
+ }
42
+ if ( ! isset( $hit->post_content ) ) {
43
+ // The "fields" is set to "id=>parent".
44
+ $original_hit = $hit;
45
+ $hit = get_post( $hit->ID );
46
  }
47
 
48
  if ( isset( $hit->blog_id ) ) {
70
  // This is a post in a translated post type.
71
  if ( intval( $hit->ID ) === intval( $id ) ) {
72
  // The post exists in the current language, and can be included.
73
+ $filtered_hits[] = $original_hit;
74
  }
75
  } else {
76
  // This is not a translated post type, so include all posts.
77
+ $filtered_hits[] = $original_hit;
78
  }
79
  } elseif ( get_bloginfo( 'language' ) === $current_blog_language ) {
80
  // If there is no WPML but the target blog has identical language with current blog,
81
  // we use the hits. Note en-US is not identical to en-GB!
82
+ $filtered_hits[] = $original_hit;
83
  }
84
 
85
  if ( isset( $hit->blog_id ) ) {
lib/compatibility/yoast-seo.php CHANGED
@@ -11,6 +11,7 @@
11
  */
12
 
13
  add_filter( 'relevanssi_do_not_index', 'relevanssi_yoast_noindex', 10, 2 );
 
14
 
15
  /**
16
  * Blocks indexing of posts marked "noindex" in the Yoast SEO settings.
@@ -29,3 +30,17 @@ function relevanssi_yoast_noindex( $do_not_index, $post_id ) {
29
  }
30
  return $do_not_index;
31
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  */
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.
30
  }
31
  return $do_not_index;
32
  }
33
+
34
+ /**
35
+ * Excludes the "noindex" posts from Relevanssi indexing.
36
+ *
37
+ * Adds a MySQL query restriction that blocks posts that have the Yoast SEO
38
+ * "noindex" setting set to "1" from indexing.
39
+ *
40
+ * @param string $restriction The MySQL query restriction to modify.
41
+ */
42
+ function relevanssi_yoast_exclude( $restriction ) {
43
+ global $wpdb;
44
+ $restriction .= " AND post.ID NOT IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_yoast_wpseo_meta-robots-noindex' AND meta_value = '1' ) ";
45
+ return $restriction;
46
+ }
lib/contextual-help.php CHANGED
@@ -28,9 +28,9 @@ function relevanssi_admin_help() {
28
  '<li>' . sprintf( __( "To adjust the post order, you can use the %1\$s query parameter. With %1\$s, you can use multiple layers of different sorting methods. See <a href='%2\$s'>WordPress Codex</a> for more details on using arrays for orderby.", 'relevanssi' ), '<code>orderby</code>', 'https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters' ) . '</li>' .
29
  '<li>' . __( "Inside-word matching is disabled by default, because it increases garbage results that don't really match the search term. If you want to enable it, add the following function to your theme functions.php:", 'relevanssi' ) .
30
  '<pre>add_filter( \'relevanssi_fuzzy_query\', \'rlv_partial_inside_words\' );
31
- function rlv_partial_inside_words( $query ) {
32
- return "(term LIKE \'%#term#%\')";
33
- }</pre></li>' .
34
  // Translators: %s is 'Uncheck this if you use non-ASCII characters' option name.
35
  '<li>' . sprintf( __( 'To get inside-word highlights, uncheck the "%s" option. That has a side-effect of enabling the inside-word highlights.', 'relevanssi' ), __( 'Uncheck this if you use non-ASCII characters', 'relevanssi' ) ) . '</li>' .
36
  // Translators: %s is 'relevanssi_throttle_limit'.
28
  '<li>' . sprintf( __( "To adjust the post order, you can use the %1\$s query parameter. With %1\$s, you can use multiple layers of different sorting methods. See <a href='%2\$s'>WordPress Codex</a> for more details on using arrays for orderby.", 'relevanssi' ), '<code>orderby</code>', 'https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters' ) . '</li>' .
29
  '<li>' . __( "Inside-word matching is disabled by default, because it increases garbage results that don't really match the search term. If you want to enable it, add the following function to your theme functions.php:", 'relevanssi' ) .
30
  '<pre>add_filter( \'relevanssi_fuzzy_query\', \'rlv_partial_inside_words\' );
31
+ function rlv_partial_inside_words( $query ) {
32
+ return "(relevanssi.term LIKE \'%#term#%\')";
33
+ }</pre></li>' .
34
  // Translators: %s is 'Uncheck this if you use non-ASCII characters' option name.
35
  '<li>' . sprintf( __( 'To get inside-word highlights, uncheck the "%s" option. That has a side-effect of enabling the inside-word highlights.', 'relevanssi' ), __( 'Uncheck this if you use non-ASCII characters', 'relevanssi' ) ) . '</li>' .
36
  // Translators: %s is 'relevanssi_throttle_limit'.
lib/indexing.php CHANGED
@@ -126,6 +126,7 @@ function relevanssi_generate_indexing_query( $valid_status, $extend = false, $re
126
  OR (post.post_parent=0)
127
  )
128
  ))
 
129
  $restriction ORDER BY post.ID DESC $limit";
130
  } else {
131
  $q = "SELECT post.ID
@@ -144,6 +145,7 @@ function relevanssi_generate_indexing_query( $valid_status, $extend = false, $re
144
  )
145
  )
146
  )
 
147
  $restriction ORDER BY post.ID DESC $limit";
148
  }
149
 
@@ -233,7 +235,7 @@ function relevanssi_valid_status_array() {
233
  * @param boolean|int $extend_offset If numeric, offsets the indexing by that
234
  * amount. If true, doesn't truncate the index before indexing. If false, truncates
235
  * index before indexing. Default false.
236
- * @param boolean $verbose If true, echoes out information. Default true.
237
  * @param int $post_limit How many posts to index. Default null, no limit.
238
  * @param boolean $is_ajax If true, indexing is done in AJAX context.
239
  * Default false.
@@ -243,104 +245,51 @@ function relevanssi_valid_status_array() {
243
  * posts indexed. Outside AJAX context, these values are returned as an array in
244
  * format of array(completed, posts indexed).
245
  */
246
- function relevanssi_build_index( $extend_offset = false, $verbose = true, $post_limit = null, $is_ajax = false ) {
247
  global $wpdb, $relevanssi_variables;
248
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
249
 
250
  // Thanks to Julien Mession. This speeds up indexing a lot.
251
  wp_suspend_cache_addition( true );
252
 
253
- // The values generated by these functions are safe to use for MySQL.
254
- $restriction = relevanssi_post_type_restriction();
255
- $valid_status = relevanssi_valid_status_array();
256
-
257
- $n = 0;
258
- $size = 0;
259
-
260
  if ( false === $extend_offset ) {
261
  // Truncate the index first.
262
  relevanssi_truncate_index();
263
 
264
- // Premium feature: index taxonomy terms.
265
- if ( function_exists( 'relevanssi_index_taxonomies' ) ) {
266
- if ( 'on' === get_option( 'relevanssi_index_taxonomies' ) ) {
267
- relevanssi_index_taxonomies();
268
- }
269
- }
270
-
271
- // Premium feature: index user profiles.
272
- if ( function_exists( 'relevanssi_index_users' ) ) {
273
- if ( 'on' === get_option( 'relevanssi_index_users' ) ) {
274
- relevanssi_index_users();
275
- }
276
- }
277
-
278
- // Premium feature: index post type archives.
279
- if ( function_exists( 'relevanssi_index_post_type_archives' ) ) {
280
- if ( 'on' === get_option( 'relevanssi_index_post_type_archives' ) ) {
281
- relevanssi_index_post_type_archives();
282
- }
283
  }
284
 
285
- // If $post_limit parameter is present, numeric and > 0, use that.
286
- $limit = '';
287
- if ( isset( $post_limit ) && is_numeric( $post_limit ) && $post_limit > 0 ) {
288
- $size = $post_limit;
289
- $limit = " LIMIT $post_limit";
290
- }
291
-
292
- $query = relevanssi_generate_indexing_query( $valid_status, $extend_offset, $restriction, $limit );
293
-
294
  update_option( 'relevanssi_index', '' );
295
- } elseif ( ! is_numeric( $extend_offset ) ) {
296
- // Extending, so do not truncate and skip the posts already in the index.
297
- $limit = get_option( 'relevanssi_index_limit', 200 );
298
-
299
- // If $post_limit parameter is present, numeric and > 0, use that.
300
- if ( isset( $post_limit ) && is_numeric( $post_limit ) && $post_limit > 0 ) {
301
- $limit = $post_limit;
302
- }
303
-
304
- if ( is_numeric( $limit ) && $limit > 0 ) {
305
- $size = $limit;
306
- $limit = " LIMIT $limit";
307
- } else {
308
- $limit = '';
309
- }
310
-
311
- $extend = true;
312
- $query = relevanssi_generate_indexing_query( $valid_status, $extend, $restriction, $limit );
313
- } else { // $extend_offset is numeric.
314
- // Extending, so do not truncate and skip the posts already in the index.
315
- $limit = get_option( 'relevanssi_index_limit', 200 );
316
-
317
- // If $post_limit parameter is present, numeric and > 0, use that.
318
- if ( isset( $post_limit ) && is_numeric( $post_limit ) && $post_limit > 0 ) {
319
- $limit = $post_limit;
320
- }
321
 
322
- if ( is_numeric( $limit ) && $limit > 0 ) {
323
- $size = $limit;
324
- $limit = " LIMIT $limit OFFSET $extend_offset";
325
- } else {
326
- $limit = '';
327
- }
328
 
329
- // Extend is set to false, because $limit now has LIMIT and OFFSET.
330
- $extend = false;
331
- $query = relevanssi_generate_indexing_query( $valid_status, $extend, $restriction, $limit );
332
- }
333
 
334
- $custom_fields = relevanssi_get_custom_fields();
 
 
 
 
 
335
 
336
  /* This action documented earlier in lib/indexing.php. */
337
  do_action( 'relevanssi_pre_indexing_query' );
338
- $content = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
339
 
 
340
  if ( defined( 'WP_CLI' ) && WP_CLI && function_exists( 'relevanssi_generate_progress_bar' ) ) {
 
341
  $progress = relevanssi_generate_progress_bar( 'Indexing posts', count( $content ) );
 
342
  }
343
 
 
 
 
344
  $remove_first = false;
345
  $bypass_global_post = true; // $bypassglobalpost set to true, because at this
346
  // point global $post should be null, but in some cases it is not.
@@ -351,29 +300,28 @@ function relevanssi_build_index( $extend_offset = false, $verbose = true, $post_
351
  $n++;
352
  }
353
  if ( defined( 'WP_CLI' ) && WP_CLI && $progress ) {
 
354
  $progress->tick();
 
355
  }
356
  }
357
  if ( defined( 'WP_CLI' ) && WP_CLI && $progress ) {
 
358
  $progress->finish();
 
359
  }
360
 
361
  // To prevent empty indices.
362
  $wpdb->query( "ANALYZE TABLE $relevanssi_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
363
 
364
  $complete = false;
 
 
365
  if ( ( 0 === $size ) || ( count( $content ) < $size ) ) {
366
- $message = __( 'Indexing complete!', 'relevanssi' );
367
  $complete = true;
368
- } else {
369
- $message = __( 'More to index...', 'relevanssi' );
370
- }
371
- if ( $verbose ) {
372
- printf( '<div id="message" class="updated fade"><p>%s</p></div>', esc_html( $message ) );
373
  }
374
 
375
- update_option( 'relevanssi_indexed', 'done' );
376
-
377
  // Update the document count variable.
378
  relevanssi_update_doc_count();
379
 
@@ -414,74 +362,41 @@ function relevanssi_build_index( $extend_offset = false, $verbose = true, $post_
414
  * @param boolean $remove_first If true, remove the post from the index
415
  * before indexing. Default false.
416
  * @param array $custom_fields The custom fields that are indexed for the
417
- * post.
418
  * @param boolean $bypass_global_post If true, do not use the global $post object.
419
  * Default false.
420
  * @param boolean $debug If true, echo out debugging information.
421
  * Default false.
422
  */
423
- function relevanssi_index_doc( $index_post, $remove_first = false, $custom_fields = false, $bypass_global_post = false, $debug = false ) {
424
  global $wpdb, $post, $relevanssi_variables;
425
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
426
- $post_was_null = false;
427
  $previous_post = null;
428
 
429
  // Check if this is a Jetpack Contact Form entry.
430
  if ( isset( $_REQUEST['contact-form-id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
431
- return;
432
  }
433
 
434
- if ( $bypass_global_post ) {
435
- // If $bypass_global_post is set, relevanssi_index_doc() will index the post
436
- // object or post ID as specified in $index_post.
437
- if ( isset( $post ) ) {
438
- $previous_post = $post;
439
- } else {
440
- $post_was_null = true;
441
- }
442
-
443
- if ( is_object( $index_post ) ) {
444
- $post = $index_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
445
- } else {
446
- $post = get_post( $index_post ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
447
- }
448
- } else {
449
- // Quick edit has an array in the global $post, so fetch the post ID for the
450
- // post to edit.
451
- if ( is_array( $post ) ) {
452
- $post = get_post( $post['ID'] ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
453
- }
454
-
455
- if ( empty( $post ) ) {
456
- // No $post set, so we need to use $indexpost, if it's a post object.
457
- $post_was_null = true;
458
- if ( is_object( $index_post ) ) {
459
- $post = $index_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
460
- } else {
461
- $post = get_post( $index_post ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
462
- }
463
- } else {
464
- // $post was set, let's grab the previous value in case we need it
465
- $previous_post = $post;
466
- }
467
  }
468
 
469
- if ( null === $post ) {
470
- // At this point we should have something in $post; if not, quit.
471
- if ( $post_was_null ) {
472
- $post = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
473
- }
474
- if ( $previous_post ) {
475
- $post = $previous_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
476
- }
477
- return -1;
478
  }
479
 
480
  // Finally fetch the post again by ID. Complicated, yes, but unless we do this,
481
  // we might end up indexing the post before the updates come in.
482
- $post = get_post( $post->ID ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
483
 
484
  if ( null === $post ) {
 
 
 
 
485
  return -1;
486
  }
487
 
@@ -491,10 +406,7 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
491
  if ( $debug ) {
492
  relevanssi_debug_echo( 'relevanssi_hide_post() returned true.' );
493
  }
494
- if ( $post_was_null ) {
495
- $post = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
496
- }
497
- if ( $previous_post ) {
498
  $post = $previous_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
499
  }
500
  return 'hide';
@@ -504,11 +416,8 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
504
  $index_this_post = false;
505
  $post->indexing_content = true;
506
 
507
- $index_types = get_option( 'relevanssi_index_post_types' );
508
- if ( ! is_array( $index_types ) ) {
509
- $index_types = array();
510
- }
511
- if ( in_array( $post->post_type, $index_types, true ) ) {
512
  $index_this_post = true;
513
  }
514
 
@@ -542,10 +451,7 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
542
  // This needs to be here, after the call to relevanssi_remove_doc(), because
543
  // otherwise a post that's in the index but shouldn't be there won't get removed.
544
  if ( ! $index_this_post ) {
545
- if ( $post_was_null ) {
546
- $post = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
547
- }
548
- if ( $previous_post ) {
549
  $post = $previous_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
550
  }
551
  return 'donotindex';
@@ -566,161 +472,26 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
566
  $insert_data = array();
567
 
568
  if ( 'none' !== get_option( 'relevanssi_index_comments' ) ) {
569
- if ( $debug ) {
570
- relevanssi_debug_echo( 'Indexing comments.' );
571
- }
572
- $post_comments = relevanssi_get_comments( $post->ID );
573
- if ( ! empty( $post_comments ) ) {
574
- $post_comments = relevanssi_strip_invisibles( $post_comments );
575
- $post_comments = preg_replace( '/<[a-zA-Z\/][^>]*>/', ' ', $post_comments );
576
- $post_comments = wp_strip_all_tags( $post_comments );
577
- if ( $debug ) {
578
- relevanssi_debug_echo( "Comment content: $post_comments" );
579
- }
580
- /**
581
- * Filters the indexing tokens before they are added to the $insert_data.
582
- *
583
- * @param array An array of token-frequency pairs.
584
- * @param string The context of the tokens (eg. 'content', 'title').
585
- *
586
- * @return array The filtered tokens.
587
- */
588
- $post_comments_tokens = apply_filters( 'relevanssi_indexing_tokens', relevanssi_tokenize( $post_comments, true, $min_word_length ), 'comments' );
589
- if ( count( $post_comments_tokens ) > 0 ) {
590
- foreach ( $post_comments_tokens as $token => $count ) {
591
- $n++;
592
- $insert_data[ $token ]['comment'] = $count;
593
- }
594
- }
595
- }
596
  }
597
 
598
- // Process taxonomies.
599
  $taxonomies = get_option( 'relevanssi_index_taxonomies_list', array() );
600
  foreach ( $taxonomies as $taxonomy ) {
601
- if ( $debug ) {
602
- relevanssi_debug_echo( "Indexing taxonomy terms for $taxonomy" );
603
- }
604
- $insert_data = relevanssi_index_taxonomy_terms( $post, $taxonomy, $insert_data );
605
  }
606
 
607
- // Index post author.
608
  if ( 'on' === get_option( 'relevanssi_index_author' ) ) {
609
- $author_id = $post->post_author;
610
- $display_name = get_the_author_meta( 'display_name', $author_id );
611
- /** This filter is documented in common/indexing.php */
612
- $name_tokens = apply_filters( 'relevanssi_indexing_tokens', relevanssi_tokenize( $display_name, false, $min_word_length ), 'author' );
613
- if ( $debug ) {
614
- relevanssi_debug_echo( 'Indexing post author as: ' . implode( ' ', array_keys( $name_tokens ) ) );
615
- }
616
- foreach ( $name_tokens as $token => $count ) {
617
- if ( isset( $insert_data[ $token ]['author'] ) ) {
618
- $insert_data[ $token ]['author'] += $count;
619
- } else {
620
- $insert_data[ $token ]['author'] = $count;
621
- }
622
- }
623
- }
624
-
625
- // Indexing custom fields.
626
- $remove_underscore_fields = false;
627
- if ( isset( $custom_fields ) && 'all' === $custom_fields ) {
628
- $custom_fields = get_post_custom_keys( $post->ID );
629
- }
630
- if ( isset( $custom_fields ) && 'visible' === $custom_fields ) {
631
- $custom_fields = get_post_custom_keys( $post->ID );
632
- $remove_underscore_fields = true;
633
  }
634
- /**
635
- * Filters the list of custom fields to index before indexing.
636
- *
637
- * @param array $custom_fields List of custom field names.
638
- * @param int $post->ID The post ID.
639
- */
640
- $custom_fields = apply_filters( 'relevanssi_index_custom_fields', $custom_fields, $post->ID );
641
- if ( is_array( $custom_fields ) ) {
642
- if ( $debug ) {
643
- relevanssi_debug_echo( 'Custom fields to index: ' . implode( ', ', $custom_fields ) );
644
- }
645
- $custom_fields = array_unique( $custom_fields );
646
-
647
- // Premium includes some support for ACF repeater fields.
648
- $repeater_fields = array();
649
- if ( function_exists( 'relevanssi_add_repeater_fields' ) ) {
650
- relevanssi_add_repeater_fields( $custom_fields, $post->ID );
651
- }
652
-
653
- foreach ( $custom_fields as $field ) {
654
- if ( $remove_underscore_fields ) {
655
- if ( '_relevanssi_pdf_content' !== $field && '_' === substr( $field, 0, 1 ) ) {
656
- // We always want to index _relevanssi_pdf_content.
657
- continue;
658
- }
659
- }
660
-
661
- /**
662
- * Filters the custom field value before indexing.
663
- *
664
- * @param array Custom field values.
665
- * @param string $field The custom field name.
666
- * @param int $post->ID The post ID.
667
- */
668
- $values = apply_filters( 'relevanssi_custom_field_value', get_post_meta( $post->ID, $field, false ), $field, $post->ID );
669
-
670
- if ( empty( $values ) || ! is_array( $values ) ) {
671
- continue;
672
- }
673
-
674
- foreach ( $values as $value ) {
675
- // Quick hack : allow indexing of PODS relationship custom fields // TMV.
676
- if ( is_array( $value ) && isset( $value['post_title'] ) ) {
677
- $value = $value['post_title'];
678
- }
679
-
680
- if ( function_exists( 'relevanssi_index_acf' ) ) {
681
- // Handle ACF fields. Only defined when ACF is active.
682
- relevanssi_index_acf( $insert_data, $post->ID, $field, $value );
683
- }
684
-
685
- // Flatten other arrays.
686
- if ( is_array( $value ) ) {
687
- $value = relevanssi_flatten_array( $value );
688
- }
689
 
690
- if ( $debug ) {
691
- relevanssi_debug_echo( "\tKey: " . $field . ' – value: ' . $value );
692
- }
693
- /** This filter is documented in common/indexing.php */
694
- $value_tokens = apply_filters( 'relevanssi_indexing_tokens', relevanssi_tokenize( $value, true, $min_word_length ), 'custom_field' );
695
- foreach ( $value_tokens as $token => $count ) {
696
- if ( ! isset( $insert_data[ $token ]['customfield'] ) ) {
697
- $insert_data[ $token ]['customfield'] = 0;
698
- }
699
- $insert_data[ $token ]['customfield'] += $count;
700
-
701
- // Premium indexes more detail about custom fields.
702
- if ( function_exists( 'relevanssi_customfield_detail' ) ) {
703
- $insert_data = relevanssi_customfield_detail( $insert_data, $token, $count, $field );
704
- }
705
- }
706
- }
707
- }
708
- }
709
 
710
- // Indexing excerpts.
711
- if ( isset( $post->post_excerpt ) && ( 'on' === get_option( 'relevanssi_index_excerpt' ) || 'attachment' === $post->post_type ) ) {
712
- // Include excerpt for attachments which use post_excerpt for captions - modified by renaissancehack.
713
- if ( $debug ) {
714
- relevanssi_debug_echo( "Indexing post excerpt: $post->post_excerpt" );
715
- }
716
- /** This filter is documented in common/indexing.php */
717
- $excerpt_tokens = apply_filters( 'relevanssi_indexing_tokens', relevanssi_tokenize( $post->post_excerpt, true, $min_word_length ), 'excerpt' );
718
- foreach ( $excerpt_tokens as $token => $count ) {
719
- if ( ! isset( $insert_data[ $token ]['excerpt'] ) ) {
720
- $insert_data[ $token ]['excerpt'] = 0;
721
- }
722
- $insert_data[ $token ]['excerpt'] += $count;
723
- }
724
  }
725
 
726
  // Premium can index arbitrary MySQL columns.
@@ -739,301 +510,10 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
739
  $insert_data = relevanssi_index_pdf_for_parent( $insert_data, $post->ID );
740
  }
741
 
742
- $index_titles = true;
743
- if ( ! empty( $post->post_title ) ) {
744
- /**
745
- * If this filter returns false, titles are not indexed at all.
746
- *
747
- * @param boolean Return false to prevent titles from being indexed. Default true.
748
- */
749
- if ( apply_filters( 'relevanssi_index_titles', $index_titles ) ) {
750
- if ( $debug ) {
751
- relevanssi_debug_echo( 'Indexing post title.' );
752
- }
753
- /** This filter is documented in wp-includes/post-template.php */
754
- $filtered_title = apply_filters( 'the_title', $post->post_title, $post->ID );
755
- /**
756
- * Filters the title before tokenizing and indexing.
757
- *
758
- * @param string $post->post_title The title.
759
- * @param object $post The full post object.
760
- */
761
- $filtered_title = apply_filters( 'relevanssi_post_title_before_tokenize', $filtered_title, $post );
762
- /**
763
- * Filters whether stopwords should be removed from titles in tokenizing or not.
764
- *
765
- * @param boolean If true, remove stopwords. Default true.
766
- */
767
- $title_tokens = relevanssi_tokenize( $filtered_title, apply_filters( 'relevanssi_remove_stopwords_in_titles', true ), $min_word_length );
768
- /** This filter is documented in common/indexing.php */
769
- $title_tokens = apply_filters( 'relevanssi_indexing_tokens', $title_tokens, 'title' );
770
-
771
- if ( $debug ) {
772
- relevanssi_debug_echo( "\tTitle, tokenized: " . implode( ' ', array_keys( $title_tokens ) ) );
773
- }
774
-
775
- if ( count( $title_tokens ) > 0 ) {
776
- foreach ( $title_tokens as $token => $count ) {
777
- $n++;
778
- if ( ! isset( $insert_data[ $token ]['title'] ) ) {
779
- $insert_data[ $token ]['title'] = 0;
780
- }
781
- $insert_data[ $token ]['title'] += $count;
782
- }
783
- }
784
- }
785
- }
786
-
787
- // Content indexing.
788
- $index_content = true;
789
- /**
790
- * If this filter returns false, post content is not indexed at all.
791
- *
792
- * @param boolean Return false to prevent post content from being indexed. Default true.
793
- */
794
- if ( apply_filters( 'relevanssi_index_content', $index_content ) ) {
795
- if ( $debug ) {
796
- relevanssi_debug_echo( 'Indexing post content.' );
797
- }
798
- remove_shortcode( 'noindex' );
799
- add_shortcode( 'noindex', 'relevanssi_noindex_shortcode_indexing' );
800
-
801
- /**
802
- * Filters the post content before indexing.
803
- *
804
- * @param string $post->post_content The post content.
805
- * @param object $post The full post object.
806
- */
807
- $contents = apply_filters( 'relevanssi_post_content', $post->post_content, $post );
808
- if ( $debug ) {
809
- relevanssi_debug_echo( "\tPost content after relevanssi_post_content:\n$contents" );
810
- }
811
- /**
812
- * Can be used to add extra content to the post before indexing.
813
- *
814
- * @author Alexander Gieg
815
- *
816
- * @param string The additional content.
817
- * @param object $post The post object.
818
- */
819
- $additional_content = trim( apply_filters( 'relevanssi_content_to_index', '', $post ) );
820
- if ( ! empty( $additional_content ) ) {
821
- $contents .= ' ' . $additional_content;
822
- if ( $debug ) {
823
- relevanssi_debug_echo( "\tAdditional content from relevanssi_content_to_index:\n$additional_content" );
824
- }
825
- }
826
-
827
- if ( 'on' === get_option( 'relevanssi_expand_shortcodes' ) ) {
828
- if ( function_exists( 'do_shortcode' ) ) {
829
- // WP Table Reloaded support.
830
- if ( defined( 'WP_TABLE_RELOADED_ABSPATH' ) ) {
831
- include_once WP_TABLE_RELOADED_ABSPATH . 'controllers/controller-frontend.php';
832
- $my_wp_table_reloaded = new WP_Table_Reloaded_Controller_Frontend();
833
- }
834
- // TablePress support.
835
- if ( defined( 'TABLEPRESS_ABSPATH' ) ) {
836
- if ( ! isset( TablePress::$model_options ) ) {
837
- include_once TABLEPRESS_ABSPATH . 'classes/class-model.php';
838
- include_once TABLEPRESS_ABSPATH . 'models/model-options.php';
839
- TablePress::$model_options = new TablePress_Options_Model();
840
- }
841
- $my_tablepress_controller = TablePress::load_controller( 'frontend' );
842
- $my_tablepress_controller->init_shortcodes();
843
- }
844
-
845
- $default_disables = array(
846
- 'contact-form', // Jetpack Contact Form causes an error message.
847
- 'starrater', // GD Star Rating rater shortcode causes problems.
848
- 'responsive-flipbook', // Responsive Flipbook causes problems.
849
- 'avatar_upload', // WP User Avatar is incompatible.
850
- 'product_categories', // A problematic WooCommerce shortcode.
851
- 'recent_products', // A problematic WooCommerce shortcode.
852
- 'php', // PHP Code for Posts.
853
- 'watupro', // Watu PRO doesn't co-operate.
854
- 'starbox', // Starbox shortcode breaks Relevanssi.
855
- 'cfdb-save-form-post', // Contact Form DB.
856
- 'cfdb-datatable',
857
- 'cfdb-table',
858
- 'cfdb-json',
859
- 'cfdb-value',
860
- 'cfdb-count',
861
- 'cfdb-html',
862
- 'woocommerce_cart', // WooCommerce.
863
- 'woocommerce_checkout',
864
- 'woocommerce_order_tracking',
865
- 'woocommerce_my_account',
866
- 'woocommerce_edit_account',
867
- 'woocommerce_change_password',
868
- 'woocommerce_view_order',
869
- 'woocommerce_logout',
870
- 'woocommerce_pay',
871
- 'woocommerce_thankyou',
872
- 'woocommerce_lost_password',
873
- 'woocommerce_edit_address',
874
- 'tc_process_payment',
875
- 'maxmegamenu', // Max Mega Menu.
876
- 'searchandfilter', // Search and Filter.
877
- 'downloads', // Easy Digital Downloads.
878
- 'download_history',
879
- 'purchase_history',
880
- 'download_checkout',
881
- 'purchase_link',
882
- 'download_cart',
883
- 'edd_profile_editor',
884
- 'edd_login',
885
- 'edd_register',
886
- 'swpm_protected', // Simple Membership Partially Protected content.
887
- 'gravityform', // Gravity Forms.
888
- 'sdm_latest_downloads', // SDM Simple Download Monitor.
889
- 'slimstat', // Slimstat Analytics.
890
- );
891
-
892
- $disable_shortcodes = get_option( 'relevanssi_disable_shortcodes' );
893
- $shortcodes = explode( ',', $disable_shortcodes );
894
- $shortcodes = array_unique( array_merge( $shortcodes, $default_disables ) );
895
- foreach ( $shortcodes as $shortcode ) {
896
- if ( empty( $shortcode ) ) {
897
- continue;
898
- }
899
- remove_shortcode( trim( $shortcode ) );
900
- add_shortcode( $shortcode, '__return_empty_string' );
901
- }
902
-
903
- $post_before_shortcode = $post;
904
- $contents = do_shortcode( $contents );
905
- $post = $post_before_shortcode; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
906
-
907
- if ( defined( 'TABLEPRESS_ABSPATH' ) ) {
908
- unset( $my_tablepress_controller );
909
- }
910
- if ( defined( 'WP_TABLE_RELOADED_ABSPATH' ) ) {
911
- unset( $my_wp_table_reloaded );
912
- }
913
- }
914
- } else {
915
- $contents = strip_shortcodes( $contents );
916
- }
917
-
918
- remove_shortcode( 'noindex' );
919
- add_shortcode( 'noindex', 'relevanssi_noindex_shortcode' );
920
-
921
- $contents = relevanssi_strip_invisibles( $contents );
922
-
923
- // Premium feature for better control over internal links.
924
- if ( function_exists( 'relevanssi_process_internal_links' ) ) {
925
- $contents = relevanssi_process_internal_links( $contents, $post->ID );
926
- }
927
-
928
- $contents = preg_replace( '/<[a-zA-Z\/][^>]*>/', ' ', $contents );
929
- $contents = wp_strip_all_tags( $contents );
930
- if ( function_exists( 'wp_encode_emoji' ) ) {
931
- $contents = wp_encode_emoji( $contents );
932
- }
933
- /**
934
- * Filters the post content in indexing before tokenization.
935
- *
936
- * @param string $contents The post content.
937
- * @param object $post The full post object.
938
- */
939
- $contents = apply_filters( 'relevanssi_post_content_before_tokenize', $contents, $post );
940
- /** This filter is documented in common/indexing.php */
941
- $content_tokens = apply_filters( 'relevanssi_indexing_tokens', relevanssi_tokenize( $contents, 'body', $min_word_length ), 'content' );
942
- if ( $debug ) {
943
- relevanssi_debug_echo( "\tContent, tokenized:\n" . implode( ' ', array_keys( $content_tokens ) ) );
944
- }
945
-
946
- if ( count( $content_tokens ) > 0 ) {
947
- foreach ( $content_tokens as $token => $count ) {
948
- $n++;
949
- if ( ! isset( $insert_data[ $token ]['content'] ) ) {
950
- $insert_data[ $token ]['content'] = 0;
951
- }
952
- $insert_data[ $token ]['content'] += $count;
953
- }
954
- }
955
- }
956
-
957
- /**
958
- * Sets the indexed post 'type' column in the index.
959
- *
960
- * Default value is 'post', but other common values include 'attachment',
961
- * 'user' and taxonomy name.
962
- *
963
- * @param string Type value.
964
- * @param object The post object for the current post.
965
- */
966
- $type = apply_filters( 'relevanssi_index_get_post_type', 'post', $post );
967
-
968
- /**
969
- * Filters the indexing data before it is converted to INSERT queries.
970
- *
971
- * @param array $insert_data All the tokens and their counts.
972
- * @param object $post The post object.
973
- */
974
- $insert_data = apply_filters( 'relevanssi_indexing_data', $insert_data, $post );
975
-
976
- $values = array();
977
- foreach ( $insert_data as $term => $data ) {
978
- $fields = array( 'content', 'title', 'comment', 'tag', 'link', 'author', 'category', 'excerpt', 'taxonomy', 'customfield', 'mysqlcolumn' );
979
- foreach ( $fields as $field ) {
980
- if ( ! isset( $data[ $field ] ) ) {
981
- $data[ $field ] = 0;
982
- }
983
- }
984
- if ( ! isset( $data['taxonomy_detail'] ) ) {
985
- $data['taxonomy_detail'] = '';
986
- }
987
- if ( ! isset( $data['customfield_detail'] ) ) {
988
- $data['customfield_detail'] = '';
989
- }
990
- $content = $data['content'];
991
- $title = $data['title'];
992
- $comment = $data['comment'];
993
- $tag = $data['tag'];
994
- $link = $data['link'];
995
- $author = $data['author'];
996
- $category = $data['category'];
997
- $excerpt = $data['excerpt'];
998
- $taxonomy = $data['taxonomy'];
999
- $customfield = $data['customfield'];
1000
- $mysqlcolumn = $data['mysqlcolumn'];
1001
- $taxonomy_detail = $data['taxonomy_detail'];
1002
- $customfield_detail = $data['customfield_detail'];
1003
-
1004
- $term = trim( $term );
1005
-
1006
- $value = $wpdb->prepare(
1007
- '(%d, %s, REVERSE(%s), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %d)',
1008
- $post->ID,
1009
- $term,
1010
- $term,
1011
- $content,
1012
- $title,
1013
- $comment,
1014
- $tag,
1015
- $link,
1016
- $author,
1017
- $category,
1018
- $excerpt,
1019
- $taxonomy,
1020
- $customfield,
1021
- $type,
1022
- $taxonomy_detail,
1023
- $customfield_detail,
1024
- $mysqlcolumn
1025
- );
1026
 
1027
- array_push( $values, $value );
1028
- }
1029
-
1030
- /**
1031
- * Filters the INSERT query VALUES sections before they are inserted in the INSERT query.
1032
- *
1033
- * @param array $values Value sets.
1034
- * @param object $post The post object.
1035
- */
1036
- $values = apply_filters( 'relevanssi_indexing_values', $values, $post );
1037
 
1038
  if ( ! empty( $values ) ) {
1039
  $values = implode( ', ', $values );
@@ -1044,10 +524,7 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
1044
  $wpdb->query( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1045
  }
1046
 
1047
- if ( $post_was_null ) {
1048
- $post = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1049
- }
1050
- if ( $previous_post ) {
1051
  $post = $previous_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1052
  }
1053
 
@@ -1059,36 +536,48 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
1059
  *
1060
  * @since 1.8
1061
  *
1062
- * @param object $post Post object, default null.
1063
- * @param string $taxonomy Taxonomy name, default empty string.
1064
- * @param array $insert_data Insert query data array.
 
1065
  *
1066
- * @return array Updated insert query data array.
1067
  */
1068
- function relevanssi_index_taxonomy_terms( $post = null, $taxonomy = '', $insert_data ) {
1069
- $n = 0;
1070
-
1071
- if ( null === $post || empty( $taxonomy ) ) {
1072
- return $insert_data;
1073
  }
1074
 
 
 
1075
  $min_word_length = get_option( 'relevanssi_min_word_length', 3 );
1076
- $post_taxonomy_terms = get_the_terms( $post->ID, $taxonomy );
1077
 
1078
  if ( false === $post_taxonomy_terms ) {
1079
- return $insert_data;
1080
  }
1081
 
1082
- $tag_string = '';
1083
- foreach ( $post_taxonomy_terms as $post_tag ) {
1084
- if ( is_object( $post_tag ) ) {
1085
- $tag_string .= $post_tag->name . ' ';
1086
  }
1087
  }
1088
- $tag_string = apply_filters( 'relevanssi_tag_before_tokenize', trim( $tag_string ) );
1089
- $tag_tokens = relevanssi_tokenize( $tag_string, true, $min_word_length );
1090
- if ( count( $tag_tokens ) > 0 ) {
1091
- foreach ( $tag_tokens as $token => $count ) {
 
 
 
 
 
 
 
 
 
 
 
1092
  $n++;
1093
 
1094
  switch ( $taxonomy ) {
@@ -1116,7 +605,8 @@ function relevanssi_index_taxonomy_terms( $post = null, $taxonomy = '', $insert_
1116
  $insert_data[ $token ]['taxonomy_detail'] = wp_json_encode( $tax_detail );
1117
  }
1118
  }
1119
- return $insert_data;
 
1120
  }
1121
 
1122
  /**
@@ -1514,3 +1004,642 @@ function relevanssi_index_get_post_type( $type, $post ) {
1514
  }
1515
  return $type;
1516
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  OR (post.post_parent=0)
127
  )
128
  ))
129
+ AND post.ID NOT IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_hide_post' AND meta_value = 'on')
130
  $restriction ORDER BY post.ID DESC $limit";
131
  } else {
132
  $q = "SELECT post.ID
145
  )
146
  )
147
  )
148
+ AND post.ID NOT IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_hide_post' AND meta_value = 'on')
149
  $restriction ORDER BY post.ID DESC $limit";
150
  }
151
 
235
  * @param boolean|int $extend_offset If numeric, offsets the indexing by that
236
  * amount. If true, doesn't truncate the index before indexing. If false, truncates
237
  * index before indexing. Default false.
238
+ * @param boolean $verbose Not used anymore, kept for backwards compatibility.
239
  * @param int $post_limit How many posts to index. Default null, no limit.
240
  * @param boolean $is_ajax If true, indexing is done in AJAX context.
241
  * Default false.
245
  * posts indexed. Outside AJAX context, these values are returned as an array in
246
  * format of array(completed, posts indexed).
247
  */
248
+ function relevanssi_build_index( $extend_offset = false, $verbose = null, $post_limit = null, $is_ajax = false ) {
249
  global $wpdb, $relevanssi_variables;
250
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
251
 
252
  // Thanks to Julien Mession. This speeds up indexing a lot.
253
  wp_suspend_cache_addition( true );
254
 
 
 
 
 
 
 
 
255
  if ( false === $extend_offset ) {
256
  // Truncate the index first.
257
  relevanssi_truncate_index();
258
 
259
+ // Taxonomy term, user profile and post type archive indexing.
260
+ if ( function_exists( 'relevanssi_premium_indexing' ) ) {
261
+ relevanssi_premium_indexing();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
 
 
 
 
 
 
 
 
 
 
264
  update_option( 'relevanssi_index', '' );
265
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
+ $indexing_query_args = relevanssi_indexing_query_args( $extend_offset, $post_limit );
 
 
 
 
 
268
 
269
+ // The values generated by these functions are safe to use for MySQL.
270
+ $restriction = relevanssi_post_type_restriction();
271
+ $valid_status = relevanssi_valid_status_array();
 
272
 
273
+ $query = relevanssi_generate_indexing_query(
274
+ $valid_status,
275
+ $indexing_query_args['extend'],
276
+ $restriction,
277
+ $indexing_query_args['limit']
278
+ );
279
 
280
  /* This action documented earlier in lib/indexing.php. */
281
  do_action( 'relevanssi_pre_indexing_query' );
 
282
 
283
+ $content = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
284
  if ( defined( 'WP_CLI' ) && WP_CLI && function_exists( 'relevanssi_generate_progress_bar' ) ) {
285
+ // @codeCoverageIgnoreStart
286
  $progress = relevanssi_generate_progress_bar( 'Indexing posts', count( $content ) );
287
+ // @codeCoverageIgnoreEnd
288
  }
289
 
290
+ $custom_fields = relevanssi_get_custom_fields();
291
+
292
+ $n = 0;
293
  $remove_first = false;
294
  $bypass_global_post = true; // $bypassglobalpost set to true, because at this
295
  // point global $post should be null, but in some cases it is not.
300
  $n++;
301
  }
302
  if ( defined( 'WP_CLI' ) && WP_CLI && $progress ) {
303
+ // @codeCoverageIgnoreStart
304
  $progress->tick();
305
+ // @codeCoverageIgnoreEnd
306
  }
307
  }
308
  if ( defined( 'WP_CLI' ) && WP_CLI && $progress ) {
309
+ // @codeCoverageIgnoreStart
310
  $progress->finish();
311
+ // @codeCoverageIgnoreEnd
312
  }
313
 
314
  // To prevent empty indices.
315
  $wpdb->query( "ANALYZE TABLE $relevanssi_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
316
 
317
  $complete = false;
318
+ $size = $indexing_query_args['size'];
319
+
320
  if ( ( 0 === $size ) || ( count( $content ) < $size ) ) {
 
321
  $complete = true;
322
+ update_option( 'relevanssi_indexed', 'done' );
 
 
 
 
323
  }
324
 
 
 
325
  // Update the document count variable.
326
  relevanssi_update_doc_count();
327
 
362
  * @param boolean $remove_first If true, remove the post from the index
363
  * before indexing. Default false.
364
  * @param array $custom_fields The custom fields that are indexed for the
365
+ * post. Default an empty string.
366
  * @param boolean $bypass_global_post If true, do not use the global $post object.
367
  * Default false.
368
  * @param boolean $debug If true, echo out debugging information.
369
  * Default false.
370
  */
371
+ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_fields = '', $bypass_global_post = false, $debug = false ) {
372
  global $wpdb, $post, $relevanssi_variables;
373
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
374
+ $post_was_null = true;
375
  $previous_post = null;
376
 
377
  // Check if this is a Jetpack Contact Form entry.
378
  if ( isset( $_REQUEST['contact-form-id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
379
+ return -1;
380
  }
381
 
382
+ if ( isset( $post ) ) {
383
+ $post_was_null = false;
384
+ $previous_post = $post;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  }
386
 
387
+ if ( empty( $post ) || $bypass_global_post ) {
388
+ $post = is_object( $index_post ) ? $index_post : get_post( $index_post ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
 
 
 
 
 
 
 
389
  }
390
 
391
  // Finally fetch the post again by ID. Complicated, yes, but unless we do this,
392
  // we might end up indexing the post before the updates come in.
393
+ $post = isset( $post->ID ) ? get_post( $post->ID ) : null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
394
 
395
  if ( null === $post ) {
396
+ // At this point we should have something in $post; if not, quit.
397
+ if ( $previous_post || $post_was_null ) {
398
+ $post = $previous_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
399
+ }
400
  return -1;
401
  }
402
 
406
  if ( $debug ) {
407
  relevanssi_debug_echo( 'relevanssi_hide_post() returned true.' );
408
  }
409
+ if ( $previous_post || $post_was_null ) {
 
 
 
410
  $post = $previous_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
411
  }
412
  return 'hide';
416
  $index_this_post = false;
417
  $post->indexing_content = true;
418
 
419
+ $index_types = get_option( 'relevanssi_index_post_types', array() );
420
+ if ( is_array( $index_types ) && in_array( $post->post_type, $index_types, true ) ) {
 
 
 
421
  $index_this_post = true;
422
  }
423
 
451
  // This needs to be here, after the call to relevanssi_remove_doc(), because
452
  // otherwise a post that's in the index but shouldn't be there won't get removed.
453
  if ( ! $index_this_post ) {
454
+ if ( $previous_post || $post_was_null ) {
 
 
 
455
  $post = $previous_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
456
  }
457
  return 'donotindex';
472
  $insert_data = array();
473
 
474
  if ( 'none' !== get_option( 'relevanssi_index_comments' ) ) {
475
+ $n += relevanssi_index_comments( $insert_data, $post->ID, $min_word_length, $debug );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  }
477
 
 
478
  $taxonomies = get_option( 'relevanssi_index_taxonomies_list', array() );
479
  foreach ( $taxonomies as $taxonomy ) {
480
+ $n += relevanssi_index_taxonomy_terms( $insert_data, $post->ID, $taxonomy, $debug );
 
 
 
481
  }
482
 
 
483
  if ( 'on' === get_option( 'relevanssi_index_author' ) ) {
484
+ $n += relevanssi_index_author( $insert_data, $post->post_author, $min_word_length, $debug );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
 
487
+ $n += relevanssi_index_custom_fields( $insert_data, $post->ID, $custom_fields, $min_word_length, $debug );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
 
489
+ if (
490
+ isset( $post->post_excerpt )
491
+ && ( 'on' === get_option( 'relevanssi_index_excerpt' ) || 'attachment' === $post->post_type )
492
+ ) {
493
+ // Attachment caption is stored in the excerpt.
494
+ $n += relevanssi_index_excerpt( $insert_data, $post->post_excerpt, $min_word_length, $debug );
 
 
 
 
 
 
 
 
495
  }
496
 
497
  // Premium can index arbitrary MySQL columns.
510
  $insert_data = relevanssi_index_pdf_for_parent( $insert_data, $post->ID );
511
  }
512
 
513
+ $n += relevanssi_index_title( $insert_data, $post, $min_word_length, $debug );
514
+ $n += relevanssi_index_content( $insert_data, $post, $min_word_length, $debug );