Relevanssi – A Better Search - Version 4.0.5

Version Description

  • Relevanssi code has been reviewed and modified to follow WordPress coding standards. As a result, there have been minor improvements all around the code to make things more robust and secure.
  • Custom field detail is no longer serialized. It's now JSON. If you use custom field detail, rebuild the index and change your code to use json_decode() instead of unserialize().
  • relevanssi_the_tags() and relevanssi_get_the_tags() now have different set of parameters, more in line with the_tags() and get_the_tags().
  • Taxonomy indexing settings were emptied out if you saved another options tab. That is now fixed.
  • Improvements to WPML support; WPML is now less likely to be confused in multisite searches.
  • Updated filter: relevanssi_search_ok now gets the WP_Query object as a parameter, which is helpful if you're not using the global $wp_query.
  • ACF Flexible Content field indexing didn't work properly, possibly due to a change in ACF. That should now work better.

=

Download this release

Release Info

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

Code changes from version 4.0.4 to 4.0.5

changelog.txt ADDED
@@ -0,0 +1,873 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ = 4.0.4 =
2
+ * Fixed shortcode: `searchform` shortcode didn't work properly.
3
+ * Setting post type or post content weight to 0 didn't work.
4
+ * Gravity Forms shortcode is now disabled in Relevanssi indexing.
5
+ * New filter: `relevanssi_excerpt_query` filters the search query before building excerpts.
6
+ * HTML tags are stripped from post excerpts when using the excerpt to build Relevanssi excerpts.
7
+ * Fixed filter: `relevanssi_custom_field_value` didn't have the correct post ID parameter.
8
+ * `relevanssi_get_permalink` doesn't add the `highlight` parameter to URLs outside search results pages anymore, and won't add the parameter to front page links either.
9
+ * Relevanssi used `sanitize_hex_color`, which isn't actually reliably available.
10
+ * Did you mean suggestions have been improved.
11
+ * Single-word phrases are not allowed anymore, as they do no good. They are silently converted to non-phrases now.
12
+
13
+ = 4.0.3 =
14
+ * Relevanssi didn't index all posts with one go. It does now.
15
+ * © and ® symbols caused problems in indexing; they are now included in the default punctuation removal.
16
+ * In some cases excerpt-building could take ages because of autoembed link discovery. Relevanssi now blocks the autoembed procedure in link-building.
17
+ * New filter: `relevanssi_custom_field_value` is used to filter custom field values both before indexing and before excerpt-building. Parameters include the field name and the post ID.
18
+ * Updated filter: `relevanssi_index_custom_fields` now gets a second parameter that contains the post ID.
19
+
20
+ = 4.0.2 =
21
+ * Removed couple of error notices in the code.
22
+ * Improved the support for page builders.
23
+ * Improvements to the Polylang setting.
24
+
25
+ = 4.0.1 =
26
+ * The plugin can now be uninstalled.
27
+
28
+ = 4.0 =
29
+ * Legacy code has been removed. If you have a version older than 3.6, update first to 3.6.2.2 to guarantee smooth upgrade process.
30
+ * Improved indexing: no more clicking "Continue indexing" again and again!
31
+ * Settings pages have been completely rewritten.
32
+ * There's documentation in the WordPress contextual help: just click Help on the top right corner.
33
+ * Better Polylang support. A new option to remove the Polylang language filter.
34
+ * Logs can be automatically trimmed. Old log entries are removed to save space.
35
+ * Finally a setting to adjust content weight!
36
+ * Excerpts can use the custom field content.
37
+ * Highlighting in documents is changed: it now requires a `highlight` query parameter. This helps getting pass caching and makes the highlighting more reliable. To get the query parameter active, use `relevanssi_get_permalink()` to print out the permalinks on the search results templates.
38
+ * Relevanssi added synonyms to highlighting even if synonyms were not used for searching. In some cases, synonyms were added twice.
39
+ * The User Searches page got a makeover, too.
40
+ * Relevanssi is automatically disabled in REST API searches.
41
+ * Groups and Simple Membership support has been improved.
42
+ * Sorting search results is now up to 300 times faster than before.
43
+ * Lots of improvements all over the place.
44
+ * New filter: `relevanssi_excerpt_custom_field_content` lets you modify custom field content that is used for excerpts.
45
+ * New filter: `relevanssi_punctuation_filter` allows for easy modification of punctuation handling.
46
+ * New filter: `relevanssi_default_punctuation_replacement` changes the default way to handle the rest of the punctuation.
47
+ * New filter: `relevanssi_search_again` lets you run the search again if no results are found and to modify the parameters between search runs.
48
+ * New filter: `relevanssi_fallback` allows you to do fallback searches.
49
+ * New filter: `relevanssi_page_builder_shortcodes` lets you control which page builder shortcodes Relevanssi removes before building the excerpts.
50
+ * New filter: `relevanssi_optimize_excerpts` makes excerpt-building faster, if you make the filter return `true`.
51
+
52
+ = 3.6.2.2 =
53
+ * A bug in post sorting broke string sorting (mostly post title sorting).
54
+
55
+ = 3.6.2.1 =
56
+ * A bug was left in the post ordering code. That bug is now squashed.
57
+
58
+ = 3.6.2 =
59
+ * Simple Membership plugin is now supported automatically to restrict access to posts.
60
+ * Relevanssi can now handle orderby parameter in array format.
61
+ * Relevanssi now blocks Easy Digital Downloads shortcodes when indexing to improve compatibility with EDD.
62
+ * When using `fields` to only fetch post IDs, Relevanssi doesn't try to highlight post titles.
63
+ * New action: `relevanssi_update_options` lets you adjust Relevanssi options immediately after the defaults are set.
64
+ * Remove notices about duplicated database columns when installing the plugin.
65
+
66
+ = 3.6.1 =
67
+ * SECURITY: This version fixes a SQL injection vulnerability, where a site admin could theoretically inject SQL code into Relevanssi search queries. Doing this required access to Relevanssi settings page and in my tests, I couldn't do any damage, just break the Relevanssi search, but in any case, this vulnerability is now fixed.
68
+ * Search and Filter shortcode is added to the blacklist.
69
+ * Groups plugin is now supported automatically to restrict access to posts.
70
+ * The filter `relevanssi_index_custom_fields` now works even if the custom field setting is empty.
71
+ * The filter `relevanssi_post_to_index` now has a second parameter. For posts, it simply repeats the post object, but for taxonomy terms, it has the term object.
72
+
73
+ = 3.6.0 =
74
+ * Changed a bit how Relevanssi attaches itself to queries. Instead of the global $wp_query, Relevanssi now uses the query passed as the parameter to `the_posts` filter hook. This should improve compatibility in some cases, but may cause problems in some fringe cases. If you're doing something unusual with Relevanssi, try this out before deploying to public use.
75
+ * Some meta queries caused major problems with the Relevanssi weighting algorithm. This has now been fixed.
76
+ * Error notices caused by trying to use a non-existing taxonomy term have been removed.
77
+
78
+ = 3.5.12 =
79
+ * Post type exclusion didn't work as expected.
80
+ * Relevanssi couldn't handle nested tax queries (such as those generated by WooCommerce product visibility filtering) properly.
81
+
82
+ = 3.5.11.1 =
83
+ * New filter: `relevanssi_allow_one_letter_highlights` lets you allow one-letter highlights. Just make the filter function return `true`.
84
+ * New filter: `relevanssi_block_one_letter_searches` by default blocks one-letter searches. If you want to enable them, add a filter function that always returns `false`.
85
+ * Fixed an undefined variable notice.
86
+
87
+ = 3.5.11 =
88
+ * Synonym indexing failed if synonyms contained a forward slash.
89
+ * Highlighting HTML tags has been improved further.
90
+ * New filter: `relevanssi_tag_before_tokenize` allows you to access tag content before indexing.
91
+ * Relevanssi now actively blocks one-letter search terms, as they are generally pointless and can cause "out of memory" issues. One-letter search terms are no longer highlighted, either. These are usually caused by cases like "word's" being interpreted as "word s".
92
+ * New filter: `relevanssi_disable_shortcodes_excerpt` lets you add more shortcodes to be disabled before excerpts are built.
93
+
94
+ = 3.5.10 =
95
+ * Some users got a fatal parse error. That shouldn't happen anymore.
96
+ * FacetWP users ran into trouble, as `relevanssi_do_query()` started to explicitly expect a WP_Query object in version 1.15.0. That expectation is removed; it's still highly recommended for future compatibility that you use WP_Query objects.
97
+ * Small bug fix: `get_current_screen()` is now only used when it's available to avoid occasional fatal errors.
98
+ * Error messages from `DOING_AJAX` being undefined should be removed.
99
+
100
+ = 3.5.9.1 =
101
+ * WP.org plugin repo didn't like 3.5.9 for some reason, hoping to have better luck with this.
102
+
103
+ = 3.5.9 =
104
+ * Improved the way highlighting handles HTML tags, especially when highlighting on post pages.
105
+ * The throttle limit setting was removed from the settings page for causing trouble. If you need to change it, update `relevanssi_throttle_limit` option directly.
106
+ * Relevanssi didn't support tax_queries with `field` set to `name`. That works now.
107
+ * Much faster way of showing the 25 most common words in the index. If you've disabled this feature because it was so slow, try enabling it - you might be surprised!
108
+
109
+ = 3.5.8 =
110
+ * Did you mean function had a XSS vulnerability, which is now removed.
111
+ * Minimum word length wasn't applied to titles in indexing. It is now fixed. If you think this is a problem, rebuild the index.
112
+ * TablePress compatibility has been improved.
113
+ * Meta query handling has been improved, thanks to Maxime Culea.
114
+ * Improved WP_Query parameter support: setting query variable `sentence` to 1 forces phrase search.
115
+ * Improved ACF compatibility.
116
+
117
+ = 3.5.7.1 =
118
+ * Small fix for a bug that broke the settings page.
119
+
120
+ = 3.5.7 =
121
+ * An improved version of the sorting function will not throw notices when Intuitive Custom Post Order plugin is used.
122
+ * New filter: `relevanssi_missing_sort_key` can be used to adjust the result sorting when using keys that are not present in all posts (eg. menu_order).
123
+ * Czech translation and stopwords, thanks to Michael Kucera.
124
+ * Relevanssi broke the WP admin menu search when admin searches were enabled.
125
+ * Relevanssi broke the admin page search under WP 4.7. Relevanssi is now disabled in admin page searches.
126
+ * The way accented characters are handled in highlighting is improved. A new filter, `relevanssi_accents_replacement_arrays`, can be used to adjust the accent replacement.
127
+
128
+ = 3.5.6.1 =
129
+ * Fix for a fatal bug in 3.5.6.
130
+
131
+ = 3.5.6 =
132
+ * Relevanssi admin page had a vulnerability that allowed SQL injection attacks. That is now fixed.
133
+ * Relevanssi didn't like to highlight search terms that are followed by a ?, an ! or an apostrophe.
134
+ * New filter: `relevanssi_ok_to_log` lets you control whether search queries are logged or not.
135
+
136
+ = 3.5.5 =
137
+ * 500 errors caused by arrays in custom fields should be gone now.
138
+ * Improvements to the ACF "select" field support.
139
+ * Relevanssi will not break when frontend plugins insert posts.
140
+ * `relevanssi_match` filter has a new parameter, which contains the search term.
141
+ * Polylang support has been improved.
142
+ * WPML and Polylang filters work when "fields" is set to "ids".
143
+ * New filter: `relevanssi_log_get_user` gets passed the user object before Relevanssi decides if the query should be logged or not.
144
+
145
+ = 3.5.4 =
146
+ * Relevanssi had a bug that lead to inflated relevancy scores for posts.
147
+ * Relevanssi can now index the human-readable labels of ACF "select" fields. (Thanks to Raphaël Droz.)
148
+ * New filter: `relevanssi_30days` can be used to adjust the 30 day logs to a different number of days.
149
+ * Adding stopwords that contain apostrophes didn't work.
150
+ * Ensured PHP7 and WP 4.6 compatibility.
151
+ * Fixed a small glitch that could happen if a highlighted term is next to a starting square bracket.
152
+
153
+ = 3.5.3 =
154
+ * New filter `relevanssi_user_searches_limit` to adjust the number of user searches shown in the logs.
155
+ * Old data check is only done on Relevanssi settings page, not on all admin pages. That should improve admin performance.
156
+ * Fixed a fatal error when searching includes private posts.
157
+ * New filter: `relevanssi_remote_addr` can be used to modify the IP address logged to Relevanssi logs.
158
+ * Blocked CFDB and WooCommerce shortcodes that are causing problems with Relevanssi.
159
+
160
+ = 3.5.2 =
161
+ * Added correct support for `term_taxonomy_id` in the `fields` parameter in tax_queries.
162
+
163
+ = 3.5.1 =
164
+ * Fixed an error in the Did you mean function.
165
+ * Fixed an error if the search term was not found in content.
166
+ * Fixed an error when building excerpts from posts shorter than the excerpt length.
167
+ * Blocked the `[starpro]` shortcode that was causing problems with Relevanssi.
168
+ * New filter: `relevanssi_remove_stopwords_in_titles` allows you to include stopwords in titles.
169
+ * Added support for `term_tax_id` in the `fields` parameter in tax_queries.
170
+ * Excerpt-building failed if multibyte string operations were missing. It should work now.
171
+
172
+ = 3.5 =
173
+ * Tokenizer was using `strlen()` and not `mb_strlen()`, so word lengths were not calculated properly. If your site uses non-ASCII alphabet, rebuilding the index is a good idea.
174
+ * Small improvement to WPML multilanguage filtering.
175
+ * `relevanssi_the_title()` got a new parameter: if you don't want to echo the title, you can use it like `relevanssi_the_title(false)` to make it return the title.
176
+ * Relevanssi had `the_title` filter hook calls that were missing the second parameter; that's now fixed.
177
+ * The excerpt-building algorithm is completely rewritten based on work by Ben Boyter (http://www.boyter.org/).
178
+ * The `[watupro]` shortcode didn't work with Relevanssi, so Relevanssi will now bypass it.
179
+ * The plugin i18n features have been improved slightly.
180
+ * New filter: `relevanssi_didyoumean_suggestion` lets you modify the Did you mean? suggestion before it's displayed.
181
+ * `relevanssi_didyoumean()` has a new parameter: you can now choose whether the result is echoed out (the default value) or just returned.
182
+ * In the search results breakdown, you can now use %categories% and %taxonomies% to show the number of matches in categories and taxonomies other than tags and cats, respectively.
183
+ * Relevanssi supports `fields` parameter (both `ids` and `id=>parent`) to return only post IDs or post IDs and post parents.
184
+
185
+ = 3.4.2 =
186
+ * Empty lines on synonym settings caused problems. Fixed that.
187
+ * In WordPress 4.2 installations, emoji in will be handled better. Emoji in posts may cause problems with WordPress versions below 4.2, so please update!
188
+
189
+ = 3.4.1 =
190
+ * Removed a notice about an undefined variable.
191
+
192
+ = 3.4 =
193
+ * New filter: `relevanssi_valid_admin_status` can be used to adjust post statuses that Relevanssi will handle.
194
+ * If Relevanssi creates an empty excerpt for a post and there's a user-set excerpt for the post, that excerpt is used.
195
+ * No ellipsis is added to the post excerpt, if the post excerpt shows the whole post.
196
+ * The `relevanssi_post_title_before_tokenize` filter now has a second parameter that contains the post object.
197
+ * New filter: `relevanssi_display_common_words` can be used to disable the "25 most common words" listing on the settings page, if it's too heavy to load.
198
+ * Relevanssi was sanitizing taxonomy titles too aggressively. That is now toned down a bit.
199
+ * Relevanssi now supports `post_parent`, `post_parent__in` and `post_parent__not_in`, though you have to set them in `relevanssi_modify_wp_query` filter for them to work.
200
+ * Meta query support should now be perfect; there were some limitations with complicated meta queries before.
201
+
202
+ = 3.3.8 =
203
+ * Fixed a bug that caused the results to change depending of the order of words in a multi-word search query.
204
+ * Added `product_categories` and `recent_products` from WooCommerce to the list of blocked shortcodes.
205
+ * There are improvements in excerpt-building and highlighting, especially when fuzzy search is enabled.
206
+ * Fixed a possible (if quite unlikely) XSS vulnerability.
207
+ * Improved search performance (thanks to MikeNGarrett).
208
+ * Sometimes highlights in documents make the document content disappear. I don't know why, but I've added a fix that should make the content visible (without the highlights) if a problem appears.
209
+
210
+ = 3.3.7.1 =
211
+ * Fixed bbPress compatibility.
212
+
213
+ = 3.3.7 =
214
+ * Fixed bbPress compatibility.
215
+
216
+ = 3.3.6 =
217
+ * Relevanssi handles taxonomy terms in search better. The change requires a reindexing.
218
+ * Fix in indexing: Relevanssi will now bypass the global $post when indexing. This should help with problems with the Cookie Law Info plugin, for example.
219
+ * Tax query relation setting didn't work properly. It is now fixed.
220
+ * Word-based excerpt building sometimes created too short excerpts. That is now fixed.
221
+ * Synonyms are now highlighted.
222
+ * Phrase matching had issues where searching for a too common phrase crashed the search. That has been fixed.
223
+ * LIKE operator didn't work properly in meta_queries.
224
+ * Problems with Avatar Upload plugin are fixed.
225
+ * Offset errors with mb_stripos() shouldn't happen anymore.
226
+ * A small problem in taxonomy search MySQL fixed, also a small problem with AND operator in tax_queries.
227
+ * New filter: `relevanssi_post_to_index` lets you access the post object before the post is indexed.
228
+ * New filter: `relevanssi_orderby` lets you modify the $orderby value before Relevanssi sorts posts.
229
+ * New filter: `relevanssi_order` lets you modify the $order value before Relevanssi sorts posts.
230
+ * New filter: `relevanssi_post_title_before_tokenize` lets you modify post titles before indexing.
231
+ * New filter: `relevanssi_private_cap` lets you adjust the capability setting for private posts in custom post types.
232
+ * Deprecated use of `like_escape` has been fixed.
233
+
234
+ = 3.3.5 =
235
+ * Fixed a bug where excluding posts would cause the search to fail.
236
+ * Fixed a bug causing duplicate search results in WPML searches.
237
+ * Increased plugin safety against hackers.
238
+ * There was a bug in `relevanssi_comment_content_to_index` filter.
239
+ * Some people had problems with the log entry timestamps. Fixed that.
240
+ * New filter: `relevanssi_prevent_default_request` gives you more control over where Relevanssi prevents the default query from running.
241
+ * New filter: `relevanssi_private_cap` lets you set the correct capability for finding private posts in custom post types.
242
+ * The option to exclude categories and tags from search only worked for categories, not tags. Tags have been separated to a different option.
243
+
244
+ = 3.3.4 =
245
+ * Couple of bug fixes.
246
+
247
+ = 3.3.3 =
248
+ * OR fallback had problems.
249
+ * Indexing sub pages didn't work.
250
+ * Relevanssi now automatically treats 'ß' as 'ss'. If your site has 'ß' in text, reindexing the database is a good idea.
251
+ * Query variable `post_status` is now supported.
252
+
253
+ = 3.3.2 =
254
+ * Fixed a warning on search results page.
255
+
256
+ = 3.3.1 =
257
+ * Fixed bugs related to the removal of the cache feature.
258
+
259
+ = 3.3 =
260
+ * Improvements to excerpts: excerpts with phrases work much better now, and the excerpt creation logic has been improved: the excerpts are now better. The process takes a bit more time, though.
261
+ * Allowing HTML tags in excerpts could lead to those tags being left open. Relevanssi will now try to close open HTML tags in excerpts.
262
+ * Allowed tags were not controlled in comments. They are now.
263
+ * Highlighting in documents didn't always work; it should be more reliable now.
264
+ * Non-integer values are removed from `post__in` and `post__not_in` before processing them.
265
+ * Query variables `p` and `page_id` are now supported.
266
+ * Relevanssi now understands `date_query` variables as well.
267
+ * The original post excerpt is stored in $post->original_excerpt.
268
+ * Taxonomy search works better with term id parameters (for example from `wp_category_dropdown`).
269
+ * Errors about $wpdb->prepare() missing an argument removed.
270
+ * New functions: `relevanssi_the_title()` and `relevanssi_get_the_title()` can be used to display highlighted titles in search results.
271
+ * The old title highlighting method has been disabled, because it caused highlights in wrong places. Now the highlighted title is stored in $post->highlighted_post_title, take it from there or use the Relevanssi title functions to display it.
272
+ * Polylang and WPML support was adjusted to perform better in edge cases.
273
+ * Indexing is faster, thanks to some improved code from Tom Novelli.
274
+ * MySQL injection attack vulnerability removed.
275
+ * The cache feature is now removed. Relevanssi should automatically drop the cache tables.
276
+ * New filter: `relevanssi_indexing_data` lets you modify the data before it's indexed.
277
+
278
+ = 3.2 =
279
+ * Fixed a bug in the TablePress support.
280
+ * Titles are put through the_title filter before indexing.
281
+ * New filter: `relevanssi_join` can be used to join tables in the Relevanssi search MySQL queries. Thanks to Ninos Ego.
282
+ * New filter: `relevanssi_post_content` can be used to modify post content before any Relevanssi processing.
283
+ * New filter: `relevanssi_post_content_before_tokenize` can be used to modify post content just before it's tokenized.
284
+ * New filter: `relevanssi_indexing_values` can be used to modify what Relevanssi stores in the index.
285
+ * New filter: `relevanssi_default_meta_query_relation` can be used to change the default meta query relation (default value is "AND").
286
+ * When using a meta_query, `relation` can be set to OR now.
287
+ * Phrases are now matched to excerpts.
288
+ * Number of queries Relevanssi generates is much, much lower.
289
+ * New filter: `relevanssi_didyoumean_url` lets you modify the URL generated by the did you mean feature.
290
+ * Better set of Russian stopwords.
291
+ * Relevanssi now highlights search query synonyms as well in documents.
292
+
293
+ = 3.1.9 =
294
+ * Fix to make Relevanssi compatible with WordPress 3.7.
295
+ * Fixed a mistyped database table name.
296
+ * Relevanssi disables responsive-flipbook shortcode in indexing; it was causing problems.
297
+ * Fixed a problem with an author dropdown with no author selected.
298
+
299
+ = 3.1.8 =
300
+ * Category restriction and exclusion and couple of other category-related settings didn't work properly.
301
+ * Support for Polylang broke the support for WPML. That is now fixed.
302
+ * One deprecated `$wpdb->escape()` was still left; it's gone now.
303
+ * Shortcode `layerslider` was causing problems with Relevanssi; Relevanssi now disables it before building excerpts.
304
+ * Relevanssi won't break BBPress search anymore.
305
+ * If Relevanssi Premium is installed, deleting Relevanssi will not remove the databases and the options.
306
+
307
+ = 3.1.7 =
308
+ * New filter: `relevanssi_comment_content_to_index` lets you modify comment content before it's indexed by Relevanssi (to index comment meta, for example).
309
+ * Facetious support: if post_type is set to -1, Relevanssi will not hang up on it.
310
+ * Numerical search terms work better now.
311
+ * Excerpt-building had issues, which are now fixed.
312
+ * Punctuation removal now replaces   with a space.
313
+ * "starrater" short code from GD Star Rating is now disabled in indexing.
314
+ * Punctuation removal now replaces invisible spaces with a normal space.
315
+ * Division by zero error caused by 0 in posts_per_page is now prevented, and -1 value for posts_per_page handled better.
316
+ * Relevanssi doesn't apply `get_the_excerpt` filters to excerpts it builds any more.
317
+ * New filter: `relevanssi_excerpt` lets you modify the excerpts Relevanssi creates.
318
+ * Relevanssi now suspends WP post cache while indexing, making indexing a lot more efficient. Thanks to Julien Mession for this one.
319
+ * Deprecated function errors in 3.6 removed.
320
+ * When search included user profiles or taxonomy terms, Relevanssi would generate lots of MySQL errors. Not anymore.
321
+ * New filter: `relevanssi_valid_status` lets you modify the post statuses Relevanssi indexes.
322
+ * New filter: `relevanssi_index_taxonomies_args` lets you modify the arguments passed to get_terms() when indexing taxonomies (for example to set 'hide_empty' to false).
323
+ * Searching by taxonomy ID could confuse two taxonomies with the same term_id. The search is now checking the taxonomy as well to see it's correct.
324
+ * Basic support for Polylang plugin.
325
+ * Russian and Italian stopwords are now included, thanks to Flector and Valerio Vendrame.
326
+ * Small fix in the way user meta fields are handled.
327
+
328
+ = 3.1.6 =
329
+ * DEACTIVATE AND ACTIVATE THE PLUGIN AFTER YOU UPDATE.
330
+ * Fuzzy searches are now a lot more efficient; they were a huge resource hog before.
331
+ * Fixed a possible MySQL injection attack.
332
+ * Fixed MySQL errors from empty meta queries.
333
+ * Sort order (orderby and order variables) are now read from query variables instead of global variables.
334
+ * Relevanssi will not choke on bad values of orderby anymore.
335
+ * Limit searches is improved: when using AND search it is less likely to miss results.
336
+ * Phrase recognition read the whole post content (which it didn't need) from database, causing memory issues in some cases. Fixed that.
337
+ * Highlighting was broken, and should work much better now.
338
+
339
+ = 3.1.5 =
340
+ * OR fallback didn't actually fall back to OR, but instead got stuck in an endless loop of AND searches.
341
+ * Meta queries didn't work without a key; now they work with just meta_value or meta_value_num.
342
+ * Meta queries had problems with meta_value being set to null.
343
+ * Relevanssi now supports category__and. By default this sets include_children to false.
344
+ * When querying by slug, the term taxonomy is also taken into consideration, fixing problems when same slug appears in different taxonomies.
345
+ * Author search didn't work.
346
+ * Fixed an error message caused by all-number synonyms starting with zero, like 02.
347
+ * Synonyms are now case-insensitive.
348
+ * New filter: `relevanssi_default_tax_query_relation` can be used to change the default tax query relation from OR to AND.
349
+ * Fixed undefined variable errors when doing an OR fallback.
350
+ * New filter: `relevanssi_bots_to_not_log` makes it possible to block bots from logs. The format matches what other plugins, ie. WP-Useronline, use for bot blocking, so you can share block lists.
351
+ * New filter: `relevanssi_admin_search_ok` gives you more control when Relevanssi overrides the default WP search in admin, useful for fixing P2P_Box AJAX search.
352
+ * Ordering search results by title or date in admin search works now.
353
+ * Modified the way the highlights work; now highlighting words with apostrophes should produce more meaningful results.
354
+ * Highlighting should not highlight anything between & and ; or in <style> or <script> tags, thus solving some of the problems related to highlights. Reports of how well this works are welcome.
355
+ * On-post highlighting now only highlights content in the loop, so menu texts and other off-the-loop stuff should not get highlights anymore.
356
+ * New action hook: `relevanssi_pre_indexing_query` can be used to "SET OPTION SQL_BIG_SELECTS=1" if needed.
357
+ * Major indexing problems caused by shortcodes changing the post ID during the indexing of posts are now fixed.
358
+ * Relevanssi was being called twice when a post was saved, on `save_post` and `wp_insert_post`. I removed the hook on `save_post`.
359
+ * Unsuccessful searches are now ordered by count, like the successful queries are.
360
+
361
+ = 3.1.4 =
362
+ * Choosing which taxonomies to index is now done with an easy-to-use checkbox list.
363
+ * Support for WP Table Reloaded and TablePress. Tables created with these plugins will now be expanded and the content indexed by Relevanssi.
364
+ * New filter: `relevanssi_index_comments_exclude` can be used to exclude comments from indexing. The filter gets the post ID as a parameter, so you can prevent comments of particular posts being indexed, yet index those posts.
365
+ * Relevanssi now adds spaces between tags when creating excerpts to make neater excerpts from tables and other similar situations.
366
+ * Relevanssi now indexes unattached attachments, if you choose to index attachments.
367
+ * Fixed the problems with Twenty Ten and Twenty Eleven themes better.
368
+ * $match->tag now contains the number of tag hits.
369
+ * Relevanssi now adds relevance score to posts before passing them to relevanssi_hits_filter. You can find it in $post->relevance_score.
370
+ * Tags in breakdowns always showed 0, even though tags were indexed and searched correctly. That's now fixed.
371
+
372
+ = 3.1.3 =
373
+ * AND search did not work in all cases.
374
+ * Posts couldn't be found by category name. Fixed that.
375
+
376
+ = 3.1.2 =
377
+ * Exclude category option was broken. Fixed that.
378
+ * Searching for a non-existing category ID caused an error. Fixed that.
379
+ * Occasional blank screens of death occurred when multibyte string operations weren't installed. That should not happen anymore.
380
+ * Fallback to OR search was a bit broken.
381
+
382
+ = 3.1.1 =
383
+ * Small fix to prevent database errors.
384
+ * Small fix to prevent disappearing excerpts.
385
+
386
+ = 3.1 =
387
+ * Fixed the uninstalling instructions.
388
+ * Fixes a problem with Twenty Ten and Twenty Eleven themes that caused doubled "Continue Reading" links.
389
+ * Fixed a notice about undefined variable on plugin update pages.
390
+ * Small bug fixes on search to remove warning notices.
391
+ * New filter: `relevanssi_index_custom_fields` can be used to modify the list of custom fields to index.
392
+ * Deleting menus caused a warning. That is now fixed.
393
+ * Relevanssi has an option to disable IP logging (which is actually illegal in some countries). Thanks to Stefan Eufinger.
394
+ * Searching in subcategories worked sometimes, but not always. Thanks to Faebu.
395
+ * The "Limit searches" option didn't work too well in the case of strong weightings, as it didn't take note of any weights. Now it works better.
396
+ * Added a note about disabling custom excerpts when they are not needed - they can slow down the search quite a bit.
397
+ * New filter: `relevanssi_options_capability` can be used to modify the capability required to see the options page (default is `manage_options`).
398
+ * Fixed the way IDF is calculated to account some extreme cases with small databases.
399
+ * New filter: `relevanssi_index_custom_fields` gives added control over which custom fields are indexed.
400
+ * Fixed filter: `relevanssi_pre_excerpt_content` wasn't working properly.
401
+ * Relevanssi now supports tax_query, for most part. You can query multiple taxonomies, use relation AND and OR, use operators AND, IN and NOT IN and choose include_children (which defaults to true). Old `taxonomy` and `term` still work, but I recommend using tax_query for the level of control it offers.
402
+ * Relevanssi now works better with category restrictions. The extra `cats` query variable is no longer necessary, Relevanssi can now read multiple categories from `cat`. You can also use `category__and`, `category__in` and `category__not_in`.
403
+ * Same goes with tags: `tags` is now longer necessary. Relevanssi has full support for `tag`, `tag_id`, `tag__and`, `tag__in`, `tag__not_in`, `tag_slug__and`, `tag_slug__in` and `tag_slug__not_in`. For `tag`, both `term1+term2` and `term1,term2` is supported.
404
+ * Relevanssi now supports `author_name` and negative values for `author`.
405
+ * Relevanssi now supports `offset` query variable.
406
+ * Relevanssi now supports meta_query. You can use all comparisons (also EXISTS and NOT EXISTS, even if you don't have WP 3.5). You can also use the older `meta_key` and `meta_value` query variables, including all the comparisons. I have not tested all possible meta_query constructions, so bug reports of things that don't work as expected are welcome.
407
+ * New index on the database makes some database operations faster.
408
+ * Removed a bug that prevents one-character words from being indexed in titles, despite the minimum word length setting.
409
+ * Removed a warning when searching for nothing.
410
+ * Fixes a warning about $wpdb->prepare() caused by a change in WordPress 3.5.
411
+
412
+ = 3.0.5 =
413
+ * AFTER UPGRADING FROM 2.x: Make sure you deactivate and reactivate Relevanssi in order to make the database changes happen.
414
+ * Fixed a major bug that caused the searches to fail when "Limit searches" was enabled, but "Limit" was not defined.
415
+ * Modified `relevanssi_remove_punct()` to replace curly apostrophes and quotes with spaces instead of removing them, to make the index more consistent (regular apostrophes were replaced with spaces). Reindexing the database is a good idea.
416
+ * Fixed some misleading text on the options page.
417
+
418
+ = 3.0.4 =
419
+ * AFTER UPGRADING FROM 2.x: Make sure you deactivate and reactivate Relevanssi in order to make the database changes happen.
420
+ * Fixed another problem with the Jetpack Contact Form.
421
+ * Fixed an error message caused by searching for numbers.
422
+ * Phrases are now also recognized in drafts and attachments.
423
+ * You can now set `post_types` to 'any'.
424
+
425
+ = 3.0.3 =
426
+ * AFTER UPGRADING FROM 2.x: Make sure you deactivate and reactivate Relevanssi in order to make the database changes happen.
427
+ * Fixed a bug that made custom taxonomy term searches fail.
428
+ * New filter: `relevanssi_user_searches_capability` lets you modify the minimum capability required to see the User searches page.
429
+
430
+ = 3.0.2 =
431
+ * AFTER UPGRADING FROM 2.x: Make sure you deactivate and reactivate Relevanssi in order to make the database changes happen.
432
+ * Fixed the "Cannot use a scalar value as an array" bug in indexing.
433
+ * Role-Scoper users: in order to make Relevanssi work with Role-Scoper, replace the Relevanssi helper file in Role-Scoper with [this file](https://www.relevanssi.com/relevanssi-helper-front_rs.txt).
434
+ * Removed an error message about set_time_limit() under safe_mode.
435
+ * Jetpack Contact Form shortcode caused problems when indexing. Relevanssi will now simply remove the shortcode before indexing.
436
+ * Fixed errors caused by / characters in highlighting.
437
+
438
+ = 3.0.1 =
439
+ * AFTER UPGRADING FROM 2.x: Make sure you deactivate and reactivate Relevanssi in order to make the database changes happen.
440
+ * Fixed lots of problems in logging searches.
441
+ * Added an alert when user hasn't selected any post types to index (and default values).
442
+ * Custom field setting 'visible' works now.
443
+ * Searching by category title works now, and you can adjust the category weight in the settings.
444
+
445
+ = 3.0 =
446
+ * AFTER UPGRADING FROM 2.x: Make sure you deactivate and reactivate Relevanssi in order to make the database changes happen.
447
+ * WORD OF WARNING: This is a major update, with lots of changes as you can see, and since I couldn't find any beta testers to help test it out, consider this a beta release, with bugs probable.
448
+ * The database has been updated to match the more advanced structure in Relevanssi Premium. This requires a re-indexing of the database.
449
+ * The indexing process is more efficient now.
450
+ * Relevanssi now includes a throttle feature, which makes the searches more efficient.
451
+ * Relevanssi now disables the default WP search.
452
+ * The custom field search hack using `cat` set to "custom" doesn't work any more. If you wish to filter by custom field, you need Relevanssi Premium, which does it better anyway.
453
+ * Relevanssi can handle certain whitespace characters better in indexing.
454
+ * Apostrophes are now replaced with whitespace instead of being removed.
455
+ * Relevanssi now shows correct number of results when posts_per_page is set to -1.
456
+ * Fuzzy search didn't always activate when it should, if all found posts are private posts that can't be shown to user.
457
+ * Tab characters in excerpts are handled better now.
458
+ * Relevanssi search logs will now store user ID's and IP addresses for each query.
459
+ * You can now use user logins as well as numeric ID's to stop user from being logged.
460
+ * Attachments are now handled better. I'd still like to hear any complaints about attachments.
461
+ * Relevanssi now updates index for posts added with wp_update_post() function. (Thanks to Simon Blackbourn)
462
+ * Searching for pages in admin didn't work properly. Fixed that.
463
+ * Fixed warnings for undefined variables.
464
+ * Relevanssi won't mess media library searches any more.
465
+ * Search terms are no longer highlighted in titles on post pages. That caused too many problems.
466
+ * New collation rules to MySQL databases will make sure that word pairs like "pode" and "pôde" will not be considered duplicates in the stopword database.
467
+ * You can now set the "Custom fields to index" to "all" to index all custom fields and "visible" to index all visible custom fields (but not the ones with names starting with an underscore).
468
+ * Plugin now works properly without multibyte string functions.
469
+ * You can now choose to allow HTML tags in excerpts.
470
+ * New filter: `relevanssi_modify_wp_query` lets you modify $wp_query before it is passed to Relevanssi.
471
+ * New filter: `relevanssi_search_ok` lets you adjust when search is enabled.
472
+ * New filter: `relevanssi_pre_excerpt_content` lets you adjust post content before excerpt creation.
473
+ * New filter: `relevanssi_excerpt_content` lets you adjust post content before excerpt creation, but after `the_content`.
474
+ * New filter: `relevanssi_ellipsis` lets you change the default '...' in excerpts to something else.
475
+ * New filter: `relevanssi_do_not_index` is given a post ID and expects a boolean in return: should this post be indexed or not?
476
+ * New filter: `relevanssi_match` lets you meddle with the matching engine.
477
+ * New filter: `relevanssi_results` filters the result set from the search.
478
+ * New filter: `relevanssi_content_to_index` let's you add whatever content you wish to posts before they are indexed.
479
+ * New filter: `relevanssi_didyoumean_query` let's you modify the query for Did you mean? queries
480
+ * Changed filter: `relevanssi_post_ok` has different arguments, see source code for details.
481
+ * New shortcode: use shortcode `noindex` to wrap parts of posts you want to keep from the index.
482
+ * And a bunch of other changes.
483
+
484
+ = 2.9.14 =
485
+ * Relevanssi will now index pending and future posts. These posts are only shown in the admin search.
486
+
487
+ = 2.9.13 =
488
+ * Stripping shortcodes from excerpts didn't work properly. Should work now.
489
+ * Fixed a mistake in the FAQ: correct post date parameter is `post_date`, not `date`.
490
+ * New filter `relevanssi_results` added. This filter will process an array with (post->ID => document weight) pairs.
491
+ * Private and draft posts were deleted from the index when they were edited. This bug has been fixed. (Thanks to comprock.)
492
+ * When continuing indexing, Relevanssi now tells if there's more to index. (Thanks to mrose17.)
493
+ * Fixed problems with searching attachments. Indexing attachments still has some problems. When you build the index, attachments are indexed properly.
494
+ * Improved WPML support.
495
+ * The `relevanssi_index_doc()` function has a new parameter that allows you to bypass global $post and force the function to index the document given as a parameter (see 2.9.13 release notes at Relevanssi.com for more details).
496
+
497
+ = 2.9.12 =
498
+ * Scheduled cache truncate wasn't scheduled properly. It is now.
499
+ * Added support for 'author' query variable.
500
+ * Fixed a bug with indexing custom post types.
501
+
502
+ = 2.9.11 =
503
+ * Plugin now works properly without multibyte string functions.
504
+ * Fixed s2member support for s2member versions 110912 and above. (Thanks to Jason Caldwell.)
505
+ * Added support for 'tag' query variable.
506
+
507
+ = 2.9.10 =
508
+ * AND search failed, when search query included terms that are shorter than the minimum word length.
509
+ * Improved s2member support.
510
+ * Fixed errors about deprecated ereg_replace.
511
+ * Small fix to Did you mean suggestions.
512
+
513
+ = 2.9.9 =
514
+ * Removed warnings about undefined functions and missing $wpdb.
515
+ * Fixed a bug that removed 'à' from search terms.
516
+ * Phrases are recognized from custom field searches.
517
+
518
+ = 2.9.8 =
519
+ * Support for s2member membership plugin. Search won't show posts that the current user isn't allowed to see.
520
+ * New filter `relevanssi_post_ok` can be used to add support for other membership plugins.
521
+ * Post meta fields that contain arrays are now indexed properly, expanding all the arrays.
522
+
523
+ = 2.9.7 =
524
+ * Fixed a bug that causes problems when paging search results.
525
+ * Taxonomy term restrictions didn't work most of the time.
526
+ * the_content filters didn't run on excerpts.
527
+ * Style data and other extra elements created by short codes are now stripped.
528
+
529
+ = 2.9.6 =
530
+ * Fixed a problem causing "Attempt to modify property of non-object" errors.
531
+ * Fixed a warning message.
532
+
533
+ = 2.9.5 =
534
+ * Searching for private posts caused an error message.
535
+
536
+ = 2.9.4 =
537
+ * Relevanssi should now be much lighter on server.
538
+ * Post date selection didn't work properly. Fixed that.
539
+ * Stopwords can be exported.
540
+ * Restricting indexing on custom post types works better.
541
+ * Minimum word length is properly enforced in indexing.
542
+ * Punctuation removal is more efficient.
543
+ * Fixed a MySQL error that was triggered by a media upload.
544
+ * Fixed a bug that caused an error when quick editing a post.
545
+
546
+ = 2.9.3 =
547
+ * A call to a non-existing function in 2.9.2 made all sorts of mess. This release fixes all problems with broken loops. I'm sorry about the bug.
548
+
549
+ = 2.9.2 =
550
+ * It's now possible to adjust the number of search results per page. See [Changing posts_per_page](https://www.relevanssi.com/knowledge-base/posts-per-page/) for instructions.
551
+ * Somebody reported revisions appearing in the search results. Added an extra check to prevent that.
552
+ * Improved the indexing procedure to prevent MySQL errors from appearing and to streamline the process.
553
+ * Improved the way custom post types can be handled in indexing.
554
+ * Improved the method of removing nested highlights.
555
+
556
+ = 2.9.1 =
557
+ * It is now possible to change the default result order from relevance to post date.
558
+ * Fixed a bug that caused wrong $post object to be set in indexing.
559
+ * Added a new hook `relevanssi_excerpt_content`; see [Knowledge Base](https://www.relevanssi.com/category/knowledge-base/) for details.
560
+
561
+ = 2.9 =
562
+ * Fixed a bug that caused Cyrillic searches in the log to get corrupted.
563
+ * Punctuation removal function is now triggered with a filter call and can thus be replaced.
564
+ * Google Adsense caused double hits to the user search logs. That's now fixed thanks to Justin Klein.
565
+ * User search log is available to user with `edit_post` capabilities (editor role). Thanks to John Blackbourn.
566
+ * A proper database collation is now set. Thanks to John Blackbourn.
567
+ * UI looks better. Thanks to John Blackbourn.
568
+ * Lots of small fixes here and there.
569
+
570
+ = 2.8.2 =
571
+ * The `order` parameter was case sensitive. It isn't anymore.
572
+ * WordPress didn't support searching for multiple categories with the `cat` query variable. There's now new `cats` which can take multiple categories.
573
+ * Similar to `cats` vs `cat`, you can use `post_types` to restrict the search to multiple post types.
574
+
575
+ = 2.8.1 =
576
+ * Fixed two small mistakes that caused error notices.
577
+ * Custom post types, particularly those created by More Types plugin, were causing problems.
578
+
579
+ = 2.8 =
580
+ * There's now a way to truncate the cache (sorry it took so long). Expired cache data is now automatically removed from the database every day. There's also an option to clear the caches.
581
+ * Highlights didn't work properly with non-ASCII alphabets. Now there's an option to make them work.
582
+ * Title highlight option now affects external search term highlights as well.
583
+ * There were some bugs on the options page.
584
+
585
+ = 2.7.5 =
586
+ * There was a bug that caused shortcodes to fail in 2.7.4. That's fixed now.
587
+ * Category search will now include subcategories as well, both when including and excluding.
588
+
589
+ = 2.7.4 =
590
+ * Improved the fallback to fuzzy search if no hits are found with regular search.
591
+ * AND searches sometimes failed to work properly, causing unnecessary fallback to OR search. Fixed.
592
+ * When using WPML, it's now possible to choose if the searches are limited to current language.
593
+ * Adding stopwords from the list of 25 common words didn't work. It works now.
594
+ * The instructions to add a category dropdown to search form weren't quite correct. They are now.
595
+ * Small fix that makes shortcodes in posts more compatible with Relevanssi.
596
+
597
+ = 2.7.3 =
598
+ * IMPORTANT SECURITY UPDATE: Earlier versions of Relevanssi have a cross-site scripting (XSS) vulnerability. Please install this update as soon as possible.
599
+ * Added instructions of doing a category dropdown in the search form in the FAQ.
600
+
601
+ = 2.7.2 =
602
+ * A silly typo caused the caching not to work. That's fixed now.
603
+ * A new filter: `relevanssi_didyoumean_query` lets you modify the query used for 'Did you mean?' searches.
604
+
605
+ = 2.7.1 =
606
+ * Thanks to a bug in the code, the WPML support didn't work. It's fixed now.
607
+
608
+ = 2.7 =
609
+ * Caching search results is possible. If you have lots of repeated queries, caching will provide extra speed and less wear on server.
610
+ * Multilanguage plugin WPML is now supported. If WPML is active, Relevanssi will automatically restrict search results to current language.
611
+ * New filter: `relevanssi_search_filter` lets you adjust search query variables. See source code for further details. Thanks to Sam Hotchkiss.
612
+ * Got a report of synonyms not working; hopefully fixed it now.
613
+ * It is now possible to set the minimum word length to index. Default is now 3 instead of 2.
614
+ * You can now add several stopwords at one go and remove all stopwords.
615
+ * Author search didn't work properly. It works now.
616
+ * Search result highlighting functions properly now, there might've been some problems with it.
617
+
618
+ = 2.6 =
619
+ * New setting allows user to define how `exclude_from_search` is handled. It's now possible to exclude a custom post type from general searches and search for it specifically by defining post_type.
620
+ * New filter: `relevanssi_hits_filter` lets you process hits found by Relevanssi. See FAQ.
621
+
622
+ = 2.5.6 =
623
+ * Attachments are no longer automatically indexed; there's an option for it now.
624
+ * You can now exclude custom post types from index.
625
+ * When AND search fails, it falls back to OR search. It's now possible to disable this fallback.
626
+
627
+ = 2.5.5 =
628
+ * The stopword management created empty stopwords. It won't anymore.
629
+ * Faulty HTML code in the admin page has been fixed.
630
+ * Indexing shortcodes that need the global $post context is now possible.
631
+ * Relevanssi is now aware of attachments and manages post_status of "inherit".
632
+ * These fixes were provided by Warren Tape, thanks!
633
+
634
+ = 2.5.4 =
635
+ * Small bugfix relating to post types.
636
+ * Added stopword management tools: way to remove and add stopwords.
637
+ * Custom excerpts can now be created from post excerpts as well, if those are indexed.
638
+ * Added answers to some frequently asked questions to the documentation.
639
+
640
+ = 2.5.3 =
641
+ * Very small bugfix fixing the error on line 1192.
642
+
643
+ = 2.5.2 =
644
+ * Fixed a bug about `mysql_real_escape_string()` expecting a string.
645
+ * Added documentation about compatibility issues.
646
+
647
+ = 2.5.1 =
648
+ * Option to highlight search terms in comment text as well.
649
+ * Fixed a small problem in highlighting search terms.
650
+
651
+ = 2.5 =
652
+ * Better support for other search plugins like [Dave's WordPress Live Search](http://wordpress.org/extend/plugins/daves-wordpress-live-search/).
653
+ * New User searches screen that shows more data about user searches.
654
+ * Search logs can now be emptied.
655
+ * Custom fields weren't indexed on updated posts. That is now fixed.
656
+ * Once again improved the highlighting: now the highlighting will look for word boundaries and won't highlight terms inside words.
657
+ * Relevanssi query engine can now be accessed directly, making all sorts of advanced hacking easier. See FAQ.
658
+
659
+ = 2.4.1 =
660
+ * Fixed a problem where search term highlighting was changing terms to lowercase.
661
+ * Fixed a problem with highlighting breaking stuff in shortcodes.
662
+ * Made some changes to the admin interface - there's more to come here, as the admin page is a bit of a mess right now.
663
+
664
+ = 2.4 =
665
+ * Highlighting post content won't highlight inside HTML tags anymore.
666
+ * Soft hyphens inside words are now removed in indexing. They still confuse the highlighting.
667
+ * Matching engine is now able to match category titles that contain apostrophes.
668
+
669
+ = 2.3.3.1 =
670
+ * Suppressed the error messages on the correct mb_strpos() function call. If you still get mb_strpos() errors, update.
671
+ * Added a FAQ note on getting the number of search results found.
672
+
673
+ = 2.3.3 =
674
+ * Suppressed notices on one mb_strpos() call.
675
+ * Added a search variable "by_date" to filter search results, see FAQ for details.
676
+
677
+ = 2.3.2 =
678
+ * Fixed a serious bug related to taxonomy term searches that could cause strange search results. Thanks to Charles St-Pierre for finding and killing the bug.
679
+ * Spanish stopwords are now included (thanks to Miguel Mariano).
680
+
681
+ = 2.3.1 =
682
+ * I fixed the highlighting logic a bit, the highlighting didn't work properly before.
683
+
684
+ = 2.3 =
685
+ * New highlighting option: HTML5 mark tag. Thanks to Jeff Byrnes.
686
+ * Relevanssi can now highlight search term hits in the posts user views from search. Highlighting for search term hits from external searches will be added later.
687
+ * It is now possible to add custom filtering to search results, see FAQ for details. Thanks to Charles St-Pierre.
688
+ * Removed search result highlighting from admin search, where it wasn't very useful.
689
+
690
+ = 2.2 =
691
+ * Relevanssi used to index navigation menu items. It won't, anymore.
692
+ * Translation and stopwords in Brazilian Portuguese added, thanks to Pedro Padron.
693
+
694
+ = 2.1.9 =
695
+ * No changes, I'm just trying to resurrect the broken Relevanssi plugin page.
696
+
697
+ = 2.1.8 =
698
+ * Including the popular microtime_float function caused conflicts with several other plugins (whose authors are just as sloppy as I am!). Fixed that.
699
+
700
+ = 2.1.7 =
701
+ * The index categories option wasn't saved properly. Now it is.
702
+ * Fixed the %terms% breakdown option to show correct counts and added %total% to show total hit count.
703
+ * Phrases are now matched also in post titles and category titles (before they were only matched against post content).
704
+ * Post excerpts can now be indexed and searched. I would appreciate feedback from people who use this feature: do you use the excerpts in search results? If you use custom snippets created by Relevanssi, what you want them to display?
705
+ * Set the constant TIMER to true to enable timing of the search process for debugging reasons.
706
+
707
+ = 2.1.6 =
708
+ * Title highlighting caused an error. That is now fixed. I also streamlined the highlighting code a bit.
709
+
710
+ = 2.1.5 =
711
+ * You can now enter synonyms, expanding queries with synonyms when doing an OR search. This is useful to expand acronyms and abbreviations, for example.
712
+ * When doing a phrase search, highlighting will only highlight phrase hits.
713
+ * New breakdown variable %terms% will list hits by term.
714
+ * Some users reported error messages about unexpected T_OBJECT_OPERATOR. Those shouldn't happen, please let me know if they still do.
715
+ * Highlighting will now highlight only complete words.
716
+
717
+ = 2.1.4 =
718
+ * Fixed a small bug that could cause all queries by anonymous users to go unlogged.
719
+
720
+ = 2.1.3 =
721
+ * OR operator makes a comeback! The default operator is now an option, and if you choose AND and search gets no results, an OR search is also run.
722
+ * You can now give a list of user ids - any searches by those users will not be logged. List your admin user id, so your test searches won't clutter the log.
723
+
724
+ = 2.1.2 =
725
+ * Removing punctuation didn't work properly, making phrase search impossible. I'd thought I'd fix it, but for some reason I made a mistake and the fix didn't appear in the released versions.
726
+ * Search has now an implicit AND operator, which means that every search term must appear in all result documents. Please let me know if you'd prefer an implicit OR operator, like Relevanssi had before.
727
+ * Relevanssi options page now shows the amount of indexed documents, making troubleshooting indexing easier.
728
+
729
+ = 2.1.1 =
730
+ * "Did you mean" suggestions now work in blogs that are not in root directory.
731
+ * Early 2.1 downloads had faulty encodings. Update to make sure you've got a good file.
732
+
733
+ = 2.1 =
734
+ * An experimental "Did you mean" suggestion feature. Feedback is most welcome.
735
+ * Added a short code to facilitate adding links to search results.
736
+ * Fixed a small bug that in some cases caused MySQL errors.
737
+
738
+ = 2.0.3 =
739
+ * Fixed problems relating to the orderby parameter.
740
+
741
+ = 2.0.2 =
742
+ * Small bug fix: with private posts, sometimes correct amount of posts weren't displayed.
743
+
744
+ = 2.0.1 =
745
+ * Exclude posts/pages option wasn't saved on the options page. It works now.
746
+ * 2.0 included an unnecessary function that broke Relevanssi in WP 2.8.5. Fixed that.
747
+
748
+ = 2.0 =
749
+ * Post authors can now be indexed and searched. Author are indexed by their display name.
750
+ * In search results, $post->relevance_score variable will now contain the score of the search result.
751
+ * Comment authors are now included in the index, if comments are indexed.
752
+ * Search results can be sorted by any $post field and in any order, in addition of sorting them by relevancy.
753
+ * Private posts are indexed and displayed to the users capable of seeing them. This uses Role-Scoper plugin, if it's available, otherwise it goes by WordPress capabilities.
754
+ * Searches can be restricted with a taxonomy term (see FAQ for details).
755
+
756
+ = 1.9 =
757
+ * Excerpts are now better and will contain more search terms and not just the first hit.
758
+ * Fixed an error relating to shortcodes in excerpts.
759
+ * If comments are indexed, custom excerpts will show text from comments as well as post content.
760
+ * Custom post type posts are now indexed as they are edited. That didn't work before.
761
+ * Cleaned out more error notices.
762
+
763
+ = 1.8.1 =
764
+ * Sometimes empty ghost entries would appear in search results. No more.
765
+ * Added support for the WordPress' post_type argument to restrict search results to single post type.
766
+ * Relevanssi will now check for the presence of multibyte string functions and warn if they're missing.
767
+ * The category indexing option checkbox didn't work. It's now fixed.
768
+ * Small fix in the way punctuation is removed.
769
+ * Added a new indexing option to index all public post types.
770
+
771
+ = 1.8 =
772
+ * Fixed lots of error notices that popped up when E_NOTICE was on. Sorry about those.
773
+ * Custom post types can now be indexed if wanted. Default behaviour is to index all post types (posts, pages and custom types).
774
+ * Custom taxonomies can also be indexed in addition to standard post tags. Default behaviour is to index nothing. If somebody knows a way to list all custom taxonomies, that information would be appreciated.
775
+
776
+ = 1.7.3 =
777
+ * Small bug fix: code that created database indexes was broken. Say "ALTER TABLE `wp_relevanssi` ADD INDEX (doc)" and "ALTER TABLE `wp_relevanssi` ADD INDEX (term)" to your MySQL db to fix this for an existing installation.
778
+
779
+ = 1.7.2 =
780
+ * Small bug fix: public posts that are changed to private are now removed from index (password protected posts remain in index).
781
+ * An Italian translation is now included (thanks to Alessandro Fiorotto).
782
+
783
+ = 1.7.1 =
784
+ * Small fix: the hidden variable cat now accepts negative category and tag ids. Negative categories and tags are excluded in search. Mixing inclusion and exclusion is possible.
785
+
786
+ = 1.7 =
787
+ * Major bug fix: Relevanssi doesn't kill other post loops on the search result page anymore. Please let me know if Relevanssi feels too slow after the update.
788
+ * Post categories can now be indexed.
789
+
790
+ = 1.6 =
791
+ * Relevanssi is now able to expand shortcodes before indexing to include shortcode content to the index.
792
+ * Fixed a bug related to indexing, where tag stripping didn't work quite as expected.
793
+
794
+ = 1.5.3 =
795
+ * Added a way to uninstall the plugin.
796
+ * A French translation is now included (thanks to Jean-Michel Meyer).
797
+
798
+ = 1.5.2 =
799
+ * Fixed a small typo in the code, tag and comment hit count didn't work in the breakdown. If you don't use the breakdown feature, updating is not necessary.
800
+
801
+ = 1.5.1 =
802
+ * User interface update, small changes to make the plugin easier to use.
803
+ * Fixed a small bug that sometimes causes "Empty haystack" warnings.
804
+
805
+ = 1.5 =
806
+ * Comments can now be indexed and searched (thanks to Cristian Damm).
807
+ * Tags can also be indexed (thanks to Cristian Damm).
808
+ * Search term hits in the titles can be highlighted in search results (thanks to Cristian Damm).
809
+ * When using custom excerpts, it's possible to add extra information on where the hits were made.
810
+ * Fuzzy matching is now user-adjustable.
811
+ * UTF-8 support is now better (thanks to Marcus Dalgren).
812
+
813
+ = 1.4.4 =
814
+ * Added an option to exclude posts or pages from search results. This feature was requested and provided by Cristian Damm.
815
+
816
+ = 1.4.3 =
817
+ * Indexing of custom fields is now possible. Just add a list of custom field names you want to include in the index on the settings page and re-index.
818
+
819
+ = 1.4.2 =
820
+ * Users can search for specific phrases by wrapping the phase with "quotes".
821
+ * Fixed a bug that caused broken HTML in some cases of highlighted search results (search term matches in highlighting HTML tags were being highlighted).
822
+ * Improved punctuation removal. This change requires reindexing the whole database.
823
+
824
+ = 1.4.1 =
825
+ * Fixed a bug that caused empty search snippets when using word-based snippets.
826
+ * Improved support for WP 2.5.
827
+ * Added an option to exclude categories and tags from search results.
828
+ * Added an option to index only posts or pages.
829
+ * Added French stopwords.
830
+
831
+ = 1.4 =
832
+ * Added an option to restrict searches to certain categories or tags, either by plugin option or hidden input field in the search form.
833
+ * The contents of `<script>` and other such tags are now removed from excerpts.
834
+ * When indexing, HTML tags and `[shortcodes]` are removed.
835
+ * Digits are no longer removed from terms. Re-index database to get them indexed.
836
+ * Wrapped the output of `relevanssi_the_excerpt()` in <p> tags.
837
+ * Stopwords are no longer removed from search queries.
838
+ * Search result snippet length can now be determined in characters or whole words.
839
+
840
+ = 1.3.3 =
841
+ * Small bug fixes, removed the error message caused by a query that is all stop words.
842
+ * Content and excerpt filters are now applied to excerpts created by Relevanssi.
843
+ * Default highlight CSS class has a unique name, `search-results` was already used by WordPress.
844
+
845
+ = 1.3.2 =
846
+ * Quicktags are now stripped from custom-created excerpts.
847
+ * Added a function `relevanssi_the_excerpt()`, which prints out the excerpt without triggering `wp_trim_excerpt()` filters.
848
+
849
+ = 1.3.1 =
850
+ * Another bug fix release.
851
+
852
+ = 1.3 =
853
+ * New query logging feature. Any feedback on query log display features would be welcome: what information you want to see?
854
+ * Added a CSS class option for search term highlighting.
855
+ * Fixed a bug in the search result excerpt generation code that caused endless loops with certain search terms.
856
+
857
+ = 1.2 =
858
+ * Added new features to display custom search result snippets and highlight the search terms in the results.
859
+
860
+ = 1.1.3 =
861
+ * Fixed a small bug, made internationalization possible (translations are welcome!).
862
+
863
+ = 1.1.2 =
864
+ * English stopword file had a problem, which is now fixed.
865
+
866
+ = 1.1.1 =
867
+ * Fixed a stupid bug introduced in the previous update. Remember always to test your code before sending files to repository!
868
+
869
+ = 1.1 =
870
+ * Fixes the problem with pages in search results.
871
+
872
+ = 1.0 =
873
+ * First published version.
lib/admin-ajax.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * /lib/admin-ajax.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
+
11
+ add_action( 'wp_ajax_relevanssi_truncate_index', 'relevanssi_truncate_index_ajax_wrapper' );
12
+ add_action( 'wp_ajax_relevanssi_index_posts', 'relevanssi_index_posts_ajax_wrapper' );
13
+ add_action( 'wp_ajax_relevanssi_count_posts', 'relevanssi_count_posts_ajax_wrapper' );
14
+ add_action( 'wp_ajax_relevanssi_count_missing_posts', 'relevanssi_count_missing_posts_ajax_wrapper' );
15
+ add_action( 'wp_ajax_relevanssi_list_categories', 'relevanssi_list_categories' );
16
+
17
+ /**
18
+ * Truncates the Relevanssi index.
19
+ *
20
+ * Wipes the index clean using relevanssi_truncate_index().
21
+ */
22
+ function relevanssi_truncate_index_ajax_wrapper() {
23
+ $response = relevanssi_truncate_index();
24
+ echo wp_json_encode( $response );
25
+ wp_die();
26
+ }
27
+
28
+ /**
29
+ * Indexes posts in AJAX context.
30
+ *
31
+ * AJAX wrapper for indexing posts. Parses the arguments, uses the
32
+ * relevanssi_build_index() to do the hard work, then creates the AJAX response.
33
+ */
34
+ function relevanssi_index_posts_ajax_wrapper() {
35
+ check_ajax_referer( 'relevanssi_indexing_nonce', 'security' );
36
+
37
+ $completed = absint( $_POST['completed'] );
38
+ $total = absint( $_POST['total'] );
39
+ $offset = absint( $_POST['offset'] );
40
+ $limit = absint( $_POST['limit'] );
41
+ $extend = strval( $_POST['extend'] );
42
+
43
+ $extend = false;
44
+ if ( 'true' === $extend ) {
45
+ $extend = true;
46
+ }
47
+
48
+ if ( $limit < 1 ) {
49
+ $limit = 1;
50
+ }
51
+
52
+ $response = array();
53
+
54
+ $is_ajax = true;
55
+ $verbose = false;
56
+ if ( $extend ) {
57
+ $offset = true;
58
+ }
59
+
60
+ $indexing_response = relevanssi_build_index( $offset, $verbose, $limit, $is_ajax );
61
+
62
+ if ( $indexing_response['indexing_complete'] ) {
63
+ $response['completed'] = 'done';
64
+ $response['percentage'] = 100;
65
+ $completed += $indexing_response['indexed'];
66
+ $response['total_posts'] = $completed;
67
+ $processed = $total;
68
+ } else {
69
+ $completed += $indexing_response['indexed'];
70
+ $response['completed'] = $completed;
71
+
72
+ if ( true === $offset ) {
73
+ $processed = $completed;
74
+ } else {
75
+ $offset = $offset + $limit;
76
+ $processed = $offset;
77
+ }
78
+
79
+ if ( $total > 0 ) {
80
+ $response['percentage'] = $processed / $total * 100;
81
+ } else {
82
+ $response['percentage'] = 0;
83
+ }
84
+ }
85
+
86
+ $response['feedback'] = sprintf(
87
+ // translators: Number of posts indexed on this go, total number of posts indexed so far, number of posts processed on this go, total number of posts to process.
88
+ _n( 'Indexed %1$d post (total %2$d), processed %3$d / %4$d.', 'Indexed %1$d posts (total %2$d), processed %3$d / %4$d.',
89
+ $indexing_response['indexed'], 'relevanssi'
90
+ ),
91
+ $indexing_response['indexed'], $completed, $processed, $total
92
+ ) . "\n";
93
+ $response['offset'] = $offset;
94
+
95
+ echo wp_json_encode( $response );
96
+ wp_die();
97
+ }
98
+
99
+ /**
100
+ * Counts the posts to index.
101
+ *
102
+ * AJAX wrapper for relevanssi_count_total_posts().
103
+ */
104
+ function relevanssi_count_posts_ajax_wrapper() {
105
+ $count = relevanssi_count_total_posts();
106
+ echo wp_json_encode( $count );
107
+ wp_die();
108
+ }
109
+
110
+ /**
111
+ * Counts the posts missing from the index.
112
+ *
113
+ * AJAX wrapper for relevanssi_count_missing_posts().
114
+ */
115
+ function relevanssi_count_missing_posts_ajax_wrapper() {
116
+ $count = relevanssi_count_missing_posts();
117
+ echo wp_json_encode( $count );
118
+ wp_die();
119
+ }
120
+
121
+ /**
122
+ * Lists categories.
123
+ *
124
+ * AJAX wrapper for get_categories().
125
+ */
126
+ function relevanssi_list_categories() {
127
+ $categories = get_categories( array(
128
+ 'taxonomy' => 'category',
129
+ 'hide_empty' => false,
130
+ ) );
131
+ echo wp_json_encode( $categories );
132
+ wp_die();
133
+ }
lib/admin_ajax.php DELETED
@@ -1,81 +0,0 @@
1
- <?php
2
-
3
- add_action( 'wp_ajax_relevanssi_truncate_index', 'relevanssi_truncate_index_ajax_wrapper' );
4
- add_action( 'wp_ajax_relevanssi_index_posts', 'relevanssi_index_posts_ajax_wrapper' );
5
- add_action( 'wp_ajax_relevanssi_count_posts', 'relevanssi_count_posts_ajax_wrapper' );
6
- add_action( 'wp_ajax_relevanssi_count_missing_posts', 'relevanssi_count_missing_posts_ajax_wrapper' );
7
- add_action( 'wp_ajax_relevanssi_list_categories', 'relevanssi_list_categories' );
8
-
9
- function relevanssi_truncate_index_ajax_wrapper() {
10
- $response = relevanssi_truncate_index();
11
- echo json_encode($response);
12
- wp_die();
13
- }
14
-
15
- function relevanssi_index_posts_ajax_wrapper() {
16
- $completed = absint( $_POST['completed'] );
17
- $total = absint( $_POST['total'] );
18
- $offset = absint( $_POST['offset'] );
19
- $limit = absint( $_POST['limit'] );
20
- $extend = strval($_POST['extend']);
21
- $extend === 'true' ? $extend = true : $extend = false;
22
-
23
- if ($limit < 1) $limit = 1;
24
-
25
- $response = array();
26
-
27
- $is_ajax = true;
28
- $verbose = false;
29
- //$limit = apply_filters('relevanssi_ajax_indexing_limit', 50);
30
- if ($extend) $offset = true;
31
-
32
- $indexing_response = relevanssi_build_index(true, $verbose, $limit, $is_ajax);
33
-
34
- if ($indexing_response['indexing_complete']) {
35
- $response['completed'] = "done";
36
- $response['percentage'] = 100;
37
- $completed += $indexing_response['indexed'];
38
- $response['total_posts'] = $completed;
39
- $processed = $total;
40
- }
41
- else {
42
- $completed += $indexing_response['indexed'];
43
- $response['completed'] = $completed;
44
-
45
- if ($offset === true) {
46
- $processed = $completed;
47
- }
48
- else {
49
- $offset = $offset + $limit;
50
- $processed = $offset;
51
- }
52
-
53
- $total > 0 ? $response['percentage'] = $processed / $total * 100 : $response['percentage'] = 0;
54
- }
55
-
56
- $response['feedback'] = sprintf(_n("Indexed %d post (total %d), processed %d / %d.", "Indexed %d posts (total %d), processed %d / %d.", $indexing_response['indexed'], 'relevanssi'), $indexing_response['indexed'], $completed, $processed, $total) . "\n";
57
- $response['offset'] = $offset;
58
-
59
- echo json_encode($response);
60
- wp_die();
61
- }
62
-
63
- function relevanssi_count_posts_ajax_wrapper() {
64
- $count = relevanssi_count_total_posts();
65
- echo json_encode($count);
66
- wp_die();
67
- }
68
-
69
- function relevanssi_count_missing_posts_ajax_wrapper() {
70
- $count = relevanssi_count_missing_posts();
71
- echo json_encode($count);
72
- wp_die();
73
- }
74
-
75
- function relevanssi_list_categories() {
76
- $categories = get_categories(array('taxonomy' => 'category', 'hide_empty' => false));
77
- echo json_encode($categories);
78
- wp_die();
79
- }
80
-
81
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/admin_scripts.js CHANGED
@@ -122,6 +122,10 @@ jQuery(document).ready(function($){
122
  $("#relevanssi_show_matches_text").attr('disabled', !this.checked);
123
  $("#relevanssi_highlight_docs_external").attr('disabled', !this.checked);
124
  });
 
 
 
 
125
  });
126
 
127
  var time = 0;
@@ -186,6 +190,7 @@ function process_indexing_step(args) {
186
  offset: args.offset,
187
  limit: args.limit,
188
  extend: args.extend,
 
189
  },
190
  dataType: 'json',
191
  success: function(response) {
@@ -260,6 +265,7 @@ function process_indexing_step(args) {
260
  'total_seconds' : args.total_seconds,
261
  'limit' : args.limit,
262
  'extend' : args.extend,
 
263
  };
264
 
265
  process_indexing_step(new_args);
122
  $("#relevanssi_show_matches_text").attr('disabled', !this.checked);
123
  $("#relevanssi_highlight_docs_external").attr('disabled', !this.checked);
124
  });
125
+
126
+ $("#relevanssi_searchblogs_all").click(function() {
127
+ $("#relevanssi_searchblogs").attr('disabled', this.checked);
128
+ });
129
  });
130
 
131
  var time = 0;
190
  offset: args.offset,
191
  limit: args.limit,
192
  extend: args.extend,
193
+ security: args.security,
194
  },
195
  dataType: 'json',
196
  success: function(response) {
265
  'total_seconds' : args.total_seconds,
266
  'limit' : args.limit,
267
  'extend' : args.extend,
268
+ 'security' : args.security,
269
  };
270
 
271
  process_indexing_step(new_args);
lib/admin_scripts_free.js CHANGED
@@ -40,7 +40,8 @@ jQuery(document).ready(function($) {
40
  'offset' : 0,
41
  'total_seconds' : 0,
42
  'limit' : 10,
43
- 'extend' : false,
 
44
  };
45
  process_indexing_step(args);
46
  });
40
  'offset' : 0,
41
  'total_seconds' : 0,
42
  'limit' : 10,
43
+ 'extend' : false,
44
+ 'security' : nonce.indexing_nonce,
45
  };
46
  process_indexing_step(args);
47
  });
lib/class-relevanssi-taxonomy-walker.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * /lib/class-relevanssi-taxonomy-walker.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
+
11
+ /**
12
+ * A taxonomy walker used in Relevanssi interface.
13
+ *
14
+ * This is needed for wp_terms_checklist() in the Relevanssi admin interface to
15
+ * control the way the taxonomies are listed.
16
+ */
17
+ class Relevanssi_Taxonomy_Walker extends Walker_Category_Checklist {
18
+ /**
19
+ * Name of the input element.
20
+ *
21
+ * @var string $name Name of the input element.
22
+ */
23
+ public $name;
24
+
25
+ /**
26
+ * Creates a single element of the list.
27
+ *
28
+ * @see Walker::start_el()
29
+ *
30
+ * @param string $output Used to append additional content (passed by reference).
31
+ * @param object $category Category data object.
32
+ * @param int $depth Optional. Depth of category in reference to parents. Default 0.
33
+ * @param array $args Optional. An array of arguments. See wp_list_categories(). Default empty array.
34
+ * @param int $id Optional. ID of the current category. Default 0.
35
+ */
36
+ public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
37
+ if ( empty( $args['taxonomy'] ) ) {
38
+ $taxonomy = 'category';
39
+ } else {
40
+ $taxonomy = $args['taxonomy'];
41
+ }
42
+
43
+ $name = $this->name;
44
+
45
+ if ( ! isset( $args['popular_cats'] ) ) {
46
+ $args['popular_cats'] = array();
47
+ }
48
+
49
+ if ( ! isset( $args['selected_cats'] ) ) {
50
+ $args['selected_cats'] = array();
51
+ }
52
+
53
+ $class = '';
54
+ $inner_class = '';
55
+
56
+ if ( ! empty( $args['list_only'] ) ) {
57
+ $aria_checked = 'false';
58
+ $inner_class = 'category';
59
+
60
+ /** This filter is documented in wp-includes/category-template.php */
61
+ $output .= "\n" . '<li' . $class . '>' .
62
+ '<div class="' . $inner_class . '" data-term-id=' . $category->term_id .
63
+ ' tabindex="0" role="checkbox" aria-checked="' . $aria_checked . '">' .
64
+ esc_html( apply_filters( 'the_category', $category->name ) ) . '</div>';
65
+ } else {
66
+ /** This filter is documented in wp-includes/category-template.php */
67
+ $output .= "\n<li id='{$taxonomy}-{$category->term_id}'$class>" .
68
+ '<label class="selectit"><input value="' . $category->term_id . '" type="checkbox" name="' . $name . '[]" id="in-' . $taxonomy . '-' . $category->term_id . '"' .
69
+ checked( in_array( strval( $category->term_id ), $args['selected_cats'], true ), true, false ) .
70
+ disabled( empty( $args['disabled'] ), false, false ) . ' /> ' .
71
+ esc_html( apply_filters( 'the_category', $category->name ) ) . '</label>';
72
+ }
73
+ }
74
+ }
lib/common.php CHANGED
@@ -1,129 +1,241 @@
1
  <?php
 
 
 
 
 
 
 
 
2
 
3
- // thanks to rvencu
4
- function relevanssi_wpml_filter($data) {
5
- $use_filter = get_option('relevanssi_wpml_only_current');
6
- if ('on' == $use_filter) {
7
- //save current blog language
8
- $lang = get_bloginfo('language');
9
- $filtered_hits = array();
10
- foreach ($data[0] as $hit) {
11
- if (is_integer($hit)) $hit = get_post($hit); // this in case "fields" is set to "ids"
12
-
13
- if (isset($hit->blog_id)) {
14
- switch_to_blog($hit->blog_id);
 
 
 
 
 
 
 
 
 
 
15
  }
16
- global $sitepress;
17
 
18
- if (function_exists('icl_object_id') && !function_exists('pll_is_translated_post_type')) {
19
- if ($sitepress->is_translated_post_type($hit->post_type)) {
20
- if ($hit->ID == icl_object_id($hit->ID, $hit->post_type, false, $sitepress->get_current_language())) $filtered_hits[] = $hit;
 
 
 
 
 
21
  }
22
- else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  $filtered_hits[] = $hit;
24
  }
25
- }
26
- // if there is no WPML but the target blog has identical language with current blog,
27
- // we use the hits. Note en-US is not identical to en-GB!
28
- elseif (get_bloginfo('language') == $lang) {
29
  $filtered_hits[] = $hit;
30
  }
31
- if (isset($hit->blog_id)) {
 
32
  restore_current_blog();
33
  }
34
  }
35
- return array($filtered_hits, $data[1]);
 
36
  }
 
37
  return $data;
38
  }
39
 
40
- /*
41
- * If the Polylang allow all option is enabled, removes the Polylang language filter.
 
 
 
 
 
 
42
  */
43
- function relevanssi_polylang_filter($query) {
44
- $polylang_allow_all = get_option('relevanssi_polylang_all_languages');
45
- if ($polylang_allow_all == "on") {
46
  $ok_queries = array();
47
 
48
- foreach ($query->tax_query->queries as $tax_query) {
49
- if ($tax_query['taxonomy'] != 'language') $ok_queries[] = $tax_query;
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
  $query->tax_query->queries = $ok_queries;
52
-
53
- if (isset($query->query_vars['tax_query'])) {
 
54
  $ok_queries = array();
55
- foreach ($query->query_vars['tax_query'] as $tax_query) {
56
- if ($tax_query['taxonomy'] != 'language') $ok_queries[] = $tax_query;
 
 
57
  }
58
  $query->query_vars['tax_query'] = $ok_queries;
59
  }
60
 
61
- if (isset($query->query_vars['taxonomy']) && $query->query_vars['taxonomy'] === 'language') {
62
- unset($query->query_vars['taxonomy']);
63
- unset($query->query_vars['term']);
 
64
  }
65
  }
66
 
67
  return $query;
68
  }
69
 
70
- /*
71
- * Fetches a key-direction pair from the orderby array. Converts key names to match the post object parameters
72
- * when necessary and seeds the random generator, if required.
73
- */
74
- function relevanssi_get_next_key(&$orderby) {
75
- if (!is_array($orderby) || count($orderby) < 1) return array('key' => null, 'dir' => null, 'compare' => null);
76
-
77
- list($key) = array_keys($orderby);
78
- $dir = $orderby[$key];
79
- unset($orderby[$key]);
80
-
81
- $compare = "string";
82
-
83
- if (strtolower($dir) == "rand") $key = "rand";
84
-
85
- if ('title' == $key) $key = 'post_title';
86
- if ('date' == $key) $key = 'post_date';
87
- if ('modified' == $key) $key = 'post_modified';
88
- if ('parent' == $key) $key = 'post_parent';
89
- if ('type' == $key) $key = 'post_type';
90
- if ('name' == $key) $key = 'post_name';
91
- if ('author' == $key) $key = 'post_author';
92
- if ('relevance' == $key) $key = 'relevance_score';
93
-
94
- $numeric_keys = array('menu_order', 'ID', 'post_parent', 'post_author', 'comment_count', 'relevance_score');
95
- $date_keys = array('post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt');
96
-
97
- if (in_array($key, $numeric_keys)) $compare = "number";
98
- if (in_array($key, $date_keys)) $compare = "date";
99
-
100
- if ('rand' == $key) {
101
- if (is_numeric($dir)) srand($dir);
102
- }
103
- else {
104
- $dir = strtolower($dir);
105
- if ($dir != "asc") $dir = "desc";
106
- }
107
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  $values = array(
109
- 'key' => $key,
110
- 'dir' => $dir,
111
- 'compare' => $compare
112
  );
113
  return $values;
114
  }
115
 
116
- /*
117
- * Fetches the key values for the item pair. If random order is required, will randomize the order.
 
 
 
 
 
 
 
 
 
 
 
118
  */
119
- function relevanssi_get_compare_values($key, $item_1, $item_2) {
120
- function_exists('mb_strtolower') ? $strtolower = 'mb_strtolower' : $strtolower = 'strtolower';
121
-
122
- if ($key === "rand") {
123
  do {
124
  $key1 = rand();
125
  $key2 = rand();
126
- } while ($key1 == $key2);
127
  $keys = array(
128
  'key1' => $key1,
129
  'key2' => $key2,
@@ -131,32 +243,58 @@ function relevanssi_get_compare_values($key, $item_1, $item_2) {
131
  return $keys;
132
  }
133
 
134
- $key1 = "";
135
- $key2 = "";
136
 
137
- if ($key === "meta_value" || $key === "meta_value_num") {
138
  global $wp_query;
 
139
  $key = $wp_query->query_vars['meta_key'];
140
- if (!isset($key)) return array("", "");
141
-
142
- $key1 = get_post_meta($item_1->ID, $key, true);
143
- if (empty($key1)) $key1 = apply_filters('relevanssi_missing_sort_key', $key1, $key);
144
 
145
- $key2 = get_post_meta($item_2->ID, $key, true);
146
- if (empty($key2)) $key2 = apply_filters('relevanssi_missing_sort_key', $key2, $key);
147
- }
148
- else {
149
- if (isset($item_1->$key)) {
150
- $key1 = call_user_func($strtolower, $item_1->$key);
 
 
 
 
 
 
 
 
 
151
  }
152
- else {
153
- $key1 = apply_filters('relevanssi_missing_sort_key', $key1, $key);
 
 
 
 
 
154
  }
155
- if (isset($item_2->$key)) {
156
- $key2 = call_user_func($strtolower, $item_2->$key);
 
 
 
 
 
 
157
  }
158
- else {
159
- $key2 = apply_filters('relevanssi_missing_sort_key', $key2, $key);
 
 
 
 
 
160
  }
161
  }
162
 
@@ -167,344 +305,571 @@ function relevanssi_get_compare_values($key, $item_1, $item_2) {
167
  return $keys;
168
  }
169
 
170
- function relevanssi_compare_values($key1, $key2, $compare) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  $val = 0;
172
- if ($compare === "date") {
173
- if (strtotime($key1) > strtotime($key2)) {
174
  $val = 1;
175
- }
176
- else if (strtotime($key1) < strtotime($key2)) {
177
  $val = -1;
178
  }
179
- }
180
- else if ($compare === "string") {
181
- $val = relevanssi_mb_strcasecmp($key1, $key2);
182
- }
183
- else {
184
- if ($key1 > $key2) {
185
  $val = 1;
186
- }
187
- else if ($key1 < $key2) {
188
  $val = -1;
189
  }
190
  }
191
  return $val;
192
  }
193
 
194
- function relevanssi_mb_strcasecmp($str1, $str2, $encoding = null) {
195
- if (!function_exists('mb_internal_encoding')) {
196
- return strcasecmp($str1, $str2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  }
198
- else {
199
- if (null === $encoding) $encoding = mb_internal_encoding();
200
- return strcmp(mb_strtoupper($str1, $encoding), mb_strtoupper($str2, $encoding));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  }
202
  }
203
 
204
- function relevanssi_cmp_function($a, $b) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  global $relevanssi_keys, $relevanssi_dirs, $relevanssi_compares;
 
206
  $level = -1;
207
- $val = 0;
208
 
209
- while ($val === 0) {
210
  $level++;
211
- if (!isset($relevanssi_keys[$level])) {
 
212
  $level--;
213
- break; // give up – we can't sort these two
214
  }
215
- $compare = $relevanssi_compares[$level];
216
- $compare_values = relevanssi_get_compare_values($relevanssi_keys[$level], $a, $b);
217
- $val = relevanssi_compare_values($compare_values['key1'], $compare_values['key2'], $compare);
218
  }
219
-
220
- if ('asc' === $relevanssi_dirs[$level]) {
221
- return $val;
222
- }
223
- else {
224
- return $val * -1;
225
  }
 
 
226
  }
227
 
228
  /**
229
- * Function by Matthew Hood http://my.php.net/manual/en/function.sort.php#75036
 
 
 
 
 
 
 
 
 
 
 
230
  */
231
- function relevanssi_object_sort(&$data, $orderby) {
232
  global $relevanssi_keys, $relevanssi_dirs, $relevanssi_compares;
233
-
234
- $relevanssi_keys = array();
235
- $relevanssi_dirs = array();
236
  $relevanssi_compares = array();
237
-
238
  do {
239
- $values = relevanssi_get_next_key($orderby);
240
- if (!empty($values['key'])) {
241
- $relevanssi_keys[] = $values['key'];
242
- $relevanssi_dirs[] = $values['dir'];
243
  $relevanssi_compares[] = $values['compare'];
244
  }
245
- } while (!empty($values['key']));
246
-
247
- $primary_key = $relevanssi_keys[0];
248
- if (!isset($data[0]->$primary_key)) return; // trying to sort by a non-existent key
249
 
250
- usort($data, "relevanssi_cmp_function");
 
 
 
 
251
 
252
- return;
253
  }
254
 
255
- function relevanssi_show_matches($data, $hit) {
256
- isset($data['body_matches'][$hit]) ? $body = $data['body_matches'][$hit] : $body = 0;
257
- isset($data['title_matches'][$hit]) ? $title = $data['title_matches'][$hit] : $title = 0;
258
- isset($data['tag_matches'][$hit]) ? $tag = $data['tag_matches'][$hit] : $tag = 0;
259
- isset($data['category_matches'][$hit]) ? $category = $data['category_matches'][$hit] : $category = 0;
260
- isset($data['taxonomy_matches'][$hit]) ? $taxonomy = $data['taxonomy_matches'][$hit] : $taxonomy = 0;
261
- isset($data['comment_matches'][$hit]) ? $comment = $data['comment_matches'][$hit] : $comment = 0;
262
- isset($data['scores'][$hit]) ? $score = round($data['scores'][$hit], 2) : $score = 0;
263
- isset($data['term_hits'][$hit]) ? $term_hits_a = $data['term_hits'][$hit] : $term_hits_a = array();
264
- arsort($term_hits_a);
265
- $term_hits = "";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  $total_hits = 0;
267
- foreach ($term_hits_a as $term => $hits) {
268
- $term_hits .= " $term: $hits";
269
  $total_hits += $hits;
270
  }
271
 
272
- $text = stripslashes(get_option('relevanssi_show_matches_text'));
273
- $replace_these = array("%body%", "%title%", "%tags%", "%categories%", "%taxonomies%", "%comments%", "%score%", "%terms%", "%total%");
274
- $replacements = array($body, $title, $tag, $category, $taxonomy, $comment, $score, $term_hits, $total_hits);
275
-
276
- $result = " " . str_replace($replace_these, $replacements, $text);
277
-
278
- return apply_filters('relevanssi_show_matches', $result);
 
 
 
 
 
 
 
279
  }
280
 
281
- function relevanssi_update_log($query, $hits) {
282
- if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] == "Mediapartners-Google")
283
- return;
284
-
285
- global $wpdb, $relevanssi_variables;
286
-
287
- $user = apply_filters('relevanssi_log_get_user', wp_get_current_user());
288
- if ($user->ID != 0 && get_option('relevanssi_omit_from_logs')) {
289
- $omit = explode(",", get_option('relevanssi_omit_from_logs'));
290
- if (in_array($user->ID, $omit)) return;
291
- if (in_array($user->user_login, $omit)) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  }
293
 
294
- // Bot filter, by Justin_K
295
- // See: http://wordpress.org/support/topic/bot-logging-problem-w-tested-solution
296
- $user_agent = "";
297
- if (isset($_SERVER['HTTP_USER_AGENT'])) {
298
- $user_agent = $_SERVER['HTTP_USER_AGENT'];
299
- $bots = array('Google'=>'Mediapartners-Google');
300
- $bots = apply_filters('relevanssi_bots_to_not_log', $bots);
301
- foreach ($bots as $name => $lookfor) {
302
- if (stristr($user_agent, $lookfor) !== false) return;
303
- }
 
 
 
 
 
 
 
304
  }
305
 
306
- get_option('relevanssi_log_queries_with_ip') == "on" ? $ip = apply_filters('relevanssi_remote_addr', $_SERVER['REMOTE_ADDR']) : $ip = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
 
308
- $ok_to_log = apply_filters('relevanssi_ok_to_log', true, $query, $hits, $user_agent, $ip);
309
- if ($ok_to_log) {
310
- $q = $wpdb->prepare("INSERT INTO " . $relevanssi_variables['log_table'] . " (query, hits, user_id, ip, time) VALUES (%s, %d, %d, %s, NOW())", $query, intval($hits), $user->ID, $ip);
311
- $wpdb->query($q);
312
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  }
314
 
 
 
 
 
 
 
 
 
315
  function relevanssi_trim_logs() {
316
  global $wpdb, $relevanssi_variables;
317
- $interval = get_option('relevanssi_trim_logs');
318
- $query = "DELETE FROM " . $relevanssi_variables['log_table'] . " WHERE time < TIMESTAMP(DATE_SUB(NOW(), INTERVAL $interval DAY))";
319
-
320
- $wpdb->query($query);
 
321
  }
322
 
323
  /**
324
- * Do note that while this function takes $post_ok as a parameter, it actually doesn't care much
325
- * about the previous value, and will instead overwrite it. If you want to make sure your value
326
- * is preserved, either disable this default function, or run your function on a later priority
327
- * (this defaults to 10).
 
 
 
 
 
 
 
 
 
 
 
 
328
  */
329
- function relevanssi_default_post_ok($post_ok, $doc) {
330
- $status = relevanssi_get_post_status($doc);
331
 
332
- // if it's not public, don't show
333
- if ('publish' != $status) {
334
  $post_ok = false;
335
  }
336
 
337
- // ...unless
338
-
339
- if ('private' == $status) {
340
  $post_ok = false;
341
 
342
- if (function_exists('awp_user_can')) {
343
- // Role-Scoper, though Role-Scoper actually uses a different function to do this
344
- // So whatever is in here doesn't actually run.
345
- $current_user = wp_get_current_user();
346
- $post_ok = awp_user_can('read_post', $doc, $current_user->ID);
 
347
  }
348
- else if (defined('GROUPS_CORE_VERSION')) {
349
- // Groups
350
- $current_user = wp_get_current_user();
351
- $post_ok = Groups_Post_Access::user_can_read_post($doc, $current_user->ID);
352
- }
353
- else if (defined('SIMPLE_WP_MEMBERSHIP_VER')) {
354
- // Simple Membership
355
- $logged_in = SwpmMemberUtils::is_member_logged_in();
356
- if (!$logged_in) {
357
- $post_ok = false;
358
- }
359
- else {
360
- $access_ctrl = SwpmAccessControl::get_instance();
361
- $post_ok = $access_ctrl->can_i_read_post($doc);
362
- }
363
  }
364
- else {
365
- // Basic WordPress version
366
- $type = relevanssi_get_post_type($doc);
367
- if (isset($GLOBALS['wp_post_types'][$type]->cap->read_private_posts)) {
368
- $cap = $GLOBALS['wp_post_types'][$type]->cap->read_private_posts;
369
- }
370
- else {
371
- // guessing here
372
- $cap = 'read_private_' . $type . 's';
373
- }
374
- if (current_user_can($cap)) {
375
- $post_ok = true;
376
- }
 
 
 
 
 
 
 
 
 
 
 
377
  }
378
  }
379
 
380
- // only show drafts, pending and future posts in admin search
381
- if (in_array($status, apply_filters('relevanssi_valid_admin_status', array('draft', 'pending', 'future'))) && is_admin()) {
 
 
 
 
 
 
 
 
 
 
382
  $post_ok = true;
383
  }
384
 
385
- if (relevanssi_s2member_level($doc) == 0) $post_ok = false; // not ok with s2member
386
-
387
  return $post_ok;
388
  }
389
 
390
  /**
391
- * This is only for legacy use, current versions of s2member do not need this anymore, they filter through pre_get_posts and 'not_in'.
392
- *
393
- * Return values:
394
- * 2: full access to post
395
- * 1: show title only
396
- * 0: no access to post
397
- * -1: s2member not active
 
 
 
 
 
398
  */
399
- function relevanssi_s2member_level($doc) {
400
- $return = -1;
401
- if (function_exists('is_permitted_by_s2member')) {
402
- // s2member
403
- $alt_view_protect = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["filter_wp_query"];
404
-
405
- if (version_compare (WS_PLUGIN__S2MEMBER_VERSION, "110912", ">="))
406
- $completely_hide_protected_search_results = (in_array ("all", $alt_view_protect) || in_array ("searches", $alt_view_protect)) ? true : false;
407
- else /* Backward compatibility with versions of s2Member, prior to v110912. */
408
- $completely_hide_protected_search_results = (strpos ($alt_view_protect, "all") !== false || strpos ($alt_view_protect, "searches") !== false) ? true : false;
409
-
410
- if (is_permitted_by_s2member($doc)) {
411
- // Show title and excerpt, even full content if you like.
412
- $return = 2;
413
- }
414
- else if (!is_permitted_by_s2member($doc) && $completely_hide_protected_search_results === false) {
415
- // Show title and excerpt. Alt View Protection is NOT enabled for search results. However, do NOT show full content body.
416
- $return = 1;
417
- }
418
- else {
419
- // Hide this search result completely.
420
- $return = 0;
421
- }
422
- }
423
-
424
- return $return;
425
- }
426
-
427
- function relevanssi_populate_array($matches) {
428
  global $relevanssi_post_array, $relevanssi_post_types, $wpdb;
429
- if (function_exists('wp_suspend_cache_addition'))
430
- wp_suspend_cache_addition(true);
 
431
 
432
  $ids = array();
433
- foreach ($matches as $match) {
434
- array_push($ids, $match->doc);
435
  }
436
 
437
- $ids = implode(',', $ids);
438
- $posts = $wpdb->get_results("SELECT * FROM $wpdb->posts WHERE id IN ($ids)");
439
- foreach ($posts as $post) {
440
- $relevanssi_post_array[$post->ID] = $post;
441
- $relevanssi_post_types[$post->ID] = $post->post_type;
 
442
  }
443
 
444
- if (function_exists('wp_suspend_cache_addition'))
445
- wp_suspend_cache_addition(false);
446
  }
447
 
448
- function relevanssi_get_term_taxonomy($id) {
 
 
 
 
 
 
 
 
 
 
 
449
  global $wpdb;
450
- $taxonomy = $wpdb->get_var("SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = $id");
451
  return $taxonomy;
452
  }
453
 
454
  /**
455
- * Extracts phrases from search query
456
- * Returns an array of phrases
 
 
 
 
 
457
  */
458
- function relevanssi_extract_phrases($q) {
459
- function_exists( 'mb_strpos' ) ? $strpos_function = "mb_strpos" : $strpos_function = "strpos";
460
- function_exists( 'mb_substr' ) ? $substr_function = "mb_substr" : $substr_function = "substr";
 
 
 
 
 
 
461
 
462
- $pos = call_user_func($strpos_function, $q, '"');
463
 
464
  $phrases = array();
465
- while ($pos !== false) {
466
  $start = $pos;
467
- $end = call_user_func($strpos_function, $q, '"', $start + 1);
468
 
469
- if ($end === false) {
470
- // just one " in the query
471
  $pos = $end;
472
  continue;
473
  }
474
- $phrase = call_user_func($substr_function, $q, $start + 1, $end - $start - 1);
475
- $phrase = trim($phrase);
476
 
477
- // Do not count single-word phrases as phrases
478
- if (!empty($phrase) && str_word_count($phrase) > 1) $phrases[] = $phrase;
 
 
479
  $pos = $end;
480
  }
481
 
482
  return $phrases;
483
  }
484
 
485
- /* If no phrase hits are made, this function returns an empty string
486
- * If phrase matches are found, the function returns MySQL queries
 
 
 
 
 
 
 
 
 
 
 
487
  */
488
- function relevanssi_recognize_phrases($q) {
489
  global $wpdb;
490
 
491
- $phrases = relevanssi_extract_phrases($q);
492
 
493
  $all_queries = array();
494
- if (count($phrases) > 0) {
495
- foreach ($phrases as $phrase) {
496
  $queries = array();
497
- $phrase = str_replace("", '_', $phrase);
498
- $phrase = str_replace("", '_', $phrase);
499
- $phrase = str_replace("'", '_', $phrase);
500
- $phrase = str_replace('"', '_', $phrase);
501
- $phrase = str_replace("", '_', $phrase);
502
- $phrase = str_replace("", '_', $phrase);
503
- $phrase = str_replace("", '_', $phrase);
504
- $phrase = str_replace("´", '_', $phrase);
505
- $phrase = $wpdb->esc_like($phrase);
506
- $phrase = esc_sql($phrase);
507
- "on" == get_option("relevanssi_index_excerpt") ? $excerpt = " OR post_excerpt LIKE '%$phrase%'" : $excerpt = "";
 
 
 
 
508
  $query = "(SELECT ID FROM $wpdb->posts
509
  WHERE (post_content LIKE '%$phrase%' OR post_title LIKE '%$phrase%' $excerpt)
510
  AND post_status IN ('publish', 'draft', 'private', 'pending', 'future', 'inherit'))";
@@ -525,22 +890,30 @@ function relevanssi_recognize_phrases($q) {
525
 
526
  $queries[] = $query;
527
 
528
- $queries = implode(' OR relevanssi.doc IN ', $queries);
529
- $queries = "AND (relevanssi.doc IN $queries)";
530
  $all_queries[] = $queries;
531
  }
532
- }
533
- else {
534
- $phrases = "";
535
  }
536
 
537
- $all_queries = implode(" ", $all_queries);
538
 
539
  return $all_queries;
540
  }
541
 
542
- // found here: http://forums.digitalpoint.com/showthread.php?t=1106745
543
- function relevanssi_strip_invisibles($text) {
 
 
 
 
 
 
 
 
 
544
  $text = preg_replace(
545
  array(
546
  '@<style[^>]*?>.*?</style>@siu',
@@ -553,611 +926,1032 @@ function relevanssi_strip_invisibles($text) {
553
  '@<iframe[^>]*?.*?</iframe>@siu',
554
  '@<del[^>]*?.*?</del>@siu',
555
  ),
556
- ' ',
557
- $text );
558
  return $text;
559
  }
560
 
561
- function relevanssi_strlen_sort($a, $b) {
562
- return relevanssi_strlen($b) - relevanssi_strlen($a);
 
 
 
 
 
 
 
 
 
 
 
 
563
  }
564
 
 
 
 
 
 
 
 
 
565
  function relevanssi_get_custom_fields() {
566
- $custom_fields = get_option("relevanssi_index_fields");
567
- if ($custom_fields) {
568
- if ($custom_fields == 'all') {
569
  return $custom_fields;
570
- }
571
- else if ($custom_fields == 'visible') {
572
  return $custom_fields;
573
- }
574
- else {
575
- $custom_fields = explode(",", $custom_fields);
576
- for ($i = 0; $i < count($custom_fields); $i++) {
577
- $custom_fields[$i] = trim($custom_fields[$i]);
578
  }
579
  }
580
- }
581
- else {
582
  $custom_fields = false;
583
  }
584
  return $custom_fields;
585
  }
586
 
587
- function relevanssi_mb_trim($string) {
588
- $string = str_replace(chr(194) . chr(160), '', $string);
589
- $string = preg_replace( "/(^\s+)|(\s+$)/us", "", $string );
590
- return $string;
 
 
 
 
 
 
 
 
 
591
  }
592
 
593
- function relevanssi_remove_punct($a) {
594
- if (!is_string($a)) return ""; // In case something sends a non-string here.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
595
 
596
- $a = preg_replace ('/<[^>]*>/', ' ', $a);
597
 
598
- $punct_options = get_option('relevanssi_punctuation');
599
 
600
- $hyphen_replacement = " ";
601
- $endash_replacement = " ";
602
- $emdash_replacement = " ";
603
- if (isset($punct_options['hyphens']) && $punct_options['hyphens'] === "remove") {
604
- $hyphen_replacement = "";
605
- $endash_replacement = "";
606
- $emdash_replacement = "";
607
  }
608
- if (isset($punct_options['hyphens']) && $punct_options['hyphens'] === "keep") {
609
- $hyphen_replacement = "HYPHENTAIKASANA";
610
- $endash_replacement = "ENDASHTAIKASANA";
611
- $emdash_replacement = "EMDASHTAIKASANA";
612
  }
613
 
614
- $quote_replacement = " ";
615
- if (isset($punct_options['quote']) && $punct_options['quotes'] === "remove") $quote_replacement = "";
 
 
616
 
617
- $ampersand_replacement = " ";
618
- if (isset($punct_options['ampersands']) && $punct_options['ampersands'] === "remove") {
619
- $ampersand_replacement = "";
620
  }
621
- if (isset($punct_options['ampersands']) && $punct_options['ampersands'] === "keep") {
622
- $ampersand_replacement = "AMPERSANDTAIKASANA";
 
 
 
 
 
 
 
 
623
  }
624
 
625
  $replacement_array = array(
626
- "ß" => 'ss',
627
- "·" => '',
628
- "" => '',
629
- "" => '',
630
- "®" => '',
631
- "©" => '',
632
- "&shy;" => '',
633
- "&nbsp;" => ' ',
634
- '&#8217;' => ' ',
635
- chr(194) . chr(160) => ' ',
636
- "×" => ' ',
637
- "'" => $quote_replacement,
638
- "" => $quote_replacement,
639
- "" => $quote_replacement,
640
- "" => $quote_replacement,
641
- "" => $quote_replacement,
642
- "" => $quote_replacement,
643
- "´" => $quote_replacement,
644
- "-" => $hyphen_replacement,
645
- "" => $endash_replacement,
646
- "" => $emdash_replacement,
647
- "&amp;" => $ampersand_replacement,
648
- "&" => $ampersand_replacement,
 
649
  );
650
 
651
- $replacement_array = apply_filters('relevanssi_punctuation_filter', $replacement_array);
652
-
653
- $a = str_replace("\r", ' ', $a); // --- replace with empty space
654
- $a = str_replace("\n", ' ', $a); // --- replace with space
655
- $a = str_replace("\t", ' ', $a); // --- replace with space
656
-
657
- $a = stripslashes($a);
658
-
659
- $a = str_replace(array_keys($replacement_array), array_values($replacement_array), $a);
660
-
661
- $a = preg_replace('/[[:punct:]]+/u', apply_filters('relevanssi_default_punctuation_replacement', ' '), $a);
662
-
663
- $a = preg_replace('/[[:space:]]+/', ' ', $a);
664
-
665
- $a = str_replace('AMPERSANDTAIKASANA', '&', $a);
666
- $a = str_replace('HYPHENTAIKASANA', '-', $a);
667
- $a = str_replace('ENDASHTAIKASANA', '–', $a);
668
- $a = str_replace('EMDASHTAIKASANA', '—', $a);
669
-
670
- $a = trim($a);
671
- return $a;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
672
  }
673
 
674
 
675
  /**
676
- * This function will prevent the default search from running, when Relevanssi is
677
- * active.
678
- * Thanks to John Blackbourne.
 
 
 
 
 
 
 
 
 
679
  */
680
  function relevanssi_prevent_default_request( $request, $query ) {
681
- if ($query->is_search) {
682
- global $wpdb;
683
- if (isset($query->query_vars['post_type']) && isset($query->query_vars['post_status'])) {
684
- if ($query->query_vars['post_type'] == 'attachment' && $query->query_vars['post_status'] == 'inherit,private') {
685
- // this is a media library search; do not meddle
686
- return $request;
 
 
 
 
 
 
 
 
 
 
687
  }
688
  }
689
- $bbpress = false;
690
- if ($query->query_vars['post_type'] == 'topic' || $query->query_vars['post_type'] == 'reply') $bbpress = true;
691
- if (is_array($query->query_vars['post_type'])) {
692
- if (in_array('topic', $query->query_vars['post_type'])) $bbpress = true;
693
- if (in_array('reply', $query->query_vars['post_type'])) $bbpress = true;
694
  }
695
- if ($bbpress) {
696
- // this is a BBPress search; do not meddle
697
  return $request;
698
  }
 
699
  $admin_search_ok = true;
700
- $admin_search_ok = apply_filters('relevanssi_admin_search_ok', $admin_search_ok, $query );
 
 
 
 
 
 
 
 
701
 
702
  $prevent = true;
703
- $prevent = apply_filters('relevanssi_prevent_default_request', $prevent, $query );
704
-
705
- if (empty($query->query_vars['s'])) {
706
- $prevent = false;
 
 
 
 
 
 
 
 
 
707
  $admin_search_ok = false;
708
  }
709
 
710
- if ( $query->is_admin && defined( 'DOING_AJAX' ) && DOING_AJAX ) {
711
- $prevent = false;
712
  $admin_search_ok = false;
713
  }
714
 
715
- if ( $query->is_admin && $query->query_vars['post_type'] == 'page') {
716
- $prevent = false;
 
717
  $admin_search_ok = false;
718
  }
719
 
720
- if (!is_admin() && $prevent )
 
 
721
  $request = "SELECT * FROM $wpdb->posts WHERE 1=2";
722
- else if ('on' == get_option('relevanssi_admin_search') && $admin_search_ok )
723
  $request = "SELECT * FROM $wpdb->posts WHERE 1=2";
 
724
  }
725
  return $request;
726
  }
727
 
728
- function relevanssi_tokenize($str, $remove_stops = true, $min_word_length = -1) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
729
  $tokens = array();
730
- if (is_array($str)) {
731
- foreach ($str as $part) {
732
- $tokens = array_merge($tokens, relevanssi_tokenize($part, $remove_stops, $min_word_length));
 
 
 
733
  }
 
 
 
734
  }
735
- if (is_array($str)) return $tokens;
736
 
737
- if ( function_exists('mb_internal_encoding') )
738
- mb_internal_encoding("UTF-8");
 
739
 
740
- if ($remove_stops) {
 
741
  $stopword_list = relevanssi_fetch_stopwords();
742
  }
743
 
744
- if (function_exists('relevanssi_thousandsep')) {
745
- $str = relevanssi_thousandsep($str);
 
746
  }
747
 
748
- $str = apply_filters('relevanssi_remove_punctuation', $str);
749
-
750
- if ( function_exists('mb_strtolower') )
751
- $str = mb_strtolower($str);
752
- else
753
- $str = strtolower($str);
754
-
755
- $t = strtok($str, "\n\t ");
756
- while ($t !== false) {
757
- $t = strval($t);
 
 
 
 
 
758
  $accept = true;
759
 
760
- if (relevanssi_strlen($t) < $min_word_length) {
761
- $t = strtok("\n\t ");
762
  continue;
763
  }
764
- if ($remove_stops == false) {
765
- $accept = true;
766
- }
767
- else {
768
- if (count($stopword_list) > 0) { //added by OdditY -> got warning when stopwords table was empty
769
- if (in_array($t, $stopword_list)) {
770
- $accept = false;
771
- }
772
- }
773
  }
774
 
775
- if (RELEVANSSI_PREMIUM) {
776
- $t = apply_filters('relevanssi_premium_tokenizer', $t);
 
 
 
 
 
 
 
 
777
  }
778
 
779
- if ($accept) {
780
- $t = relevanssi_mb_trim($t);
781
- if (is_numeric($t)) $t = " $t"; // $t ends up as an array index, and numbers just don't work there
782
- if (!isset($tokens[$t])) {
783
- $tokens[$t] = 1;
784
  }
785
- else {
786
- $tokens[$t]++;
 
 
787
  }
788
  }
789
 
790
- $t = strtok("\n\t ");
791
  }
 
792
  return $tokens;
793
  }
794
 
795
- function relevanssi_get_post_status($id) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
796
  global $relevanssi_post_array;
797
-
798
- $type = substr($id, 0, 2);
799
- if ($type == '**') {
800
- return 'publish';
801
- }
802
- if ($type == 'u_') {
803
  return 'publish';
804
  }
805
 
806
- if (isset($relevanssi_post_array[$id])) {
807
- $status = $relevanssi_post_array[$id]->post_status;
808
- if ('inherit' == $status) {
809
- $parent = $relevanssi_post_array[$id]->post_parent;
810
- $status = relevanssi_get_post_status($parent);
811
- if ($status == false) {
812
- // attachment without a parent
813
- // let's assume it's public
814
  $status = 'publish';
 
 
815
  }
816
  }
817
  return $status;
818
- }
819
- else {
820
- return get_post_status($id);
 
 
 
 
 
 
821
  }
822
  }
823
 
824
- function relevanssi_get_post_type($id) {
 
 
 
 
 
 
 
 
 
 
 
 
825
  global $relevanssi_post_array;
826
 
827
- if (isset($relevanssi_post_array[$id])) {
828
- return $relevanssi_post_array[$id]->post_type;
829
- }
830
- else {
831
- return get_post_type($id);
 
 
 
 
832
  }
833
  }
834
 
835
- function relevanssi_the_tags($sep = ', ', $echo = true) {
836
- $tags = relevanssi_highlight_terms(get_the_tag_list('', $sep), get_search_query());
837
- if ($echo) {
838
- echo $tags;
839
- }
840
- else {
 
 
 
 
 
 
 
 
 
 
841
  return $tags;
842
  }
843
  }
844
 
845
- function relevanssi_get_the_tags($sep = ', ') {
846
- return relevanssi_the_tags($sep, false);
 
 
 
 
 
 
 
 
 
 
847
  }
848
 
849
- function relevanssi_get_term_tax_id($field, $id, $taxonomy) {
 
 
 
 
 
 
 
 
 
 
850
  global $wpdb;
851
- return $wpdb->get_var(
852
- "SELECT term_taxonomy_id
853
- FROM $wpdb->term_taxonomy
854
- WHERE term_id = $id AND taxonomy = '$taxonomy'");
855
  }
856
 
857
  /**
858
- * Takes in a search query, returns it with synonyms added.
 
 
 
 
 
 
859
  */
860
- function relevanssi_add_synonyms($q) {
861
- if (empty($q)) return $q;
862
-
863
- $synonym_data = get_option('relevanssi_synonyms');
864
- if ($synonym_data) {
865
- $synonyms = array();
866
- if (function_exists('mb_strtolower')) {
867
- $synonym_data = mb_strtolower($synonym_data);
868
- }
869
- else {
870
- $synonym_data = strtolower($synonym_data);
871
- }
872
- $pairs = explode(";", $synonym_data);
873
- foreach ($pairs as $pair) {
874
- if (empty($pair)) continue; // skip empty rows
875
- $parts = explode("=", $pair);
876
- $key = strval(trim($parts[0]));
877
- $value = trim($parts[1]);
878
- $synonyms[$key][$value] = true;
 
 
879
  }
880
- if (count($synonyms) > 0) {
 
881
  $new_terms = array();
882
- $terms = array_keys(relevanssi_tokenize($q, false)); // remove stopwords is false here
883
- if (!in_array($q, $terms)) $terms[] = $q;
884
-
885
- foreach ($terms as $term) {
886
- $term = trim($term);
887
- if (in_array(strval($term), array_keys($synonyms))) { // strval, otherwise numbers cause problems
888
- if (isset($synonyms[strval($term)])) { // necessary, otherwise terms like "02" can cause problems
889
- $new_terms = array_merge($new_terms, array_keys($synonyms[strval($term)]));
 
 
 
890
  }
891
  }
892
  }
893
- if (count($new_terms) > 0) {
894
- foreach ($new_terms as $new_term) {
895
- $q .= " $new_term";
896
  }
897
  }
898
  }
899
  }
900
- return $q;
901
  }
902
 
903
- /* Helper function that does mb_stripos, falls back to mb_strpos and mb_strtoupper
904
- * if that cannot be found, and falls back to just strpos if even that is not possible.
 
 
 
 
 
 
 
 
 
 
 
905
  */
906
- function relevanssi_stripos($content, $term, $offset = 0) {
907
- if ($offset > relevanssi_strlen($content)) return false;
908
-
909
- if (function_exists('mb_stripos')) {
910
- $pos = ("" == $content) ? false : mb_stripos($content, $term, $offset);
911
  }
912
- else if (function_exists('mb_strpos') && function_exists('mb_strtoupper') && function_exists('mb_substr')) {
913
- $pos = mb_strpos(mb_strtoupper($content), mb_strtoupper($term), $offset);
914
- }
915
- else {
916
- $pos = strpos(strtoupper($content), strtoupper($term), $offset);
 
 
 
 
 
 
917
  }
918
  return $pos;
919
  }
920
 
921
- /* Function to close tags in a bit of HTML code. Used to make sure no tags are left open
922
- * in excerpts. This method is not foolproof, but it's good enough for now.
 
 
 
 
 
 
 
923
  */
924
- function relevanssi_close_tags($html) {
925
- preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
926
- $openedtags = $result[1];
927
- preg_match_all('#</([a-z]+)>#iU', $html, $result);
928
- $closedtags = $result[1];
929
- $len_opened = count($openedtags);
930
- if (count($closedtags) == $len_opened) {
931
- return $html;
932
- }
933
- $openedtags = array_reverse($openedtags);
934
- for ($i=0; $i < $len_opened; $i++) {
935
- if (!in_array($openedtags[$i], $closedtags)) {
936
- $html .= '</'.$openedtags[$i].'>';
937
- } else {
938
- unset($closedtags[array_search($openedtags[$i], $closedtags)]);
939
- }
940
- }
941
- return $html;
942
  }
943
 
944
- /* Prints out post title with highlighting.
 
 
 
 
 
 
 
 
 
 
945
  */
946
- function relevanssi_the_title($echo = true) {
947
  global $post;
948
- if (empty($post->post_highlighted_title)) $post->post_highlighted_title = $post->post_title;
949
- if ($echo) echo $post->post_highlighted_title;
 
 
 
 
950
  return $post->post_highlighted_title;
951
  }
952
 
953
- /* Returns the post title with highlighting.
 
 
 
 
 
 
 
954
  */
955
- function relevanssi_get_the_title($post_id) {
956
- $post = relevanssi_get_post($post_id);
957
- if (empty($post->post_highlighted_title)) $post->post_highlighted_title = $post->post_title;
 
 
 
 
 
958
  return $post->post_highlighted_title;
959
  }
960
 
961
- function relevanssi_update_doc_count( $values, $post ) {
 
 
 
 
 
 
 
 
 
 
962
  global $wpdb, $relevanssi_variables;
963
- $relevanssi_table = $relevanssi_variables['relevanssi_table'];
964
- $D = $wpdb->get_var("SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi");
965
- update_option( 'relevanssi_doc_count', $D);
966
- return $values;
967
  }
968
 
969
- /* Uses mb_strlen() if available, otherwise falls back to strlen().
970
- */
971
- function relevanssi_strlen($s) {
972
- if ( function_exists( 'mb_strlen' ) ) return mb_strlen( $s );
 
 
 
 
 
 
 
 
 
973
  return strlen( $s );
974
  }
975
 
976
- /* If WP_CLI is available, print out the debug notice as a WP_CLI::log(), otherwise
 
 
 
977
  * just echo.
 
 
978
  */
979
- function relevanssi_debug_echo($s) {
980
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
981
- WP_CLI::log($s);
982
- }
983
- else {
984
- echo $s . "\n";
985
  }
986
  }
987
 
988
- function get_Relevanssi_Taxonomy_Walker() {
989
- if (!class_exists("Relevanssi_Taxonomy_Walker")) {
990
- class Relevanssi_Taxonomy_Walker extends Walker_Category_Checklist {
991
- public $name;
992
-
993
- public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
994
- if ( empty( $args['taxonomy'] ) ) {
995
- $taxonomy = 'category';
996
- } else {
997
- $taxonomy = $args['taxonomy'];
998
- }
999
-
1000
- $name = $this->name;
1001
-
1002
- $args['popular_cats'] = empty( $args['popular_cats'] ) ? array() : $args['popular_cats'];
1003
- $class = in_array( $category->term_id, $args['popular_cats'] ) ? ' class="popular-category"' : '';
1004
-
1005
- $args['selected_cats'] = empty( $args['selected_cats'] ) ? array() : $args['selected_cats'];
1006
-
1007
- if ( ! empty( $args['list_only'] ) ) {
1008
- $aria_checked = 'false';
1009
- $inner_class = 'category';
1010
-
1011
- if ( in_array( $category->term_id, $args['selected_cats'] ) ) {
1012
- $inner_class .= ' selected';
1013
- $aria_checked = 'true';
1014
- }
1015
-
1016
- /** This filter is documented in wp-includes/category-template.php */
1017
- $output .= "\n" . '<li' . $class . '>' .
1018
- '<div class="' . $inner_class . '" data-term-id=' . $category->term_id .
1019
- ' tabindex="0" role="checkbox" aria-checked="' . $aria_checked . '">' .
1020
- esc_html( apply_filters( 'the_category', $category->name ) ) . '</div>';
1021
- } else {
1022
- /** This filter is documented in wp-includes/category-template.php */
1023
- $output .= "\n<li id='{$taxonomy}-{$category->term_id}'$class>" .
1024
- '<label class="selectit"><input value="' . $category->term_id . '" type="checkbox" name="'.$name.'[]" id="in-'.$taxonomy.'-' . $category->term_id . '"' .
1025
- checked( in_array( $category->term_id, $args['selected_cats'] ), true, false ) .
1026
- disabled( empty( $args['disabled'] ), false, false ) . ' /> ' .
1027
- esc_html( apply_filters( 'the_category', $category->name ) ) . '</label>';
1028
- }
1029
- }
1030
- }
1031
- }
1032
-
1033
- return new Relevanssi_Taxonomy_Walker;
1034
  }
1035
 
1036
- // Thanks to Teemu Muikku
1037
- add_action('switch_blog', 'relevanssi_switch_blog', 1, 2 );
1038
- function relevanssi_switch_blog($new_blog, $prev_blog) {
1039
- global $relevanssi_variables, $wpdb;
 
 
 
 
 
 
 
 
 
 
 
 
1040
 
1041
- if (!isset($relevanssi_variables) || !isset($relevanssi_variables['relevanssi_table'] ))
1042
- return;
 
1043
 
1044
- $relevanssi_variables['relevanssi_table'] = $wpdb->prefix . "relevanssi";
1045
- $relevanssi_variables['stopword_table'] = $wpdb->prefix . "relevanssi_stopwords";
1046
- $relevanssi_variables['log_table'] = $wpdb->prefix . "relevanssi_log";
1047
  }
1048
 
1049
- function relevanssi_get_permalink() {
1050
- $permalink = apply_filters('relevanssi_permalink', get_permalink());
1051
- $highlight_docs = get_option('relevanssi_highlight_docs');
1052
- $query = get_search_query();
1053
- if (isset($highlight_docs) && $highlight_docs != "off" && !empty($query)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1054
  global $post;
1055
- $frontpage_id = get_option('page_on_front');
1056
- if ($post->ID != $frontpage_id) {
1057
- // We won't add the highlight parameter for the front page, as that will break the link
1058
- $permalink = esc_attr(add_query_arg(array(
1059
- 'highlight' => urlencode(get_search_query())
1060
- ), $permalink )
1061
  );
1062
  }
1063
  }
1064
  return $permalink;
1065
  }
1066
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1067
  function relevanssi_the_permalink() {
1068
- echo relevanssi_get_permalink();
1069
  }
1070
 
1071
- function relevanssi_permalink($content, $link_post = NULL) {
1072
- if ($link_post == NULL) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
  global $post;
1074
- if (isset($post->link))
1075
- $content = $post->link;
1076
  }
1077
- return $content;
 
 
 
 
 
1078
  }
1079
 
1080
- function relevanssi_didyoumean($query, $pre, $post, $n = 5, $echo = true) {
1081
- if (function_exists('relevanssi_premium_didyoumean')) {
1082
- $result = relevanssi_premium_didyoumean($query, $pre, $post, $n);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1083
  }
1084
- else {
1085
- $result = relevanssi_simple_didyoumean($query, $pre, $post, $n);
 
1086
  }
1087
-
1088
- if ($echo) echo $result;
1089
 
1090
  return $result;
1091
  }
1092
 
1093
- function relevanssi_simple_didyoumean($query, $pre, $post, $n = 5) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1094
  global $wpdb, $relevanssi_variables, $wp_query;
1095
 
1096
  $total_results = $wp_query->found_posts;
1097
 
1098
- if ($total_results > $n) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1099
 
1100
- $q = "SELECT query, count(query) as c, AVG(hits) as a FROM " . $relevanssi_variables['log_table'] . " WHERE hits > 1 GROUP BY query ORDER BY count(query) DESC";
1101
- $q = apply_filters('relevanssi_didyoumean_query', $q);
1102
 
1103
- $data = $wpdb->get_results($q);
 
 
 
 
1104
 
1105
- $query = htmlspecialchars_decode($query);
1106
- $tokens = relevanssi_tokenize($query);
1107
- $suggestion = "";
1108
  $suggestions_made = false;
1109
- foreach ($tokens as $token => $count) {
1110
- $closest = "";
 
 
1111
  $distance = -1;
1112
- foreach ($data as $row) {
1113
- if ($row->c < 2) break;
1114
-
1115
- if ($token === $row->query) {
1116
- $closest = "";
1117
  break;
1118
  }
1119
- else {
1120
- $lev = levenshtein($token, $row->query);
1121
 
1122
- if ($lev < $distance || $distance < 0) {
1123
- if ($row->a > 0) {
 
 
 
 
 
 
1124
  $distance = $lev;
1125
- $closest = $row->query;
1126
- if ($lev < 2) break; // get the first with distance of 1 and go
 
 
1127
  }
1128
  }
1129
  }
1130
  }
1131
- if (!empty($closest)) {
1132
- $query = str_ireplace($token, $closest, $query);
1133
  $suggestions_made = true;
1134
- }
1135
  }
1136
 
1137
- if ($suggestions_made) {
1138
  $suggestion = $query;
1139
- $suggestion_enc = urlencode($suggestion);
1140
  }
1141
 
1142
- $result = null;
1143
- if ($suggestion) {
1144
- $url = get_bloginfo('url');
1145
- $url = esc_attr(add_query_arg(array(
1146
- 's' => urlencode($suggestion)
1147
-
1148
- ), $url ));
1149
- $url = apply_filters('relevanssi_didyoumean_url', $url, $query, $suggestion);
1150
-
1151
- // Escape the suggestion to avoid XSS attacks
1152
- $suggestion = htmlspecialchars($suggestion);
1153
-
1154
- $result = apply_filters('relevanssi_didyoumean_suggestion', "$pre<a href='$url'>$suggestion</a>$post");
1155
- }
1156
-
1157
- return $result;
1158
  }
1159
 
1160
- function relevanssi_wpmu_drop($tables) {
 
 
 
 
 
 
 
 
 
 
 
 
 
1161
  global $relevanssi_variables;
1162
  $tables[] = $relevanssi_variables['relevanssi_table'];
1163
  $tables[] = $relevanssi_variables['stopword_table'];
@@ -1165,16 +1959,45 @@ function relevanssi_wpmu_drop($tables) {
1165
  return $tables;
1166
  }
1167
 
1168
- function relevanssi_get_post($id) {
1169
- if (function_exists('relevanssi_premium_get_post')) return relevanssi_premium_get_post($id);
1170
-
 
 
 
 
 
 
 
 
 
 
 
 
 
1171
  global $relevanssi_post_array;
1172
 
1173
- if (isset($relevanssi_post_array[$id])) {
1174
- $post = $relevanssi_post_array[$id];
1175
- }
1176
- else {
1177
- $post = get_post($id);
 
1178
  }
1179
  return $post;
1180
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ /**
3
+ * /lib/common.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
 
11
+ /**
12
+ * Filters posts based on WPML language.
13
+ *
14
+ * Attaches to 'relevanssi_hits_filter' to restrict WPML searches to the current
15
+ * language. Whether this filter is used or not depends on the option
16
+ * 'relevanssi_wpml_only_current'. Thanks to rvencu for the initial code.
17
+ *
18
+ * @global object $sitepress The WPML global object.
19
+ *
20
+ * @param array $data Index 0 has the array of results, index 1 has the search query.
21
+ *
22
+ * @return array $data The whole parameter array, with the filtered posts in the index 0.
23
+ */
24
+ function relevanssi_wpml_filter( $data ) {
25
+ $filter_enabled = get_option( 'relevanssi_wpml_only_current' );
26
+ if ( 'on' === $filter_enabled ) {
27
+ $current_blog_language = get_bloginfo( 'language' );
28
+ $filtered_hits = array();
29
+ foreach ( $data[0] as $hit ) {
30
+ if ( is_integer( $hit ) ) {
31
+ // In case "fields" is set to "ids", fetch the post object we need.
32
+ $hit = get_post( $hit );
33
  }
 
34
 
35
+ if ( isset( $hit->blog_id ) ) {
36
+ // This is a multisite search.
37
+ switch_to_blog( $hit->blog_id );
38
+ if ( function_exists( 'icl_object_id' ) ) {
39
+ // Reset the WPML cache when blog is switched, otherwise WPML
40
+ // will be confused.
41
+ global $wpml_post_translations;
42
+ $wpml_post_translations->reload();
43
  }
44
+ }
45
+
46
+ global $sitepress;
47
+
48
+ // Check if WPML is used.
49
+ if ( function_exists( 'icl_object_id' ) && ! function_exists( 'pll_is_translated_post_type' ) ) {
50
+ if ( $sitepress->is_translated_post_type( $hit->post_type ) ) {
51
+ $id = apply_filters( 'wpml_object_id', $hit->ID, $hit->post_type, false );
52
+ // This is a post in a translated post type.
53
+ if ( $id === $hit->ID ) {
54
+ // The post exists in the current language, and can be included.
55
+ $filtered_hits[] = $hit;
56
+ }
57
+ } else {
58
+ // This is not a translated post type, so include all posts.
59
  $filtered_hits[] = $hit;
60
  }
61
+ } elseif ( get_bloginfo( 'language' ) === $current_blog_language ) {
62
+ // If there is no WPML but the target blog has identical language with current blog,
63
+ // we use the hits. Note en-US is not identical to en-GB!
 
64
  $filtered_hits[] = $hit;
65
  }
66
+
67
+ if ( isset( $hit->blog_id ) ) {
68
  restore_current_blog();
69
  }
70
  }
71
+
72
+ return array( $filtered_hits, $data[1] );
73
  }
74
+
75
  return $data;
76
  }
77
 
78
+ /**
79
+ * Removes the Polylang language filters.
80
+ *
81
+ * If the Polylang allow all option is enabled ('relevanssi_polylang_all_languages'),
82
+ * removes the Polylang language filter. By default Polylang filters the languages
83
+ * using a taxonomy query.
84
+ *
85
+ * @param object $query WP_Query object we need to clean up.
86
  */
87
+ function relevanssi_polylang_filter( $query ) {
88
+ $polylang_allow_all = get_option( 'relevanssi_polylang_all_languages' );
89
+ if ( 'on' === $polylang_allow_all ) {
90
  $ok_queries = array();
91
 
92
+ if ( ! isset( $query->tax_query ) ) {
93
+ // No tax query set, backing off.
94
+ return;
95
+ }
96
+
97
+ if ( ! isset( $query->tax_query->queries ) || ! is_array( $query->tax_query->queries ) ) {
98
+ // No tax query set, backing off.
99
+ return;
100
+ }
101
+
102
+ foreach ( $query->tax_query->queries as $tax_query ) {
103
+ if ( 'language' !== $tax_query['taxonomy'] ) {
104
+ // Not a language tax query.
105
+ $ok_queries[] = $tax_query;
106
+ }
107
  }
108
  $query->tax_query->queries = $ok_queries;
109
+
110
+ if ( isset( $query->query_vars['tax_query'] ) ) {
111
+ // Tax queries can be here as well, so let's sweep this one too.
112
  $ok_queries = array();
113
+ foreach ( $query->query_vars['tax_query'] as $tax_query ) {
114
+ if ( 'language' !== $tax_query['taxonomy'] ) {
115
+ $ok_queries[] = $tax_query;
116
+ }
117
  }
118
  $query->query_vars['tax_query'] = $ok_queries;
119
  }
120
 
121
+ if ( isset( $query->query_vars['taxonomy'] ) && 'language' === $query->query_vars['taxonomy'] ) {
122
+ // Another way to set the taxonomy.
123
+ unset( $query->query_vars['taxonomy'] );
124
+ unset( $query->query_vars['term'] );
125
  }
126
  }
127
 
128
  return $query;
129
  }
130
 
131
+ /**
132
+ * Gets the next key-direction pair from the orderby array.
133
+ *
134
+ * Fetches a key-direction pair from the orderby array. Converts key names to match
135
+ * the post object parameters when necessary and seeds the random generator, if
136
+ * required.
137
+ *
138
+ * @param array $orderby An array of key-direction pairs.
139
+ *
140
+ * @return array A set of 'key', 'dir' for direction and 'compare' for proper
141
+ * comparison method.
142
+ */
143
+ function relevanssi_get_next_key( &$orderby ) {
144
+ if ( ! is_array( $orderby ) || count( $orderby ) < 1 ) {
145
+ // Nothing to see here.
146
+ return array(
147
+ 'key' => null,
148
+ 'dir' => null,
149
+ 'compare' => null,
150
+ );
151
+ }
152
+
153
+ list( $key ) = array_keys( $orderby );
154
+ $dir = $orderby[ $key ];
155
+ unset( $orderby[ $key ] );
156
+
157
+ if ( 'rand' === strtolower( $dir ) ) {
158
+ $key = 'rand';
159
+ }
160
+
161
+ // Correcting the key for couple of shorthand cases.
162
+ switch ( $key ) {
163
+ case 'title':
164
+ $key = 'post_title';
165
+ break;
166
+ case 'date':
167
+ $key = 'post_date';
168
+ break;
169
+ case 'modified':
170
+ $key = 'post_modified';
171
+ break;
172
+ case 'parent':
173
+ $key = 'post_parent';
174
+ break;
175
+ case 'type':
176
+ $key = 'post_type';
177
+ break;
178
+ case 'name':
179
+ $key = 'post_name';
180
+ break;
181
+ case 'author':
182
+ $key = 'post_author';
183
+ break;
184
+ case 'relevance':
185
+ $key = 'relevance_score';
186
+ break;
187
+ }
188
+
189
+ $numeric_keys = array( 'menu_order', 'ID', 'post_parent', 'post_author', 'comment_count', 'relevance_score' );
190
+ $date_keys = array( 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt' );
191
+
192
+ $compare = 'string';
193
+ if ( in_array( $key, $numeric_keys, true ) ) {
194
+ $compare = 'number';
195
+ } elseif ( in_array( $key, $date_keys, true ) ) {
196
+ $compare = 'date';
197
+ }
198
+
199
+ if ( 'rand' === $key ) {
200
+ if ( is_numeric( $dir ) ) {
201
+ // A specific random seed is requested.
202
+ mt_srand( $dir );
203
+ }
204
+ } else {
205
+ $dir = strtolower( $dir );
206
+ if ( 'asc' !== $dir ) {
207
+ $dir = 'desc';
208
+ }
209
+ }
210
+
211
  $values = array(
212
+ 'key' => $key,
213
+ 'dir' => $dir,
214
+ 'compare' => $compare,
215
  );
216
  return $values;
217
  }
218
 
219
+ /**
220
+ * Gets the values for comparing items for given key.
221
+ *
222
+ * Fetches the key values for the item pair. If random order is required, this
223
+ * function will randomize the order.
224
+ *
225
+ * @global object $wp_query The global WP_Query object.
226
+ *
227
+ * @param string $key The key used.
228
+ * @param object $item_1 The first post object to compare.
229
+ * @param object $item_2 The second post object to compare.
230
+ *
231
+ * @return array Array with the key values: 'key1' and 'key2', respectively.
232
  */
233
+ function relevanssi_get_compare_values( $key, $item_1, $item_2 ) {
234
+ if ( 'rand' === $key ) {
 
 
235
  do {
236
  $key1 = rand();
237
  $key2 = rand();
238
+ } while ( $key1 === $key2 );
239
  $keys = array(
240
  'key1' => $key1,
241
  'key2' => $key2,
243
  return $keys;
244
  }
245
 
246
+ $key1 = '';
247
+ $key2 = '';
248
 
249
+ if ( 'meta_value' === $key || 'meta_value_num' === $key ) {
250
  global $wp_query;
251
+ // Get the name of the field from the global WP_Query.
252
  $key = $wp_query->query_vars['meta_key'];
253
+ if ( ! isset( $key ) ) {
254
+ // The key is not set.
255
+ return array( '', '' );
256
+ }
257
 
258
+ $key1 = get_post_meta( $item_1->ID, $key, true );
259
+ if ( empty( $key1 ) ) {
260
+ /**
261
+ * Adds in a missing sorting value.
262
+ *
263
+ * In some cases the sorting method may not have values for all posts
264
+ * (for example when sorting by 'menu_order'). If you still want to use
265
+ * a sorting method like this, you can use this function to fill in a
266
+ * value (in the case of 'menu_order', for example, one could use
267
+ * PHP_INT_MAX.)
268
+ *
269
+ * @param string $key1 The value to filter.
270
+ * @param string $key The name of the key.
271
+ */
272
+ $key1 = apply_filters( 'relevanssi_missing_sort_key', $key1, $key );
273
  }
274
+
275
+ $key2 = get_post_meta( $item_2->ID, $key, true );
276
+ if ( empty( $key2 ) ) {
277
+ /**
278
+ * Documented in lib/common.php.
279
+ */
280
+ $key2 = apply_filters( 'relevanssi_missing_sort_key', $key2, $key );
281
  }
282
+ } else {
283
+ if ( isset( $item_1->$key ) ) {
284
+ $key1 = relevanssi_strtolower( $item_1->$key );
285
+ } else {
286
+ /**
287
+ * Documented in lib/common.php.
288
+ */
289
+ $key1 = apply_filters( 'relevanssi_missing_sort_key', $key1, $key );
290
  }
291
+ if ( isset( $item_2->$key ) ) {
292
+ $key2 = relevanssi_strtolower( $item_2->$key );
293
+ } else {
294
+ /**
295
+ * Documented in lib/common.php.
296
+ */
297
+ $key2 = apply_filters( 'relevanssi_missing_sort_key', $key2, $key );
298
  }
299
  }
300
 
305
  return $keys;
306
  }
307
 
308
+ /**
309
+ * Compares to values.
310
+ *
311
+ * Compares two sorting keys using date based comparison, string comparison or
312
+ * numeric comparison.
313
+ *
314
+ * @param string $key1 The first key.
315
+ * @param string $key2 The second key.
316
+ * @param string $compare The comparison method; possible values are 'date' for
317
+ * date comparisons and 'string' for string comparison, everything else is
318
+ * considered a numeric comparison.
319
+ *
320
+ * @return int $val Returns < 0 if key1 is less than key2; > 0 if key1 is greater
321
+ * than key2, and 0 if they are equal.
322
+ */
323
+ function relevanssi_compare_values( $key1, $key2, $compare ) {
324
  $val = 0;
325
+ if ( 'date' === $compare ) {
326
+ if ( strtotime( $key1 ) > strtotime( $key2 ) ) {
327
  $val = 1;
328
+ } elseif ( strtotime( $key1 ) < strtotime( $key2 ) ) {
 
329
  $val = -1;
330
  }
331
+ } elseif ( 'string' === $compare ) {
332
+ $val = relevanssi_mb_strcasecmp( $key1, $key2 );
333
+ } else {
334
+ if ( $key1 > $key2 ) {
 
 
335
  $val = 1;
336
+ } elseif ( $key1 < $key2 ) {
 
337
  $val = -1;
338
  }
339
  }
340
  return $val;
341
  }
342
 
343
+ /**
344
+ * Multibyte friendly case-insensitive string comparison.
345
+ *
346
+ * If multibyte string functions are available, do strcmp() after using
347
+ * mb_strtoupper() to both strings. Otherwise use strcasecmp().
348
+ *
349
+ * @param string $str1 First string to compare.
350
+ * @param string $str2 Second string to compare.
351
+ * @param string $encoding The encoding to use. Defaults to mb_internal_encoding().
352
+ *
353
+ * @return int $val Returns < 0 if str1 is less than str2; > 0 if str1 is greater
354
+ * than str2, and 0 if they are equal.
355
+ */
356
+ function relevanssi_mb_strcasecmp( $str1, $str2, $encoding = null ) {
357
+ if ( ! function_exists( 'mb_internal_encoding' ) ) {
358
+ return strcasecmp( $str1, $str2 );
359
+ } else {
360
+ if ( null === $encoding ) {
361
+ $encoding = mb_internal_encoding();
362
+ }
363
+ return strcmp( mb_strtoupper( $str1, $encoding ), mb_strtoupper( $str2, $encoding ) );
364
  }
365
+ }
366
+
367
+ /**
368
+ * Multibyte friendly strtolower.
369
+ *
370
+ * If multibyte string functions are available, returns mb_strtolower() and falls
371
+ * back to strtolower() if multibyte functions are not available.
372
+ *
373
+ * @param string $string The string to lowercase.
374
+ *
375
+ * @return string $string The string in lowercase.
376
+ */
377
+ function relevanssi_strtolower( $string ) {
378
+ if ( ! function_exists( 'mb_strtolower' ) ) {
379
+ return strtolower( $string );
380
+ } else {
381
+ return mb_strtolower( $string );
382
  }
383
  }
384
 
385
+ /**
386
+ * Compares values using multiple levels of sorting keys.
387
+ *
388
+ * Comparison function for usort() using multiple levels of sorting methods. If one
389
+ * level produces a tie, the sort will get a next level of sorting methods.
390
+ *
391
+ * @global array $relevanssi_keys An array of sorting keys by level.
392
+ * @global array $relevanssi_dirs An array of sorting directions by level.
393
+ * @global array $relevanssi_compares An array of comparison methods by level.
394
+ *
395
+ * @param object $a A post object.
396
+ * @param object $b A post object.
397
+ *
398
+ * @return int $val Returns < 0 if a is less than b; > 0 if a is greater
399
+ * than b, and 0 if they are equal.
400
+ */
401
+ function relevanssi_cmp_function( $a, $b ) {
402
  global $relevanssi_keys, $relevanssi_dirs, $relevanssi_compares;
403
+
404
  $level = -1;
405
+ $val = 0;
406
 
407
+ while ( 0 === $val ) {
408
  $level++;
409
+ if ( ! isset( $relevanssi_keys[ $level ] ) ) {
410
+ // No more levels; we've hit the bedrock.
411
  $level--;
412
+ break;
413
  }
414
+ $compare = $relevanssi_compares[ $level ];
415
+ $compare_values = relevanssi_get_compare_values( $relevanssi_keys[ $level ], $a, $b );
416
+ $val = relevanssi_compare_values( $compare_values['key1'], $compare_values['key2'], $compare );
417
  }
418
+
419
+ if ( 'desc' === $relevanssi_dirs[ $level ] ) {
420
+ $val = $val * -1;
 
 
 
421
  }
422
+
423
+ return $val;
424
  }
425
 
426
  /**
427
+ * Sorts post objects.
428
+ *
429
+ * Sorts post objects using multiple levels of sorting methods. This function was
430
+ * originally written by Matthew Hood and published in the PHP manual comments.
431
+ * The actual sorting is handled by relevanssi_cmp_function().
432
+ *
433
+ * @global array $relevanssi_keys An array of sorting keys by level.
434
+ * @global array $relevanssi_dirs An array of sorting directions by level.
435
+ * @global array $relevanssi_compares An array of comparison methods by level.
436
+ *
437
+ * @param array $data The posts to sort are in $data[0], used as a reference.
438
+ * @param array $orderby The array of orderby rules with directions.
439
  */
440
+ function relevanssi_object_sort( &$data, $orderby ) {
441
  global $relevanssi_keys, $relevanssi_dirs, $relevanssi_compares;
442
+
443
+ $relevanssi_keys = array();
444
+ $relevanssi_dirs = array();
445
  $relevanssi_compares = array();
446
+
447
  do {
448
+ $values = relevanssi_get_next_key( $orderby );
449
+ if ( ! empty( $values['key'] ) ) {
450
+ $relevanssi_keys[] = $values['key'];
451
+ $relevanssi_dirs[] = $values['dir'];
452
  $relevanssi_compares[] = $values['compare'];
453
  }
454
+ } while ( ! empty( $values['key'] ) );
 
 
 
455
 
456
+ $primary_key = $relevanssi_keys[0];
457
+ if ( ! isset( $data[0]->$primary_key ) ) {
458
+ // Trying to sort by a non-existent key.
459
+ return;
460
+ }
461
 
462
+ usort( $data, 'relevanssi_cmp_function' );
463
  }
464
 
465
+ /**
466
+ * Generates the search result breakdown added to the search results.
467
+ *
468
+ * Gets the source data, generates numbers of it and then replaces the placeholders
469
+ * in the breakdown template with the data.
470
+ *
471
+ * @param array $data The source data.
472
+ * @param int $hit The post ID.
473
+ *
474
+ * @return string The search results breakdown for the post.
475
+ */
476
+ function relevanssi_show_matches( $data, $hit ) {
477
+ if ( isset( $data['body_matches'][ $hit ] ) ) {
478
+ $body = $data['body_matches'][ $hit ];
479
+ } else {
480
+ $body = 0;
481
+ }
482
+ if ( isset( $data['title_matches'][ $hit ] ) ) {
483
+ $title = $data['title_matches'][ $hit ];
484
+ } else {
485
+ $title = 0;
486
+ }
487
+ if ( isset( $data['tag_matches'][ $hit ] ) ) {
488
+ $tag = $data['tag_matches'][ $hit ];
489
+ } else {
490
+ $tag = 0;
491
+ }
492
+ if ( isset( $data['category_matches'][ $hit ] ) ) {
493
+ $category = $data['category_matches'][ $hit ];
494
+ } else {
495
+ $category = 0;
496
+ }
497
+ if ( isset( $data['taxonomy_matches'][ $hit ] ) ) {
498
+ $taxonomy = $data['taxonomy_matches'][ $hit ];
499
+ } else {
500
+ $taxonomy = 0;
501
+ }
502
+ if ( isset( $data['comment_matches'][ $hit ] ) ) {
503
+ $comment = $data['comment_matches'][ $hit ];
504
+ } else {
505
+ $comment = 0;
506
+ }
507
+ if ( isset( $data['scores'][ $hit ] ) ) {
508
+ $score = round( $data['scores'][ $hit ], 2 );
509
+ } else {
510
+ $score = 0;
511
+ }
512
+ if ( isset( $data['term_hits'][ $hit ] ) ) {
513
+ $term_hits_array = $data['term_hits'][ $hit ];
514
+ arsort( $term_hits_array );
515
+ } else {
516
+ $term_hits_array = array();
517
+ }
518
+
519
+ $term_hits = '';
520
  $total_hits = 0;
521
+ foreach ( $term_hits_array as $term => $hits ) {
522
+ $term_hits .= " $term: $hits";
523
  $total_hits += $hits;
524
  }
525
 
526
+ $text = stripslashes( get_option( 'relevanssi_show_matches_text' ) );
527
+ $replace_these = array( '%body%', '%title%', '%tags%', '%categories%', '%taxonomies%', '%comments%', '%score%', '%terms%', '%total%' );
528
+ $replacements = array( $body, $title, $tag, $category, $taxonomy, $comment, $score, $term_hits, $total_hits );
529
+ $result = ' ' . str_replace( $replace_these, $replacements, $text );
530
+
531
+ /**
532
+ * Filters the search result breakdown.
533
+ *
534
+ * If you use the "Show breakdown of search hits in excerpts" option, this
535
+ * filter lets you modify the breakdown before it is added to the excerpt.
536
+ *
537
+ * @param string $result The breakdown.
538
+ */
539
+ return apply_filters( 'relevanssi_show_matches', $result );
540
  }
541
 
542
+ /**
543
+ * Adds the search query to the log.
544
+ *
545
+ * Logs the search query, trying to avoid bots.
546
+ *
547
+ * @global object $wpdb The WordPress database interface.
548
+ * @global array $relevanssi_variables The global Relevanssi variables, used for database table names.
549
+ *
550
+ * @param string $query The search query.
551
+ * @param int $hits The number of hits found.
552
+ */
553
+ function relevanssi_update_log( $query, $hits ) {
554
+ // Bot filter, by Justin_K.
555
+ // See: http://wordpress.org/support/topic/bot-logging-problem-w-tested-solution.
556
+ $user_agent = '';
557
+ if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
558
+ $user_agent = $_SERVER['HTTP_USER_AGENT'];
559
+ $bots = array( 'Google' => 'Mediapartners-Google' );
560
+
561
+ /**
562
+ * Filters the bots Relevanssi should block from logs.
563
+ *
564
+ * Lets you filter the bots that are blocked from Relevanssi logs.
565
+ *
566
+ * @param array $bots An array of bot user agents.
567
+ */
568
+ $bots = apply_filters( 'relevanssi_bots_to_not_log', $bots );
569
+ foreach ( $bots as $name => $lookfor ) {
570
+ if ( false !== stristr( $user_agent, $lookfor ) ) {
571
+ return;
572
+ }
573
+ }
574
  }
575
 
576
+ /**
577
+ * Filters the current user for logs.
578
+ *
579
+ * The current user is checked before logging a query to omit particular users.
580
+ * You can use this filter to filter out the user.
581
+ *
582
+ * @param object The current user object.
583
+ */
584
+ $user = apply_filters( 'relevanssi_log_get_user', wp_get_current_user() );
585
+ if ( 0 !== $user->ID && get_option( 'relevanssi_omit_from_logs' ) ) {
586
+ $omit = explode( ',', get_option( 'relevanssi_omit_from_logs' ) );
587
+ if ( in_array( strval( $user->ID ), $omit, true ) ) {
588
+ return;
589
+ }
590
+ if ( in_array( $user->user_login, $omit, true ) ) {
591
+ return;
592
+ }
593
  }
594
 
595
+ $ip = '';
596
+ if ( 'on' === get_option( 'relevanssi_log_queries_with_ip' ) ) {
597
+ /**
598
+ * Filters the IP address of the searcher.
599
+ *
600
+ * Relevanssi may store the IP address of the searches in the logs. If the
601
+ * setting is enabled, this filter can be used to filter out the IP address
602
+ * before the log entry is made.
603
+ *
604
+ * Do note that storing the IP address may be illegal or get you in GDPR
605
+ * trouble.
606
+ *
607
+ * @param string $ip The IP address, from $_SERVER['REMOTE_ADDR'].
608
+ */
609
+ $ip = apply_filters( 'relevanssi_remote_addr', $_SERVER['REMOTE_ADDR'] );
610
+ }
611
 
612
+ /**
613
+ * Filters whether a query should be logged or not.
614
+ *
615
+ * This filter can used to determine whether a query should be logged or not.
616
+ *
617
+ * @param boolean $ok_to_log Can the query be logged.
618
+ * @param string $query The actual query string.
619
+ * @param int $hits The number of hits found.
620
+ * @param string $user_agent The user agent that made the search.
621
+ * @param string $ip The IP address the search came from (or empty).
622
+ */
623
+ $ok_to_log = apply_filters( 'relevanssi_ok_to_log', true, $query, $hits, $user_agent, $ip );
624
+ if ( $ok_to_log ) {
625
+ global $wpdb, $relevanssi_variables;
626
+
627
+ $wpdb->query(
628
+ $wpdb->prepare( 'INSERT INTO ' . $relevanssi_variables['log_table'] . ' (query, hits, user_id, ip, time) VALUES (%s, %d, %d, %s, NOW())',
629
+ $query, intval( $hits ), $user->ID, $ip )
630
+ ); // WPCS: unprepared SQL ok, Relevanssi database table name.
631
+ }
632
  }
633
 
634
+ /**
635
+ * Trims Relevanssi log table.
636
+ *
637
+ * Trims Relevanssi log table, using the day interval setting from 'relevanssi_trim_logs'.
638
+ *
639
+ * @global object $wpdb The WordPress database interface.
640
+ * @global array $relevanssi_variables The global Relevanssi variables, used for database table names.
641
+ */
642
  function relevanssi_trim_logs() {
643
  global $wpdb, $relevanssi_variables;
644
+ $interval = intval( get_option( 'relevanssi_trim_logs' ) );
645
+ $wpdb->query(
646
+ $wpdb->prepare( 'DELETE FROM ' . $relevanssi_variables['log_table'] . ' WHERE time < TIMESTAMP(DATE_SUB(NOW(), INTERVAL %d DAY))',
647
+ $interval )
648
+ ); // WPCS: unprepared SQL ok, Relevanssi database table name.
649
  }
650
 
651
  /**
652
+ * Checks whether the user is allowed to see the post.
653
+ *
654
+ * The default behaviour on 'relevanssi_post_ok' filter hook. Do note that while
655
+ * this function takes $post_ok as a parameter, it actually doesn't care much
656
+ * about the previous value, and will instead overwrite it. If you want to make
657
+ * sure your value is preserved, either disable this default function, or run
658
+ * your function on a later priority (this defaults to 10).
659
+ *
660
+ * Includes support for various membership plugins. Currently supports Members,
661
+ * Groups, Simple Membership and s2member.
662
+ *
663
+ * @param boolean $post_ok Can the post be shown to the user.
664
+ * @param int $post_id The post ID.
665
+ *
666
+ * @return boolean $post_ok True if the user is allowed to see the post,
667
+ * otherwise false.
668
  */
669
+ function relevanssi_default_post_ok( $post_ok, $post_id ) {
670
+ $status = relevanssi_get_post_status( $post_id );
671
 
672
+ // If it's not public, don't show.
673
+ if ( 'publish' !== $status ) {
674
  $post_ok = false;
675
  }
676
 
677
+ // Let's look a bit closer at private posts.
678
+ if ( 'private' === $status ) {
 
679
  $post_ok = false;
680
 
681
+ $type = relevanssi_get_post_type( $post_id );
682
+ if ( isset( $GLOBALS['wp_post_types'][ $type ]->cap->read_private_posts ) ) {
683
+ $cap = $GLOBALS['wp_post_types'][ $type ]->cap->read_private_posts;
684
+ } else {
685
+ // Just guessing here.
686
+ $cap = 'read_private_' . $type . 's';
687
  }
688
+ if ( current_user_can( $cap ) ) {
689
+ // Current user has the required capabilities and can see the page.
690
+ $post_ok = true;
 
 
 
 
 
 
 
 
 
 
 
 
691
  }
692
+ }
693
+
694
+ if ( function_exists( 'members_can_current_user_view_post' ) ) {
695
+ // Members.
696
+ $post_ok = members_can_current_user_view_post( $post_id );
697
+ }
698
+ if ( defined( 'GROUPS_CORE_VERSION' ) ) {
699
+ // Groups.
700
+ $current_user = wp_get_current_user();
701
+ $post_ok = Groups_Post_Access::user_can_read_post( $post_id, $current_user->ID );
702
+ }
703
+ if ( class_exists( 'MeprUpdateCtrl' ) && MeprUpdateCtrl::is_activated() ) {
704
+ // Memberpress.
705
+ $post = get_post( $post_id );
706
+ $post_ok = MeprRule::is_locked( $post );
707
+ }
708
+ if ( defined( 'SIMPLE_WP_MEMBERSHIP_VER' ) ) {
709
+ // Simple Membership.
710
+ $logged_in = SwpmMemberUtils::is_member_logged_in();
711
+ if ( ! $logged_in ) {
712
+ $post_ok = false;
713
+ } else {
714
+ $access_ctrl = SwpmAccessControl::get_instance();
715
+ $post_ok = $access_ctrl->can_i_read_post( $post_id );
716
  }
717
  }
718
 
719
+ /**
720
+ * Filters statuses allowed in admin searches.
721
+ *
722
+ * By default, admin searches may show posts that have 'draft', 'pending' and
723
+ * 'future' status (in addition to 'publish' and 'private'). If you use custom
724
+ * statuses and want them included in the admin search, you can add the statuses
725
+ * using this filter.
726
+ *
727
+ * @param array $statuses Array of statuses to accept.
728
+ */
729
+ if ( in_array( $status, apply_filters( 'relevanssi_valid_admin_status', array( 'draft', 'pending', 'future' ) ), true ) && is_admin() ) {
730
+ // Only show drafts, pending and future posts in admin search.
731
  $post_ok = true;
732
  }
733
 
 
 
734
  return $post_ok;
735
  }
736
 
737
  /**
738
+ * Populates the Relevanssi post array.
739
+ *
740
+ * This is a caching mechanism to reduce the number of database queries required.
741
+ * This function fetches all post data for the matches found using single MySQL
742
+ * query, instead of doing up to 500 separate get_post() queries.
743
+ *
744
+ * @global array $relevanssi_post_array An array of fetched posts.
745
+ * @global array $relevanssi_post_types An array of post types, to be used by
746
+ * relevanssi_get_post_type() (again to avoid DB calls).
747
+ * @global object $wpdb The WordPress database interface.
748
+ *
749
+ * @param array $matches An array of search matches.
750
  */
751
+ function relevanssi_populate_array( $matches ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
752
  global $relevanssi_post_array, $relevanssi_post_types, $wpdb;
753
+
754
+ // Doing this makes life faster.
755
+ wp_suspend_cache_addition( true );
756
 
757
  $ids = array();
758
+ foreach ( $matches as $match ) {
759
+ array_push( $ids, $match->doc );
760
  }
761
 
762
+ $ids = implode( ',', $ids );
763
+ $posts = $wpdb->get_results( "SELECT * FROM $wpdb->posts WHERE id IN ($ids)" ); // WPCS: unprepared SQL ok, no user-generated inputs.
764
+
765
+ foreach ( $posts as $post ) {
766
+ $relevanssi_post_array[ $post->ID ] = $post;
767
+ $relevanssi_post_types[ $post->ID ] = $post->post_type;
768
  }
769
 
770
+ // Re-enable caching.
771
+ wp_suspend_cache_addition( false );
772
  }
773
 
774
+ /**
775
+ * Fetches the taxonomy based on term ID.
776
+ *
777
+ * Fetches the taxonomy from wp_term_taxonomy based on term_id.
778
+ *
779
+ * @global object $wpdb The WordPress database interface.
780
+ *
781
+ * @param int $term_id The term ID.
782
+ *
783
+ * @return string $taxonomy The term taxonomy.
784
+ */
785
+ function relevanssi_get_term_taxonomy( $term_id ) {
786
  global $wpdb;
787
+ $taxonomy = $wpdb->get_var( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); // WPCS: Unprepared SQL ok, database table name.
788
  return $taxonomy;
789
  }
790
 
791
  /**
792
+ * Extracts phrases from the search query.
793
+ *
794
+ * Finds all phrases wrapped in quotes from the search query.
795
+ *
796
+ * @param string $query The query.
797
+ *
798
+ * @return array An array of phrases (strings).
799
  */
800
+ function relevanssi_extract_phrases( $query ) {
801
+ $strpos_function = 'strpos';
802
+ if ( function_exists( 'mb_strpos' ) ) {
803
+ $strpos_function = 'mb_strpos';
804
+ }
805
+ $substr_function = 'substr';
806
+ if ( function_exists( 'mb_substr' ) ) {
807
+ $substr_function = 'mb_substr';
808
+ }
809
 
810
+ $pos = call_user_func( $strpos_function, $query, '"' );
811
 
812
  $phrases = array();
813
+ while ( false !== $pos ) {
814
  $start = $pos;
815
+ $end = call_user_func( $strpos_function, $query, '"', $start + 1 );
816
 
817
+ if ( false === $end ) {
818
+ // Just one " in the query.
819
  $pos = $end;
820
  continue;
821
  }
822
+ $phrase = call_user_func( $substr_function, $query, $start + 1, $end - $start - 1 );
823
+ $phrase = trim( $phrase );
824
 
825
+ // Do not count single-word phrases as phrases.
826
+ if ( ! empty( $phrase ) && str_word_count( $phrase ) > 1 ) {
827
+ $phrases[] = $phrase;
828
+ }
829
  $pos = $end;
830
  }
831
 
832
  return $phrases;
833
  }
834
 
835
+ /**
836
+ * Generates the MySQL code for restricting the search to phrase hits.
837
+ *
838
+ * This function uses relevanssi_extract_phrases() to figure out the phrases in the
839
+ * search query, then generates MySQL queries to restrict the search to the posts
840
+ * containing those phrases in the title, content, taxonomy terms or meta fields.
841
+ *
842
+ * @global object $wpdb The WordPress database interface.
843
+ *
844
+ * @param string $search_query The search query.
845
+ *
846
+ * @return string $queries If not phrase hits are found, an empty string; otherwise
847
+ * MySQL queries to restrict the search.
848
  */
849
+ function relevanssi_recognize_phrases( $search_query ) {
850
  global $wpdb;
851
 
852
+ $phrases = relevanssi_extract_phrases( $search_query );
853
 
854
  $all_queries = array();
855
+ if ( count( $phrases ) > 0 ) {
856
+ foreach ( $phrases as $phrase ) {
857
  $queries = array();
858
+ $phrase = str_replace( '', '_', $phrase );
859
+ $phrase = str_replace( '', '_', $phrase );
860
+ $phrase = str_replace( "'", '_', $phrase );
861
+ $phrase = str_replace( '"', '_', $phrase );
862
+ $phrase = str_replace( '', '_', $phrase );
863
+ $phrase = str_replace( '', '_', $phrase );
864
+ $phrase = str_replace( '', '_', $phrase );
865
+ $phrase = str_replace( '´', '_', $phrase );
866
+ $phrase = $wpdb->esc_like( $phrase );
867
+ $phrase = esc_sql( $phrase );
868
+ $excerpt = '';
869
+ if ( 'on' === get_option( 'relevanssi_index_excerpt' ) ) {
870
+ $excerpt = " OR post_excerpt LIKE '%$phrase%'";
871
+ }
872
+
873
  $query = "(SELECT ID FROM $wpdb->posts
874
  WHERE (post_content LIKE '%$phrase%' OR post_title LIKE '%$phrase%' $excerpt)
875
  AND post_status IN ('publish', 'draft', 'private', 'pending', 'future', 'inherit'))";
890
 
891
  $queries[] = $query;
892
 
893
+ $queries = implode( ' OR relevanssi.doc IN ', $queries );
894
+ $queries = "AND (relevanssi.doc IN $queries)";
895
  $all_queries[] = $queries;
896
  }
897
+ } else {
898
+ $phrases = '';
 
899
  }
900
 
901
+ $all_queries = implode( ' ', $all_queries );
902
 
903
  return $all_queries;
904
  }
905
 
906
+ /**
907
+ * Strips invisible elements from text.
908
+ *
909
+ * Strips <style>, <script>, <object>, <embed>, <applet>, <noscript>, <noembed>,
910
+ * <iframe>, and <del> tags and their contents from the text.
911
+ *
912
+ * @param string $text The source text.
913
+ *
914
+ * @return string The processed text.
915
+ */
916
+ function relevanssi_strip_invisibles( $text ) {
917
  $text = preg_replace(
918
  array(
919
  '@<style[^>]*?>.*?</style>@siu',
926
  '@<iframe[^>]*?.*?</iframe>@siu',
927
  '@<del[^>]*?.*?</del>@siu',
928
  ),
929
+ ' ', $text );
 
930
  return $text;
931
  }
932
 
933
+ /**
934
+ * Sorts strings by length.
935
+ *
936
+ * A sorting function that sorts strings by length. Uses relevanssi_strlen() to
937
+ * count the string length.
938
+ *
939
+ * @param string $a String A.
940
+ * @param string $b String B.
941
+ *
942
+ * @return int Negative value, if string A is longer; zero, if strings are equally
943
+ * long; positive, if string B is longer.
944
+ */
945
+ function relevanssi_strlen_sort( $a, $b ) {
946
+ return relevanssi_strlen( $b ) - relevanssi_strlen( $a );
947
  }
948
 
949
+ /**
950
+ * Returns the custom fields to index.
951
+ *
952
+ * Returns a list of custom fields to index, based on the custom field indexing
953
+ * setting.
954
+ *
955
+ * @return array|string An array of custom fields to index, or 'all' or 'visible'.
956
+ */
957
  function relevanssi_get_custom_fields() {
958
+ $custom_fields = get_option( 'relevanssi_index_fields' );
959
+ if ( $custom_fields ) {
960
+ if ( 'all' === $custom_fields ) {
961
  return $custom_fields;
962
+ } elseif ( 'visible' === $custom_fields ) {
 
963
  return $custom_fields;
964
+ } else {
965
+ $custom_fields = explode( ',', $custom_fields );
966
+ $count_custom_fields = count( $custom_fields );
967
+ for ( $i = 0; $i < $count_custom_fields; $i++ ) {
968
+ $custom_fields[ $i ] = trim( $custom_fields[ $i ] );
969
  }
970
  }
971
+ } else {
 
972
  $custom_fields = false;
973
  }
974
  return $custom_fields;
975
  }
976
 
977
+ /**
978
+ * Trims multibyte strings.
979
+ *
980
+ * Removes the 194+160 non-breakable spaces and removes whitespace.
981
+ *
982
+ * @param string $string The source string.
983
+ *
984
+ * @return string Trimmed string.
985
+ */
986
+ function relevanssi_mb_trim( $string ) {
987
+ $string = str_replace( chr( 194 ) . chr( 160 ), '', $string );
988
+ $string = preg_replace( '/(^\s+)|(\s+$)/us', '', $string );
989
+ return $string;
990
  }
991
 
992
+ /**
993
+ * Removes punctuation from a string.
994
+ *
995
+ * This function removes some punctuation and replaces some punctuation with spaces.
996
+ * This can partly be controlled from Relevanssi settings: see Advanced Indexing
997
+ * Settings on the Indexing tab. This function runs on the
998
+ * 'relevanssi_remove_punctuation' filter hook and can be disabled, if necessary.
999
+ *
1000
+ * @param string $a The source string.
1001
+ *
1002
+ * @return string The string without punctuation.
1003
+ */
1004
+ function relevanssi_remove_punct( $a ) {
1005
+ if ( ! is_string( $a ) ) {
1006
+ // In case something sends a non-string here.
1007
+ return '';
1008
+ }
1009
 
1010
+ $a = preg_replace( '/<[^>]*>/', ' ', $a );
1011
 
1012
+ $punct_options = get_option( 'relevanssi_punctuation' );
1013
 
1014
+ $hyphen_replacement = ' ';
1015
+ $endash_replacement = ' ';
1016
+ $emdash_replacement = ' ';
1017
+ if ( isset( $punct_options['hyphens'] ) && 'remove' === $punct_options['hyphens'] ) {
1018
+ $hyphen_replacement = '';
1019
+ $endash_replacement = '';
1020
+ $emdash_replacement = '';
1021
  }
1022
+ if ( isset( $punct_options['hyphens'] ) && 'keep' === $punct_options['hyphens'] ) {
1023
+ $hyphen_replacement = 'HYPHENTAIKASANA';
1024
+ $endash_replacement = 'ENDASHTAIKASANA';
1025
+ $emdash_replacement = 'EMDASHTAIKASANA';
1026
  }
1027
 
1028
+ $quote_replacement = ' ';
1029
+ if ( isset( $punct_options['quote'] ) && 'remove' === $punct_options['quote'] ) {
1030
+ $quote_replacement = '';
1031
+ }
1032
 
1033
+ $ampersand_replacement = ' ';
1034
+ if ( isset( $punct_options['ampersands'] ) && 'remove' === $punct_options['ampersands'] ) {
1035
+ $ampersand_replacement = '';
1036
  }
1037
+ if ( isset( $punct_options['ampersands'] ) && 'keep' === $punct_options['ampersands'] ) {
1038
+ $ampersand_replacement = 'AMPERSANDTAIKASANA';
1039
+ }
1040
+
1041
+ $decimal_replacement = ' ';
1042
+ if ( isset( $punct_options['decimals'] ) && 'remove' === $punct_options['decimals'] ) {
1043
+ $decimal_replacement = '';
1044
+ }
1045
+ if ( isset( $punct_options['decimals'] ) && 'keep' === $punct_options['decimals'] ) {
1046
+ $decimal_replacement = 'DESIMAALITAIKASANA';
1047
  }
1048
 
1049
  $replacement_array = array(
1050
+ 'ß' => 'ss',
1051
+ '·' => '',
1052
+ '' => '',
1053
+ '' => '',
1054
+ '®' => '',
1055
+ '©' => '',
1056
+ '&shy;' => '',
1057
+ '&nbsp;' => ' ',
1058
+ '&#8217;' => ' ',
1059
+ chr( 194 ) . chr( 160 ) => ' ',
1060
+ '×' => ' ',
1061
+ "'" => $quote_replacement,
1062
+ '' => $quote_replacement,
1063
+ '' => $quote_replacement,
1064
+ '' => $quote_replacement,
1065
+ '' => $quote_replacement,
1066
+ '' => $quote_replacement,
1067
+ '´' => $quote_replacement,
1068
+ '-' => $hyphen_replacement,
1069
+ '' => $endash_replacement,
1070
+ '' => $emdash_replacement,
1071
+ '&#038;' => $ampersand_replacement,
1072
+ '&amp;' => $ampersand_replacement,
1073
+ '&' => $ampersand_replacement,
1074
  );
1075
 
1076
+ /**
1077
+ * Filters the punctuation replacement array.
1078
+ *
1079
+ * This filter can be used to alter the way some of the most common punctuation
1080
+ * is handled by Relevanssi.
1081
+ *
1082
+ * @param array $replacement_array The array of punctuation and the replacements.
1083
+ */
1084
+ $replacement_array = apply_filters( 'relevanssi_punctuation_filter', $replacement_array );
1085
+
1086
+ $a = preg_replace( '/\.(\d)/', $decimal_replacement . '\1', $a );
1087
+
1088
+ $a = str_replace( "\r", ' ', $a );
1089
+ $a = str_replace( "\n", ' ', $a );
1090
+ $a = str_replace( "\t", ' ', $a );
1091
+
1092
+ $a = stripslashes( $a );
1093
+
1094
+ $a = str_replace( array_keys( $replacement_array ), array_values( $replacement_array ), $a );
1095
+ /**
1096
+ * Filters the default punctuation replacement value.
1097
+ *
1098
+ * By default Relevanssi replaces unspecified punctuation with spaces. This
1099
+ * filter can be used to change that behaviour.
1100
+ *
1101
+ * @param string $replacement The replacement value, default ' '.
1102
+ */
1103
+ $a = preg_replace( '/[[:punct:]]+/u', apply_filters( 'relevanssi_default_punctuation_replacement', ' ' ), $a );
1104
+ $a = preg_replace( '/[[:space:]]+/', ' ', $a );
1105
+
1106
+ $a = str_replace( 'AMPERSANDTAIKASANA', '&', $a );
1107
+ $a = str_replace( 'HYPHENTAIKASANA', '-', $a );
1108
+ $a = str_replace( 'ENDASHTAIKASANA', '–', $a );
1109
+ $a = str_replace( 'EMDASHTAIKASANA', '—', $a );
1110
+ $a = str_replace( 'DESIMAALITAIKASANA', '.', $a );
1111
+
1112
+ $a = trim( $a );
1113
+
1114
+ return $a;
1115
  }
1116
 
1117
 
1118
  /**
1119
+ * Prevents the default search from running.
1120
+ *
1121
+ * When Relevanssi is active, this function prevents the default search from running,
1122
+ * in order to save resources. There are some exceptions, where we don't want
1123
+ * Relevanssi to meddle.
1124
+ *
1125
+ * This function originally created by John Blackbourne.
1126
+ *
1127
+ * @global object $wpdb The WordPress database interface.
1128
+ *
1129
+ * @param string $request The MySQL query for the search.
1130
+ * @param object $query The WP_Query object.
1131
  */
1132
  function relevanssi_prevent_default_request( $request, $query ) {
1133
+ if ( $query->is_search ) {
1134
+ if ( isset( $query->query_vars['post_type'] ) && isset( $query->query_vars['post_status'] ) ) {
1135
+ if ( 'attachment' === $query->query_vars['post_type'] && 'inherit,private' === $query->query_vars['post_status'] ) {
1136
+ // This is a media library search; do not meddle.
1137
+ return $request;
1138
+ }
1139
+ }
1140
+
1141
+ if ( in_array( $query->query_vars['post_type'], array( 'topic', 'reply' ), true ) ) {
1142
+ // This is a BBPress search; do not meddle.
1143
+ return $request;
1144
+ }
1145
+ if ( is_array( $query->query_vars['post_type'] ) ) {
1146
+ if ( in_array( 'topic', $query->query_vars['post_type'], true ) || in_array( 'reply', $query->query_vars['post_type'], true ) ) {
1147
+ // This is a BBPress search; do not meddle.
1148
+ return $request;
1149
  }
1150
  }
1151
+
1152
+ if ( isset( $_REQUEST['action'] ) && 'acf' === substr( $_REQUEST['action'], 0, 3 ) ) { // WPCS: CSRF ok.
1153
+ // ACF stuff, do not touch (eg. a relationship field search).
1154
+ return $request;
 
1155
  }
1156
+ if ( isset( $query->query_vars['action'] ) && 'acf' === substr( $query->query_vars['action'], 0, 3 ) ) {
1157
+ // ACF stuff, do not touch (eg. a relationship field search).
1158
  return $request;
1159
  }
1160
+
1161
  $admin_search_ok = true;
1162
+ /**
1163
+ * Filters the admin search.
1164
+ *
1165
+ * If this filter returns 'false', Relevanssi will be disabled.
1166
+ *
1167
+ * @param boolean $admin_search_ok Is admin search allowed.
1168
+ * @param object $query The WP_Query object.
1169
+ */
1170
+ $admin_search_ok = apply_filters( 'relevanssi_admin_search_ok', $admin_search_ok, $query );
1171
 
1172
  $prevent = true;
1173
+ /**
1174
+ * Filters whether the default request is blocked or not.
1175
+ *
1176
+ * If this filter returns 'false', the default search request will not be
1177
+ * blocked.
1178
+ *
1179
+ * @param boolean $prevent Should the request be prevented.
1180
+ * @param object $query The WP_Query object.
1181
+ */
1182
+ $prevent = apply_filters( 'relevanssi_prevent_default_request', $prevent, $query );
1183
+
1184
+ if ( empty( $query->query_vars['s'] ) ) {
1185
+ $prevent = false;
1186
  $admin_search_ok = false;
1187
  }
1188
 
1189
+ if ( $query->is_admin && defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1190
+ $prevent = false;
1191
  $admin_search_ok = false;
1192
  }
1193
 
1194
+ if ( $query->is_admin && 'page' === $query->query_vars['post_type'] ) {
1195
+ // Relevanssi doesn't work on the page screen, so disable.
1196
+ $prevent = false;
1197
  $admin_search_ok = false;
1198
  }
1199
 
1200
+ global $wpdb;
1201
+
1202
+ if ( ! is_admin() && $prevent ) {
1203
  $request = "SELECT * FROM $wpdb->posts WHERE 1=2";
1204
+ } elseif ( 'on' === get_option( 'relevanssi_admin_search' ) && $admin_search_ok ) {
1205
  $request = "SELECT * FROM $wpdb->posts WHERE 1=2";
1206
+ }
1207
  }
1208
  return $request;
1209
  }
1210
 
1211
+ /**
1212
+ * Disables Relevanssi in the ACF Relationship field post search.
1213
+ *
1214
+ * We don't want to use Relevanssi on the ACF Relationship field post searches, so
1215
+ * this function disables it (on the 'relevanssi_search_ok' hook).
1216
+ *
1217
+ * @param boolean $search_ok Block the search or not.
1218
+ *
1219
+ * @return boolean False, if this is an ACF Relationship field search, pass the
1220
+ * parameter unchanged otherwise.
1221
+ */
1222
+ function relevanssi_acf_relationship_fields( $search_ok ) {
1223
+ if ( isset( $_REQUEST['action'] ) && 'acf' === substr( $_REQUEST['action'], 0, 3 ) ) { // WPCS: CSRF ok.
1224
+ $search_ok = false;
1225
+ }
1226
+ return $search_ok;
1227
+ }
1228
+
1229
+ /**
1230
+ * Tokenizes strings.
1231
+ *
1232
+ * Tokenizes strings, removes punctuation, converts to lowercase and removes
1233
+ * stopwords. The function accepts both strings and arrays of strings as
1234
+ * source material. If the parameter is an array of string, each string is
1235
+ * tokenized separately and the resulting tokens are combined into one array.
1236
+ *
1237
+ * @param string|array $string The string, or an array of strings, to tokenize.
1238
+ * @param boolean $remove_stops If true, stopwords are removed. Default true.
1239
+ * @param int $min_word_length The minimum word length to include. Default -1.
1240
+ */
1241
+ function relevanssi_tokenize( $string, $remove_stops = true, $min_word_length = -1 ) {
1242
  $tokens = array();
1243
+ if ( is_array( $string ) ) {
1244
+ // If we get an array, tokenize each string in the array.
1245
+ foreach ( $string as $substring ) {
1246
+ if ( is_string( $substring ) ) {
1247
+ $tokens = array_merge( $tokens, relevanssi_tokenize( $substring, $remove_stops, $min_word_length ) );
1248
+ }
1249
  }
1250
+
1251
+ // And we're done!
1252
+ return $tokens;
1253
  }
 
1254
 
1255
+ if ( function_exists( 'mb_internal_encoding' ) ) {
1256
+ mb_internal_encoding( 'UTF-8' );
1257
+ }
1258
 
1259
+ $stopword_list = array();
1260
+ if ( $remove_stops ) {
1261
  $stopword_list = relevanssi_fetch_stopwords();
1262
  }
1263
 
1264
+ if ( function_exists( 'relevanssi_apply_thousands_separator' ) ) {
1265
+ // A Premium feature.
1266
+ $string = relevanssi_apply_thousands_separator( $string );
1267
  }
1268
 
1269
+ /**
1270
+ * Removes punctuation from the string.
1271
+ *
1272
+ * The default function on this filter is relevanssi_remove_punct(), which
1273
+ * removes some punctuation and replaces some with spaces.
1274
+ *
1275
+ * @param string $string String with punctuation.
1276
+ */
1277
+ $string = apply_filters( 'relevanssi_remove_punctuation', $string );
1278
+
1279
+ $string = relevanssi_strtolower( $string );
1280
+
1281
+ $token = strtok( $string, "\n\t " );
1282
+ while ( false !== $token ) {
1283
+ $token = strval( $token );
1284
  $accept = true;
1285
 
1286
+ if ( relevanssi_strlen( $token ) < $min_word_length ) {
1287
+ $token = strtok( "\n\t " );
1288
  continue;
1289
  }
1290
+ if ( $remove_stops && in_array( $token, $stopword_list, true ) ) {
1291
+ $accept = false;
 
 
 
 
 
 
 
1292
  }
1293
 
1294
+ if ( RELEVANSSI_PREMIUM ) {
1295
+ /**
1296
+ * Fires Premium tokenizer.
1297
+ *
1298
+ * Filters the token through the Relevanssi Premium tokenizer to add some
1299
+ * Premium features to the tokenizing (mostly stemming).
1300
+ *
1301
+ * @param string $token Search query token.
1302
+ */
1303
+ $token = apply_filters( 'relevanssi_premium_tokenizer', $token );
1304
  }
1305
 
1306
+ if ( $accept ) {
1307
+ $token = relevanssi_mb_trim( $token );
1308
+ if ( is_numeric( $token ) ) {
1309
+ // $token ends up as an array index, and numbers don't work there.
1310
+ $token = " $token";
1311
  }
1312
+ if ( ! isset( $tokens[ $token ] ) ) {
1313
+ $tokens[ $token ] = 1;
1314
+ } else {
1315
+ $tokens[ $token ]++;
1316
  }
1317
  }
1318
 
1319
+ $token = strtok( "\n\t " );
1320
  }
1321
+
1322
  return $tokens;
1323
  }
1324
 
1325
+ /**
1326
+ * Returns the post status from post ID.
1327
+ *
1328
+ * Returns the post status. This replacement for get_post_status() can handle user
1329
+ * profiles and taxonomy terms (both always return 'publish'). The status is read
1330
+ * from the Relevanssi caching mechanism to avoid unnecessary database calls, and
1331
+ * if nothing else works, this function falls back to get_post_status().
1332
+ *
1333
+ * @global array $relevanssi_post_array The Relevanssi post cache array.
1334
+ *
1335
+ * @param string $post_id The post ID.
1336
+ *
1337
+ * @return string The post status.
1338
+ */
1339
+ function relevanssi_get_post_status( $post_id ) {
1340
  global $relevanssi_post_array;
1341
+ $type = substr( $post_id, 0, 2 );
1342
+ if ( '**' === $type || 'u_' === $type ) {
1343
+ // Taxonomy term or user (a Premium feature).
 
 
 
1344
  return 'publish';
1345
  }
1346
 
1347
+ if ( isset( $relevanssi_post_array[ $post_id ] ) ) {
1348
+ $status = $relevanssi_post_array[ $post_id ]->post_status;
1349
+ if ( 'inherit' === $status ) {
1350
+ // Attachment, let's see what the parent says.
1351
+ $parent = $relevanssi_post_array[ $post_id ]->post_parent;
1352
+ if ( ! $parent ) {
1353
+ // Attachment without a parent, let's assume it's public.
 
1354
  $status = 'publish';
1355
+ } else {
1356
+ $status = relevanssi_get_post_status( $parent );
1357
  }
1358
  }
1359
  return $status;
1360
+ } else {
1361
+ // No hit from the cache; let's add this post to the cache.
1362
+ $post = get_post( $post_id );
1363
+ if ( ! $post ) {
1364
+ return '';
1365
+ }
1366
+
1367
+ $relevanssi_post_array[ $post_id ] = $post;
1368
+ return $post->post_status;
1369
  }
1370
  }
1371
 
1372
+ /**
1373
+ * Returns the post type.
1374
+ *
1375
+ * Replacement for get_post_type() that uses the Relevanssi post cache to reduce the
1376
+ * number of database calls required.
1377
+ *
1378
+ * @global array $relevanssi_post_array The Relevanssi post cache array.
1379
+ *
1380
+ * @param int $post_id The post ID.
1381
+ *
1382
+ * @return string The post type.
1383
+ */
1384
+ function relevanssi_get_post_type( $post_id ) {
1385
  global $relevanssi_post_array;
1386
 
1387
+ if ( isset( $relevanssi_post_array[ $post_id ] ) ) {
1388
+ return $relevanssi_post_array[ $post_id ]->post_type;
1389
+ } else {
1390
+ // No hit from the cache; let's add this post to the cache.
1391
+ $post = get_post( $post_id );
1392
+
1393
+ $relevanssi_post_array[ $post_id ] = $post;
1394
+
1395
+ return $post->post_type;
1396
  }
1397
  }
1398
 
1399
+ /**
1400
+ * Prints out a list of tags for post.
1401
+ *
1402
+ * Replacement for the_tags() that does the same, but applies Relevanssi search term
1403
+ * highlighting on the results.
1404
+ *
1405
+ * @param string $before What is printed before the tags, default null.
1406
+ * @param string $separator The separator between items, default ', '.
1407
+ * @param string $after What is printed after the tags, default ''.
1408
+ * @param boolean $echo If true, echo, otherwise return the result. Default true.
1409
+ */
1410
+ function relevanssi_the_tags( $before = null, $separator = ', ', $after = '', $echo = true ) {
1411
+ $tags = relevanssi_highlight_terms( get_the_tag_list( $before, $separator, $after ), get_search_query() );
1412
+ if ( $echo ) {
1413
+ echo $tags; // WPCS: XSS ok. All content is already escaped by WP.
1414
+ } else {
1415
  return $tags;
1416
  }
1417
  }
1418
 
1419
+ /**
1420
+ * Gets a list of tags for post.
1421
+ *
1422
+ * Replacement for get_the_tags() that does the same, but applies Relevanssi search term
1423
+ * highlighting on the results.
1424
+ *
1425
+ * @param string $before What is printed before the tags, default null.
1426
+ * @param string $separator The separator between items, default ', '.
1427
+ * @param string $after What is printed after the tags, default ''.
1428
+ */
1429
+ function relevanssi_get_the_tags( $before = null, $separator = ', ', $after = '' ) {
1430
+ return relevanssi_the_tags( $before, $sep, $after, false );
1431
  }
1432
 
1433
+ /**
1434
+ * Returns the term taxonomy ID for a term based on term ID.
1435
+ *
1436
+ * @global object $wpdb The WordPress database interface.
1437
+ *
1438
+ * @param int $term_id The term ID.
1439
+ * @param string $taxonomy The taxonomy.
1440
+ *
1441
+ * @return int Term taxonomy ID.
1442
+ */
1443
+ function relevanssi_get_term_tax_id( $term_id, $taxonomy ) {
1444
  global $wpdb;
1445
+ return $wpdb->get_var( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id = %d AND taxonomy = %s", $term_id, $taxonomy ) );
 
 
 
1446
  }
1447
 
1448
  /**
1449
+ * Adds synonyms to a search query.
1450
+ *
1451
+ * Takes a search query and adds synonyms to it.
1452
+ *
1453
+ * @param string $query The source query.
1454
+ *
1455
+ * @return string The query with synonyms added.
1456
  */
1457
+ function relevanssi_add_synonyms( $query ) {
1458
+ if ( empty( $query ) ) {
1459
+ return $query;
1460
+ }
1461
+
1462
+ $synonym_data = get_option( 'relevanssi_synonyms' );
1463
+ if ( $synonym_data ) {
1464
+ $synonyms = array();
1465
+ $synonym_data = relevanssi_strtolower( $synonym_data );
1466
+ $pairs = explode( ';', $synonym_data );
1467
+
1468
+ foreach ( $pairs as $pair ) {
1469
+ if ( empty( $pair ) ) {
1470
+ // Skip empty rows.
1471
+ continue;
1472
+ }
1473
+ $parts = explode( '=', $pair );
1474
+ $key = strval( trim( $parts[0] ) );
1475
+ $value = trim( $parts[1] );
1476
+
1477
+ $synonyms[ $key ][ $value ] = true;
1478
  }
1479
+
1480
+ if ( count( $synonyms ) > 0 ) {
1481
  $new_terms = array();
1482
+ $terms = array_keys( relevanssi_tokenize( $query, false ) ); // Remove stopwords is false here.
1483
+ if ( ! in_array( $query, $terms, true ) ) {
1484
+ // Include the whole query in the terms, unless it's not there already.
1485
+ $terms[] = $query;
1486
+ }
1487
+
1488
+ foreach ( $terms as $term ) {
1489
+ $term = trim( $term );
1490
+ if ( in_array( strval( $term ), array_keys( $synonyms ), true ) ) { // Strval(), otherwise numbers cause problems.
1491
+ if ( isset( $synonyms[ strval( $term ) ] ) ) { // Necessary, otherwise terms like "02" can cause problems.
1492
+ $new_terms = array_merge( $new_terms, array_keys( $synonyms[ strval( $term ) ] ) );
1493
  }
1494
  }
1495
  }
1496
+ if ( count( $new_terms ) > 0 ) {
1497
+ foreach ( $new_terms as $new_term ) {
1498
+ $query .= " $new_term";
1499
  }
1500
  }
1501
  }
1502
  }
1503
+ return $query;
1504
  }
1505
 
1506
+ /**
1507
+ * Returns the position of substring in the string.
1508
+ *
1509
+ * Uses mb_stripos() if possible, falls back to mb_strpos() and mb_strtoupper() if
1510
+ * that cannot be found, and falls back to just strpos() if even that is not
1511
+ * possible.
1512
+ *
1513
+ * @param string $haystack String where to look.
1514
+ * @param string $needle The string to look for.
1515
+ * @param int $offset Where to start, default 0.
1516
+ *
1517
+ * @return mixed False, if no result or $offset outside the length of $haystack,
1518
+ * otherwise the position (which can be non-false 0!).
1519
  */
1520
+ function relevanssi_stripos( $haystack, $needle, $offset = 0 ) {
1521
+ if ( $offset > relevanssi_strlen( $haystack ) ) {
1522
+ return false;
 
 
1523
  }
1524
+
1525
+ if ( function_exists( 'mb_stripos' ) ) {
1526
+ if ( '' === $haystack ) {
1527
+ $pos = false;
1528
+ } else {
1529
+ $pos = mb_stripos( $haystack, $needle, $offset );
1530
+ }
1531
+ } elseif ( function_exists( 'mb_strpos' ) && function_exists( 'mb_strtoupper' ) && function_exists( 'mb_substr' ) ) {
1532
+ $pos = mb_strpos( mb_strtoupper( $haystack ), mb_strtoupper( $needle ), $offset );
1533
+ } else {
1534
+ $pos = strpos( strtoupper( $haystack ), strtoupper( $needle ), $offset );
1535
  }
1536
  return $pos;
1537
  }
1538
 
1539
+ /**
1540
+ * Closes tags in a bit of HTML code.
1541
+ *
1542
+ * Used to make sure no tags are left open in excerpts. This method is not foolproof,
1543
+ * but it's good enough for now.
1544
+ *
1545
+ * @param string $html The HTML code to analyze.
1546
+ *
1547
+ * @return string The HTML code, with tags closed.
1548
  */
1549
+ function relevanssi_close_tags( $html ) {
1550
+ preg_match_all( '#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result );
1551
+ $opened_tags = $result[1];
1552
+ preg_match_all( '#</([a-z]+)>#iU', $html, $result );
1553
+ $closed_tags = $result[1];
1554
+ $len_opened = count( $opened_tags );
1555
+ if ( count( $closed_tags ) === $len_opened ) {
1556
+ return $html;
1557
+ }
1558
+ $opened_tags = array_reverse( $opened_tags );
1559
+ for ( $i = 0; $i < $len_opened; $i++ ) {
1560
+ if ( ! in_array( $opened_tags[ $i ], $closed_tags, true ) ) {
1561
+ $html .= '</' . $opened_tags[ $i ] . '>';
1562
+ } else {
1563
+ unset( $closed_tags[ array_search( $opened_tags[ $i ], $closed_tags, true ) ] );
1564
+ }
1565
+ }
1566
+ return $html;
1567
  }
1568
 
1569
+ /**
1570
+ * Prints out post title with highlighting.
1571
+ *
1572
+ * Uses the global $post object. Reads the highlighted title from
1573
+ * $post->post_highlighted_title.
1574
+ *
1575
+ * @global object $post The global post object.
1576
+ *
1577
+ * @param boolean $echo If true, echo out the title. Default true.
1578
+ *
1579
+ * @return string If $echo is false, returns the title with highlights.
1580
  */
1581
+ function relevanssi_the_title( $echo = true ) {
1582
  global $post;
1583
+ if ( empty( $post->post_highlighted_title ) ) {
1584
+ $post->post_highlighted_title = $post->post_title;
1585
+ }
1586
+ if ( $echo ) {
1587
+ echo $post->post_highlighted_title; // WPCS: XSS ok, $post->post_highlighted_title is generated by Relevanssi.
1588
+ }
1589
  return $post->post_highlighted_title;
1590
  }
1591
 
1592
+ /**
1593
+ * Returns the post title with highlighting.
1594
+ *
1595
+ * Reads the highlighted title from $post->post_highlighted_title.
1596
+ *
1597
+ * @param int $post_id The post ID.
1598
+ *
1599
+ * @return string The post title with highlights.
1600
  */
1601
+ function relevanssi_get_the_title( $post_id ) {
1602
+ $post = relevanssi_get_post( $post_id );
1603
+ if ( ! is_object( $post ) ) {
1604
+ return null;
1605
+ }
1606
+ if ( empty( $post->post_highlighted_title ) ) {
1607
+ $post->post_highlighted_title = $post->post_title;
1608
+ }
1609
  return $post->post_highlighted_title;
1610
  }
1611
 
1612
+ /**
1613
+ * Updates the 'relevanssi_doc_count' option.
1614
+ *
1615
+ * The 'relevanssi_doc_count' option contains the number of documents in the
1616
+ * Relevanssi index. This function calculates the value and stores it in the
1617
+ * option.
1618
+ *
1619
+ * @global object $wpdb The WordPress database interface.
1620
+ * @global array $relevanssi_variables The Relevanssi global variable, used for table names.
1621
+ */
1622
+ function relevanssi_update_doc_count() {
1623
  global $wpdb, $relevanssi_variables;
1624
+ $doc_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT(relevanssi.doc)) FROM ' . $relevanssi_variables['relevanssi_table'] . ' AS relevanssi' ); // WPCS: unprepared SQL ok, Relevanssi table name.
1625
+ update_option( 'relevanssi_doc_count', $doc_count );
 
 
1626
  }
1627
 
1628
+ /**
1629
+ * Returns the length of the string.
1630
+ *
1631
+ * Uses mb_strlen() if available, otherwise falls back to strlen().
1632
+ *
1633
+ * @param string $s The string to measure.
1634
+ *
1635
+ * @return int The length of the string.
1636
+ */
1637
+ function relevanssi_strlen( $s ) {
1638
+ if ( function_exists( 'mb_strlen' ) ) {
1639
+ return mb_strlen( $s );
1640
+ }
1641
  return strlen( $s );
1642
  }
1643
 
1644
+ /**
1645
+ * Prints out debugging notices.
1646
+ *
1647
+ * If WP_CLI is available, prints out the debug notice as a WP_CLI::log(), otherwise
1648
  * just echo.
1649
+ *
1650
+ * @param string $notice The notice to print out.
1651
  */
1652
+ function relevanssi_debug_echo( $notice ) {
1653
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
1654
+ WP_CLI::log( $notice );
1655
+ } else {
1656
+ echo esc_html( $notice ) . "\n";
 
1657
  }
1658
  }
1659
 
1660
+ /**
1661
+ * Returns a Relevanssi_Taxonomy_Walker instance.
1662
+ *
1663
+ * Requires the class file and generates a new Relevanssi_Taxonomy_Walker instance.
1664
+ *
1665
+ * @return object A new Relevanssi_Taxonomy_Walker instance.
1666
+ */
1667
+ function get_relevanssi_taxonomy_walker() {
1668
+ require_once 'class-relevanssi-taxonomy-walker.php';
1669
+ return new Relevanssi_Taxonomy_Walker();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1670
  }
1671
 
1672
+ /**
1673
+ * Adjusts Relevanssi variables when switch_blog() happens.
1674
+ *
1675
+ * This function attaches to the 'switch_blog' action hook and adjusts the table
1676
+ * names in the global $relevanssi_variables array to match the new blog.
1677
+ *
1678
+ * @global array $relevanssi_variables The global Relevanssi variables.
1679
+ * @global object $wpdb The WordPress database interface.
1680
+ *
1681
+ * @author Teemu Muikku
1682
+ *
1683
+ * @param int $new_blog The new blog ID.
1684
+ * @param int $prev_blog The old blog ID.
1685
+ */
1686
+ function relevanssi_switch_blog( $new_blog, $prev_blog ) {
1687
+ global $relevanssi_variables, $wpdb;
1688
 
1689
+ if ( ! isset( $relevanssi_variables ) || ! isset( $relevanssi_variables['relevanssi_table'] ) ) {
1690
+ return;
1691
+ }
1692
 
1693
+ $relevanssi_variables['relevanssi_table'] = $wpdb->prefix . 'relevanssi';
1694
+ $relevanssi_variables['stopword_table'] = $wpdb->prefix . 'relevanssi_stopwords';
1695
+ $relevanssi_variables['log_table'] = $wpdb->prefix . 'relevanssi_log';
1696
  }
1697
 
1698
+ /**
1699
+ * Adds a highlight parameter to the permalink.
1700
+ *
1701
+ * Relevanssi requires a 'highligh' parameter to the permalinks in order to have
1702
+ * working highlights. This function adds the highlight. The function doesn't add
1703
+ * the parameter to the links pointing at the front page, because if we do that,
1704
+ * the link won't point to the front page anymore, but instead points to the blog
1705
+ * page.
1706
+ *
1707
+ * @global object $post The global post object.
1708
+ *
1709
+ * @param string $permalink The link to patch.
1710
+ *
1711
+ * @return string The link with the parameter added.
1712
+ */
1713
+ function relevanssi_add_highlight( $permalink ) {
1714
+ $highlight_docs = get_option( 'relevanssi_highlight_docs' );
1715
+ $query = get_search_query();
1716
+ if ( isset( $highlight_docs ) && 'off' !== $highlight_docs && ! empty( $query ) ) {
1717
  global $post;
1718
+ $frontpage_id = get_option( 'page_on_front' );
1719
+ if ( is_object( $post ) && $post->ID !== $frontpage_id ) {
1720
+ // We won't add the highlight parameter for the front page, as that will break the link.
1721
+ $permalink = esc_attr( add_query_arg( array( 'highlight' => rawurlencode( get_search_query() ) ), $permalink )
 
 
1722
  );
1723
  }
1724
  }
1725
  return $permalink;
1726
  }
1727
 
1728
+ /**
1729
+ * Gets the permalink to the current post within Loop.
1730
+ *
1731
+ * Uses get_permalink() to get the permalink, then adds the 'highlight' parameter
1732
+ * if necessary using relevanssi_add_highlight().
1733
+ *
1734
+ * @return string The permalink.
1735
+ */
1736
+ function relevanssi_get_permalink() {
1737
+ /**
1738
+ * Filters the permalink.
1739
+ *
1740
+ * @param string The permalink, generated by get_permalink().
1741
+ */
1742
+ $permalink = apply_filters( 'relevanssi_permalink', get_permalink() );
1743
+ return $permalink;
1744
+ }
1745
+
1746
+ /**
1747
+ * Echoes out the permalink to the current post within Loop.
1748
+ *
1749
+ * Uses get_permalink() to get the permalink, then adds the 'highlight' parameter
1750
+ * if necessary using relevanssi_add_highlight(), then echoes it out.
1751
+ */
1752
  function relevanssi_the_permalink() {
1753
+ echo esc_attr( relevanssi_get_permalink() );
1754
  }
1755
 
1756
+ /**
1757
+ * Adjusts the permalink to use the Relevanssi-generated link.
1758
+ *
1759
+ * This function is used to filter 'the_permalink', 'post_link' and
1760
+ * 'relevanssi_permalink'. It changes the permalink to point to
1761
+ * $post->relevanssi_link, if that exists. This means the permalinks to
1762
+ * user profiles and taxonomy terms work. This function also adds the
1763
+ * 'highlight' parameter to the URL.
1764
+ *
1765
+ * @global object $post The global post object.
1766
+ *
1767
+ * @param string $link The link to adjust.
1768
+ * @param object $link_post The post to modify. If null, use global $post.
1769
+ * Defaults null.
1770
+ *
1771
+ * @return string The modified link.
1772
+ */
1773
+ function relevanssi_permalink( $link, $link_post = null ) {
1774
+ if ( null === $link_post ) {
1775
  global $post;
 
 
1776
  }
1777
+ // Using property_exists() to avoid troubles from magic variables.
1778
+ if ( is_object( $post ) && property_exists( $post, 'relevanssi_link' ) ) {
1779
+ $link = $post->relevanssi_link;
1780
+ }
1781
+ $link = relevanssi_add_highlight( $link );
1782
+ return $link;
1783
  }
1784
 
1785
+ /**
1786
+ * Generates the Did you mean suggestions.
1787
+ *
1788
+ * A wrapper function that prints out the Did you mean suggestions. If Premium is
1789
+ * available, will use relevanssi_premium_didyoumean(), otherwise the
1790
+ * relevanssi_simple_didyoumean() is used.
1791
+ *
1792
+ * @param string $query The query.
1793
+ * @param string $pre Printed out before the suggestion.
1794
+ * @param string $post Printed out after the suggestion.
1795
+ * @param int $n Maximum number of search results found for the suggestions
1796
+ * to show up. Default 5.
1797
+ * @param boolean $echo If true, echo out. Default true.
1798
+ *
1799
+ * @return string The suggestion HTML element.
1800
+ */
1801
+ function relevanssi_didyoumean( $query, $pre, $post, $n = 5, $echo = true ) {
1802
+ if ( function_exists( 'relevanssi_premium_didyoumean' ) ) {
1803
+ $result = relevanssi_premium_didyoumean( $query, $pre, $post, $n );
1804
+ } else {
1805
+ $result = relevanssi_simple_didyoumean( $query, $pre, $post, $n );
1806
  }
1807
+
1808
+ if ( $echo ) {
1809
+ echo $result; // WPCS: XSS ok, already escaped.
1810
  }
 
 
1811
 
1812
  return $result;
1813
  }
1814
 
1815
+ /**
1816
+ * Generates the Did you mean suggestions HTML code.
1817
+ *
1818
+ * Uses relevanssi_simple_generate_suggestion() to come up with a suggestion, then
1819
+ * wraps that up with HTML code.
1820
+ *
1821
+ * @global object $wpdb The WordPress database interface.
1822
+ * @global array $relevanssi_variables The Relevanssi global variables.
1823
+ * @global object $wp_query The WP_Query object.
1824
+ *
1825
+ * @param string $query The query.
1826
+ * @param string $pre Printed out before the suggestion.
1827
+ * @param string $post Printed out after the suggestion.
1828
+ * @param int $n Maximum number of search results found for the suggestions
1829
+ * to show up. Default 5.
1830
+ *
1831
+ * @return string The suggestion HTML code, null if nothing found.
1832
+ */
1833
+ function relevanssi_simple_didyoumean( $query, $pre, $post, $n = 5 ) {
1834
  global $wpdb, $relevanssi_variables, $wp_query;
1835
 
1836
  $total_results = $wp_query->found_posts;
1837
 
1838
+ if ( $total_results > $n ) {
1839
+ return null;
1840
+ }
1841
+
1842
+ $suggestion = relevanssi_simple_generate_suggestion( $query );
1843
+
1844
+ $result = null;
1845
+ if ( $suggestion ) {
1846
+ $url = get_bloginfo( 'url' );
1847
+ $url = esc_attr( add_query_arg( array( 's' => rawurlencode( $suggestion ) ), $url ) );
1848
+
1849
+ /**
1850
+ * Filters the 'Did you mean' suggestion URL.
1851
+ *
1852
+ * @param string $url The URL for the suggested search query.
1853
+ * @param string $query The search query.
1854
+ * @param string $suggestion The suggestion.
1855
+ */
1856
+ $url = apply_filters( 'relevanssi_didyoumean_url', $url, $query, $suggestion );
1857
+
1858
+ // Escape the suggestion to avoid XSS attacks.
1859
+ $suggestion = htmlspecialchars( $suggestion );
1860
+
1861
+ /**
1862
+ * Filters the complete 'Did you mean' suggestion.
1863
+ *
1864
+ * @param string The suggestion HTML code.
1865
+ */
1866
+ $result = apply_filters( 'relevanssi_didyoumean_suggestion', "$pre<a href='$url'>$suggestion</a>$post" );
1867
+ }
1868
+
1869
+ return $result;
1870
+ }
1871
+
1872
+ /**
1873
+ * Generates the 'Did you mean' suggestions. Can be used to correct any queries.
1874
+ *
1875
+ * Uses the Relevanssi search logs as source material for corrections. If there are
1876
+ * no logged search queries, can't do anything.
1877
+ *
1878
+ * @global object $wpdb The WordPress database interface.
1879
+ * @global array $relevanssi_variables The Relevanssi global variables, used for
1880
+ * table names.
1881
+ *
1882
+ * @param string $query The query to correct.
1883
+ *
1884
+ * @return string Corrected query, empty if nothing found.
1885
+ */
1886
+ function relevanssi_simple_generate_suggestion( $query ) {
1887
+ global $wpdb, $relevanssi_variables;
1888
 
1889
+ $q = 'SELECT query, count(query) as c, AVG(hits) as a FROM ' . $relevanssi_variables['log_table'] . ' WHERE hits > 1 GROUP BY query ORDER BY count(query) DESC';
1890
+ $q = apply_filters( 'relevanssi_didyoumean_query', $q );
1891
 
1892
+ $data = wp_cache_get( 'relevanssi_didyoumean_query' );
1893
+ if ( empty( $data ) ) {
1894
+ $data = $wpdb->get_results( $q ); // WPCS: unprepared SQL ok. No user-generated input involved.
1895
+ wp_cache_set( 'relevanssi_didyoumean_query', $data );
1896
+ }
1897
 
1898
+ $query = htmlspecialchars_decode( $query );
1899
+ $tokens = relevanssi_tokenize( $query );
 
1900
  $suggestions_made = false;
1901
+ $suggestion = '';
1902
+
1903
+ foreach ( $tokens as $token => $count ) {
1904
+ $closest = '';
1905
  $distance = -1;
1906
+ foreach ( $data as $row ) {
1907
+ if ( $row->c < 2 ) {
 
 
 
1908
  break;
1909
  }
 
 
1910
 
1911
+ if ( $token === $row->query ) {
1912
+ $closest = '';
1913
+ break;
1914
+ } else {
1915
+ $lev = levenshtein( $token, $row->query );
1916
+
1917
+ if ( $lev < $distance || $distance < 0 ) {
1918
+ if ( $row->a > 0 ) {
1919
  $distance = $lev;
1920
+ $closest = $row->query;
1921
+ if ( $lev < 2 ) {
1922
+ break; // get the first with distance of 1 and go.
1923
+ }
1924
  }
1925
  }
1926
  }
1927
  }
1928
+ if ( ! empty( $closest ) ) {
1929
+ $query = str_ireplace( $token, $closest, $query );
1930
  $suggestions_made = true;
1931
+ }
1932
  }
1933
 
1934
+ if ( $suggestions_made ) {
1935
  $suggestion = $query;
 
1936
  }
1937
 
1938
+ return $suggestion;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1939
  }
1940
 
1941
+ /**
1942
+ * Instructs a multisite installation to drop the tables.
1943
+ *
1944
+ * Attaches to 'wpmu_drop_tables' and adds the Relevanssi tables to the list of
1945
+ * tables to drop.
1946
+ *
1947
+ * @global array $relevanssi_variables The Relevanssi global variables, used for
1948
+ * table names.
1949
+ *
1950
+ * @param array $tables The list of tables to drop.
1951
+ *
1952
+ * @return array Table list, with Relevanssi tables included.
1953
+ */
1954
+ function relevanssi_wpmu_drop( $tables ) {
1955
  global $relevanssi_variables;
1956
  $tables[] = $relevanssi_variables['relevanssi_table'];
1957
  $tables[] = $relevanssi_variables['stopword_table'];
1959
  return $tables;
1960
  }
1961
 
1962
+ /**
1963
+ * Replacement for get_post() that uses the Relevanssi post cache.
1964
+ *
1965
+ * Tries to fetch the post from the Relevanssi post cache. If that doesn't work, gets
1966
+ * the post using get_post().
1967
+ *
1968
+ * @param int $post_id The post ID.
1969
+ * @param int $blog_id The blog ID, default -1.
1970
+ *
1971
+ * @return object The post object.
1972
+ */
1973
+ function relevanssi_get_post( $post_id, $blog_id = -1 ) {
1974
+ if ( function_exists( 'relevanssi_premium_get_post' ) ) {
1975
+ return relevanssi_premium_get_post( $post_id, $blog_id );
1976
+ }
1977
+
1978
  global $relevanssi_post_array;
1979
 
1980
+ if ( isset( $relevanssi_post_array[ $post_id ] ) ) {
1981
+ $post = $relevanssi_post_array[ $post_id ];
1982
+ } else {
1983
+ $post = get_post( $post_id );
1984
+
1985
+ $relevanssi_post_array[ $post_id ] = $post;
1986
  }
1987
  return $post;
1988
  }
1989
+
1990
+ /**
1991
+ * Recursively flattens a multidimensional array to produce a string.
1992
+ *
1993
+ * @param array $array The source array.
1994
+ *
1995
+ * @return string The array contents as a string.
1996
+ */
1997
+ function relevanssi_flatten_array( array $array ) {
1998
+ $return_value = '';
1999
+ foreach ( new RecursiveIteratorIterator( new RecursiveArrayIterator( $array ) ) as $value ) {
2000
+ $return_value .= ' ' . $value;
2001
+ }
2002
+ return $return_value;
2003
+ }
lib/excerpts-highlights.php CHANGED
@@ -1,120 +1,219 @@
1
  <?php
 
 
 
 
 
 
 
 
2
 
3
- /** EXCERPTS **/
4
-
 
 
 
 
 
 
5
  function relevanssi_the_excerpt() {
6
- global $post;
7
- if (!post_password_required($post)) {
8
- echo "<p>" . $post->post_excerpt . "</p>";
9
- }
10
- else {
11
- echo __('There is no excerpt because this is a protected post.');
12
  }
13
  }
14
 
15
- function relevanssi_do_excerpt($t_post, $query) {
 
 
 
 
 
 
 
 
 
 
16
  global $post;
17
- $old_global_post = NULL;
18
- if ($post != NULL) $old_global_post = $post;
19
- $post = $t_post;
20
 
21
- $remove_stopwords = true;
 
 
 
 
 
22
 
23
- $query = apply_filters('relevanssi_excerpt_query', $query);
24
-
25
- $terms = relevanssi_tokenize($query, $remove_stopwords, -1);
26
 
27
- // These shortcodes cause problems with Relevanssi excerpts
28
- $problem_shortcodes = apply_filters('relevanssi_disable_shortcodes_excerpt',
29
- array('layerslider', 'responsive-flipbook', 'breadcrumb', 'robogallery', 'gravityview')
30
- );
31
- foreach ($problem_shortcodes as $shortcode) {
32
- remove_shortcode($shortcode);
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- $content = apply_filters('relevanssi_pre_excerpt_content', $post->post_content, $post, $query);
36
- if (get_option('relevanssi_excerpt_custom_fields') === "on") {
37
- $content .= relevanssi_get_custom_field_content($post->ID);
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
 
 
40
  relevanssi_kill_autoembed();
41
 
42
- $content = apply_filters('the_content', $content);
43
- $content = apply_filters('relevanssi_excerpt_content', $content, $post, $query);
 
44
 
45
- $content = relevanssi_strip_invisibles($content); // removes <script>, <embed> &c with content
46
- $content = preg_replace('/(<\/[^>]+?>)(<[^>\/][^>]*?>)/', '$1 $2', $content); // add spaces between tags to avoid getting words stuck together
47
- $content = strip_tags($content, get_option('relevanssi_excerpt_allowable_tags', '')); // this removes the tags, but leaves the content
48
 
49
- $content = preg_replace("/\n\r|\r\n|\n|\r/", " ", $content);
50
- // $content = trim(preg_replace("/\s\s+/", " ", $content));
 
 
 
 
 
 
 
 
 
51
 
52
- if (get_option('relevanssi_implicit_operator') === "OR" || get_option('relevanssi_index_synonyms') === "on") {
53
- $query = relevanssi_add_synonyms($query);
54
- }
 
 
 
 
 
 
 
 
55
 
56
- $excerpt_data = relevanssi_create_excerpt($content, $terms, $query);
 
 
57
 
58
- if (get_option("relevanssi_index_comments") != 'none') {
59
- $comment_content = relevanssi_get_comments($post->ID);
60
- $comment_content = preg_replace('/(<\/[^>]+?>)(<[^>\/][^>]*?>)/', '$1 $2', $comment_content); // add spaces between tags to avoid getting words stuck together
61
- $comment_content = strip_tags($comment_content, get_option('relevanssi_excerpt_allowable_tags', '')); // this removes the tags, but leaves the content
62
- if (!empty($comment_content)) {
63
- $comment_excerpts = relevanssi_create_excerpt($comment_content, $terms, $query);
64
- if ($comment_excerpts[1] > $excerpt_data[1]) {
 
 
 
 
 
65
  $excerpt_data = $comment_excerpts;
66
  }
67
  }
68
  }
69
 
70
- if (get_option("relevanssi_index_excerpt") != 'off') {
71
  $excerpt_content = $post->post_excerpt;
72
- $excerpt_content = strip_tags($excerpt_content, get_option('relevanssi_excerpt_allowable_tags', '')); // this removes the tags, but leaves the content
73
-
74
- if (!empty($excerpt_content)) {
75
- $excerpt_excerpts = relevanssi_create_excerpt($excerpt_content, $terms, $query);
76
- if ($excerpt_excerpts[1] > $excerpt_data[1]) {
 
77
  $excerpt_data = $excerpt_excerpts;
78
  }
79
  }
80
  }
81
 
82
- $start = $excerpt_data[2];
83
-
84
  $excerpt = $excerpt_data[0];
85
- $excerpt = trim($excerpt);
86
- $excerpt = apply_filters('relevanssi_excerpt', $excerpt);
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
- $excerpt === $post->post_content ? $whole_post_excerpted = true : $whole_post_excerpted = false;
89
- if (empty($excerpt) && !empty($post->post_excerpt)) {
90
  $excerpt = $post->post_excerpt;
91
- $excerpt = strip_tags($excerpt, get_option('relevanssi_excerpt_allowable_tags', '')); // this removes the tags, but leaves the content
92
- }
93
-
94
- $ellipsis = apply_filters('relevanssi_ellipsis', '...');
95
 
96
- $highlight = get_option('relevanssi_highlight');
97
- if ("none" != $highlight) {
98
- if ( !is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
99
- $excerpt = relevanssi_highlight_terms($excerpt, $query);
 
 
 
 
 
 
 
100
  }
101
  }
102
 
103
- $excerpt = relevanssi_close_tags($excerpt);
104
 
105
- if (!$whole_post_excerpted) {
106
- if (!$start && !empty($excerpt)) {
 
107
  $excerpt = $ellipsis . $excerpt;
108
- // do not add three dots to the beginning of the post
109
  }
110
 
111
- if (!empty($excerpt))
112
  $excerpt = $excerpt . $ellipsis;
 
113
  }
114
 
115
- if (relevanssi_s2member_level($post->ID) === 1) $excerpt = $post->post_excerpt;
116
-
117
- if ($old_global_post != NULL) $post = $old_global_post;
118
 
119
  return $excerpt;
120
  }
@@ -122,593 +221,850 @@ function relevanssi_do_excerpt($t_post, $query) {
122
  /**
123
  * Creates an excerpt from content.
124
  *
125
- * @return array - element 0 is the excerpt, element 1 the number of term hits, element 2 is
 
 
 
 
126
  * true, if the excerpt is from the start of the content.
127
  */
128
- function relevanssi_create_excerpt($content, $terms, $query) {
129
- // If you need to modify these on the go, use 'pre_option_relevanssi_excerpt_length' filter.
130
- $excerpt_length = get_option("relevanssi_excerpt_length");
131
- $type = get_option("relevanssi_excerpt_type");
 
132
 
133
  $best_excerpt_term_hits = -1;
134
- $excerpt = "";
135
 
136
- $content = preg_replace('/\s+/u', ' ', $content);
137
- $content = " $content";
138
 
139
- $phrases = relevanssi_extract_phrases(stripslashes($query));
 
140
 
 
 
 
 
 
141
  $non_phrase_terms = array();
142
- foreach ($phrases as $phrase) {
143
- $phrase_terms = array_keys(relevanssi_tokenize($phrase, $remove_stopwords = false));
144
- foreach (array_keys($terms) as $term) {
145
- if (!in_array($term, $phrase_terms)) {
146
- $non_phrase_terms[] = $term;
147
  }
148
  }
149
 
150
- $terms = $non_phrase_terms;
151
- $terms[$phrase] = 1;
152
  }
153
 
154
- // longest search terms first, because those are generally more significant
155
- uksort($terms, 'relevanssi_strlen_sort');
156
 
157
  $start = false;
158
- if ("chars" === $type) {
159
- $prev_count = floor($excerpt_length / 2);
160
- list($excerpt, $best_excerpt_term_hits, $start) = relevanssi_extract_relevant(array_keys($terms), $content, $excerpt_length, $prev_count);
161
- }
162
- else {
163
- $words = explode(' ', $content);
164
- $i = 0;
165
-
166
- $tries = 0;
167
- while ($i < count($words)) {
168
- if ($i + $excerpt_length > count($words)) {
169
- $i = count($words) - $excerpt_length;
170
- if ($i < 0) $i = 0;
 
 
 
 
 
 
 
 
 
 
171
  }
172
 
173
- $excerpt_slice = array_slice($words, $i, $excerpt_length);
174
- $excerpt_slice = implode(' ', $excerpt_slice);
175
 
176
- $excerpt_slice = " $excerpt_slice";
177
- $term_hits = 0;
178
- $count = relevanssi_count_matches(array_keys($terms), $excerpt_slice);
179
- if ($count > 0) {
180
  $tries++;
181
  }
182
- if ($count > 0 && $count > $best_excerpt_term_hits) {
183
- $best_excerpt_term_hits = $count;
184
- $excerpt = $excerpt_slice;
185
  }
186
 
187
- if (apply_filters('relevanssi_optimize_excerpts', false)) {
188
- if ($tries > 50) break;
189
- // An optimization trick.
 
 
 
 
 
 
 
 
 
 
 
190
  }
191
 
192
  $i += $excerpt_length;
193
  }
194
 
195
- if ("" === $excerpt) {
196
- $excerpt = explode(' ', $content, $excerpt_length);
197
- array_pop($excerpt);
198
- $excerpt = implode(' ', $excerpt);
199
- $start = true;
 
200
  }
201
  }
202
 
203
- return array($excerpt, $best_excerpt_term_hits, $start);
204
  }
205
 
206
- /** HIGHLIGHTING **/
207
-
208
- function relevanssi_highlight_in_docs($content) {
 
 
 
 
 
 
 
 
 
 
209
  global $wp_query;
210
- if (is_singular() && is_main_query()) {
211
- if (isset($wp_query->query_vars['highlight'])) {
212
- // Local search
213
- $q = relevanssi_add_synonyms($wp_query->query_vars['highlight']);
214
  $in_docs = true;
215
- $highlighted_content = relevanssi_highlight_terms($content, $q, $in_docs);
216
- if (!empty($highlighted_content)) $content = $highlighted_content;
217
- // Sometimes the content comes back empty; until I figure out why, this tries to be a solution.
 
 
 
218
  }
219
 
220
- if (function_exists('relevanssi_nonlocal_highlighting')) {
221
- $content = relevanssi_nonlocal_highlighting($content);
222
  }
223
  }
224
 
225
  return $content;
226
  }
227
 
228
- function relevanssi_highlight_terms($excerpt, $query, $in_docs = false) {
229
- $type = get_option("relevanssi_highlight");
230
- if ("none" === $type) {
231
- return $excerpt;
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  }
233
 
234
- switch ($type) {
235
- case "mark": // thanks to Jeff Byrnes
236
- $start_emp = "<mark>";
237
- $end_emp = "</mark>";
238
  break;
239
- case "strong":
240
- $start_emp = "<strong>";
241
- $end_emp = "</strong>";
242
  break;
243
- case "em":
244
- $start_emp = "<em>";
245
- $end_emp = "</em>";
246
  break;
247
- case "col":
248
- $col = get_option("relevanssi_txt_col");
249
- if (!$col) $col = "#ff0000";
 
 
250
  $start_emp = "<span style='color: $col'>";
251
- $end_emp = "</span>";
252
  break;
253
- case "bgcol":
254
- $col = get_option("relevanssi_bg_col");
255
- if (!$col) $col = "#ff0000";
 
 
256
  $start_emp = "<span style='background-color: $col'>";
257
- $end_emp = "</span>";
258
  break;
259
- case "css":
260
- $css = get_option("relevanssi_css");
261
- if (!$css) $css = "color: #ff0000";
 
 
262
  $start_emp = "<span style='$css'>";
263
- $end_emp = "</span>";
264
  break;
265
- case "class":
266
- $css = get_option("relevanssi_class");
267
- if (!$css) $css = "relevanssi-query-term";
 
 
268
  $start_emp = "<span class='$css'>";
269
- $end_emp = "</span>";
270
  break;
271
  default:
272
- return $excerpt;
273
  }
274
 
275
- $start_emp_token = "**{}[";
276
- $end_emp_token = "]}**";
277
 
278
- if ( function_exists('mb_internal_encoding') )
279
- mb_internal_encoding("UTF-8");
 
280
 
281
- do_action('relevanssi_highlight_tokenize');
 
 
 
282
 
283
- // Setting min_word_length to 2, in order to avoid 1-letter highlights.
284
  $min_word_length = 2;
285
- if (apply_filters('relevanssi_allow_one_letter_highlights', false)) $min_word_length = 1;
 
 
 
 
 
 
 
286
 
287
- $terms = array_keys(relevanssi_tokenize($query, $remove_stopwords = true, $min_word_length));
 
288
 
289
- if (is_array($query)) $query = implode(' ', $query); // just in case
290
- $phrases = relevanssi_extract_phrases(stripslashes($query));
 
 
291
 
 
292
  $non_phrase_terms = array();
293
- foreach ($phrases as $phrase) {
294
- $phrase_terms = array_keys(relevanssi_tokenize($phrase, $remove_stopwords = false));
295
- foreach ($terms as $term) {
296
- if (!in_array($term, $phrase_terms)) {
297
  $non_phrase_terms[] = $term;
298
  }
299
  }
300
- $terms = $non_phrase_terms;
301
  $terms[] = $phrase;
302
  }
303
 
304
- uksort($terms, 'relevanssi_strlen_sort');
305
 
306
- get_option('relevanssi_word_boundaries', 'on') === 'on' ? $word_boundaries = true : $word_boundaries = false;
307
- foreach ($terms as $term) {
308
- // $pr_term = relevanssi_replace_punctuation(preg_quote($term, '/'));
309
- $pr_term = preg_quote($term, '/');
310
- $pr_term = relevanssi_add_accent_variations($pr_term);
311
 
312
- $undecoded_excerpt = $excerpt;
313
- $excerpt = html_entity_decode($excerpt, ENT_QUOTES, 'UTF-8');
 
314
 
315
- if ($word_boundaries) {
316
- // get_option('relevanssi_fuzzy') != 'none' ? $regex = "/($pr_term)(?!(^&+)?(;))/iu" : $regex = "/(\b$pr_term|$pr_term\b)(?!(^&+)?(;))/iu";
317
- // get_option('relevanssi_fuzzy') != 'none' ? $regex = "/(\b$pr_term|$pr_term\b)(?!(^&+)?(;))/iu" : $regex = "/(\b$pr_term\b)(?!(^&+)?(;))/iu";
318
- get_option('relevanssi_fuzzy') != 'none' ? $regex = "/(\b$pr_term|$pr_term\b)/iu" : $regex = "/(\b$pr_term\b)/iu";
319
 
320
- $excerpt = preg_replace($regex, $start_emp_token . '\\1' . $end_emp_token, $excerpt);
321
- if (empty($excerpt)) $excerpt = preg_replace($regex, $start_emp_token . '\\1' . $end_emp_token, $undecoded_excerpt);
322
- }
323
- else {
324
- // $excerpt = preg_replace("/($pr_term)(?!(^&+)?(;))/iu", $start_emp_token . '\\1' . $end_emp_token, $excerpt);
325
- $excerpt = preg_replace("/($pr_term)/iu", $start_emp_token . '\\1' . $end_emp_token, $excerpt);
326
- if (empty($excerpt)) $excerpt = preg_replace("/($pr_term)/iu", $start_emp_token . '\\1' . $end_emp_token, $undecoded_excerpt);
 
 
 
 
 
 
 
 
327
  }
328
 
329
- $preg_start = preg_quote($start_emp_token);
330
- $preg_end = preg_quote($end_emp_token);
331
 
332
- if (preg_match_all('/<.*>/U', $excerpt, $matches) > 0) {
333
- // Remove highlights from inside HTML tags
334
- foreach ($matches as $match) {
335
- $new_match = str_replace($start_emp_token, '', $match);
336
- $new_match = str_replace($end_emp_token, '', $new_match);
337
- $excerpt = str_replace($match, $new_match, $excerpt);
338
  }
339
  }
340
 
341
- if (preg_match_all('/&.*;/U', $excerpt, $matches) > 0) {
342
- // Remove highlights from inside HTML entities
343
- foreach ($matches as $match) {
344
- $new_match = str_replace($start_emp_token, '', $match);
345
- $new_match = str_replace($end_emp_token, '', $new_match);
346
- $excerpt = str_replace($match, $new_match, $excerpt);
347
  }
348
  }
349
 
350
- if (preg_match_all('/<(style|script|object|embed)>.*<\/(style|script|object|embed)>/U', $excerpt, $matches) > 0) {
351
- // Remove highlights in style, object, embed and script tags
352
- foreach ($matches as $match) {
353
- $new_match = str_replace($start_emp_token, '', $match);
354
- $new_match = str_replace($end_emp_token, '', $new_match);
355
- $excerpt = str_replace($match, $new_match, $excerpt);
356
  }
357
  }
358
  }
359
 
360
- $excerpt = relevanssi_remove_nested_highlights($excerpt, $start_emp_token, $end_emp_token);
361
- $excerpt = relevanssi_fix_entities($excerpt, $in_docs);
362
 
363
- $excerpt = str_replace($start_emp_token, $start_emp, $excerpt);
364
- $excerpt = str_replace($end_emp_token, $end_emp, $excerpt);
365
- $excerpt = str_replace($end_emp . $start_emp, "", $excerpt);
366
- if (function_exists('mb_ereg_replace')) {
367
  $pattern = $end_emp . '\s*' . $start_emp;
368
- $excerpt = mb_ereg_replace($pattern, " ", $excerpt);
369
  }
370
 
371
- return $excerpt;
372
  }
373
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
 
375
- function relevanssi_replace_punctuation($a) {
376
- $a = preg_replace('/[[:punct:]]/u', '.', $a);
377
- return $a;
378
- }
379
 
380
- function relevanssi_fix_entities($excerpt, $in_docs) {
381
- if (!$in_docs) {
382
- // For excerpts, use htmlentities()
383
- $excerpt = htmlentities($excerpt, ENT_NOQUOTES, 'UTF-8'); // ENT_QUOTES or ENT_NOQUOTES?
384
 
385
- // Except for allowed tags, which are turned back into tags.
386
- $tags = get_option('relevanssi_excerpt_allowable_tags', '');
387
- $tags = trim(str_replace("<", " <", $tags));
388
- $tags = explode(" ", $tags);
389
- $closing_tags = relevanssi_generate_closing_tags($tags);
390
-
391
- $tags_entitied = htmlentities(implode(" ", $tags), ENT_NOQUOTES, 'UTF-8'); // ENT_QUOTES or ENT_NOQUOTES?
392
- $tags_entitied = explode(" ", $tags_entitied);
393
-
394
- $closing_tags_entitied = htmlentities(implode(" ", $closing_tags), ENT_NOQUOTES, 'UTF-8'); // ENT_QUOTES or ENT_NOQUOTES?
395
- $closing_tags_entitied = explode(" ", $closing_tags_entitied);
396
-
397
- $tags_entitied_regexped = array();
398
- $i = 0;
399
- foreach ($tags_entitied as $tag) {
400
- $tag = str_replace("&gt;", "(.*?)&gt;", $tag);
401
- $pattern = "~$tag~";
402
- $tags_entitied_regexped[] = $pattern;
403
-
404
- $matching_tag = $tags[$i];
405
- $matching_tag = str_replace(">", '\1>', $matching_tag);
406
- $tags[$i] = $matching_tag;
407
- $i++;
408
- }
409
-
410
- $closing_tags_entitied_regexped = array();
411
- foreach ($closing_tags_entitied as $tag) {
412
- $pattern = "~" . preg_quote($tag) . "~";
413
- $closing_tags_entitied_regexped[] = $pattern;
414
- }
415
-
416
- $tags = array_merge($tags, $closing_tags);
417
- $tags_entitied = array_merge($tags_entitied_regexped, $closing_tags_entitied_regexped);
418
-
419
- $excerpt = preg_replace($tags_entitied, $tags, $excerpt);
420
-
421
- // In case there are attributes. This is the easiest solution, as
422
- // using quotes and apostrophes un-entitied can't really break
423
- // anything.
424
- $excerpt = str_replace('&quot;', '"', $excerpt);
425
- $excerpt = str_replace('&#039;', "'", $excerpt);
426
- }
427
- else {
428
  // Running htmlentities() for whole posts tends to ruin things.
429
  // However, we want to run htmlentities() for anything inside
430
  // <pre> and <code> tags.
431
- $excerpt = relevanssi_entities_inside($excerpt, "code");
432
- $excerpt = relevanssi_entities_inside($excerpt, "pre");
433
  }
434
  return $excerpt;
435
  }
436
 
437
- function relevanssi_entities_inside($excerpt, $tag) {
438
- $hits = preg_match_all('/<' . $tag . '>(.*?)<\/' . $tag . '>/im', $excerpt, $matches);
439
- if ($hits > 0) {
 
 
 
 
 
 
 
 
 
440
  $replacements = array();
441
- foreach ($matches[1] as $match) {
442
- if (!empty($match)) $replacements[] = "<xxx" . $tag . ">" . htmlentities($match, ENT_QUOTES, 'UTF-8') . "</xxx" . $tag . ">";
 
 
443
  }
444
- if (!empty($replacements)) {
445
- for ($i = 0; $i < count($replacements); $i++) {
446
- $patterns[] = "/<" . $tag . ">(.*?)<\/" . $tag . ">/im";
 
447
  }
448
- $excerpt = preg_replace($patterns, $replacements, $excerpt, 1);
449
  }
450
- $excerpt = str_replace("xxx" . $tag, $tag, $excerpt);
451
  }
452
- return $excerpt;
453
  }
454
 
455
- function relevanssi_generate_closing_tags($tags) {
 
 
 
 
 
 
 
456
  $closing_tags = array();
457
- foreach ($tags as $tag) {
458
- $a = str_replace("<", "</", $tag);
459
- $b = str_replace(">", "/>", $tag);
 
460
  $closing_tags[] = $a;
461
  $closing_tags[] = $b;
462
  }
463
  return $closing_tags;
464
  }
465
 
466
- function relevanssi_remove_nested_highlights($s, $a, $b) {
467
- $offset = 0;
468
- $string = "";
469
- $bits = explode($a, $s);
470
- $new_bits = array($bits[0]);
471
- $in = false;
472
- for ($i = 1; $i < count($bits); $i++) {
473
- if ($bits[$i] === '') continue;
474
-
475
- if (!$in) {
476
- array_push($new_bits, $a);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  $in = true;
478
  }
479
- if (substr_count($bits[$i], $b) > 0) {
480
  $in = false;
481
  }
482
- if (substr_count($bits[$i], $b) > 1) {
483
- $more_bits = explode($b, $bits[$i]);
484
- $j = 0;
485
- $k = count($more_bits) - 2;
486
- $whole_bit = "";
487
- foreach ($more_bits as $bit) {
488
  $whole_bit .= $bit;
489
- if ($j === $k) $whole_bit .= $b;
 
 
490
  $j++;
491
  }
492
- $bits[$i] = $whole_bit;
493
  }
494
- array_push($new_bits, $bits[$i]);
495
  }
496
- $whole = implode('', $new_bits);
497
 
498
  return $whole;
499
  }
500
 
501
- /******
502
- * This code originally written by Ben Boyter
503
- * http://www.boyter.org/2013/04/building-a-search-result-extract-generator-in-php/
 
 
 
 
 
 
 
 
 
504
  */
505
-
506
- // find the locations of each of the words
507
- // Nothing exciting here. The array_unique is required
508
- // unless you decide to make the words unique before passing in
509
- function relevanssi_extract_locations($words, $fulltext) {
510
  $locations = array();
511
- foreach($words as $word) {
512
  $count_locations = 0;
513
- $wordlen = relevanssi_strlen($word);
514
- $loc = relevanssi_stripos($fulltext, $word, 0);
515
- while($loc !== FALSE) {
516
- $locations[] = $loc;
517
- $loc = relevanssi_stripos($fulltext, $word, $loc + $wordlen);
518
  $count_locations++;
519
- if (apply_filters('relevanssi_optimize_excerpts', false)) {
520
- if ($count_locations > 10) break;
521
- // If more than ten locations are found, quit: there's probably a good one in there, and this saves plenty of time
 
 
 
 
 
 
 
 
522
  }
523
- }
524
- }
525
- $locations = array_unique($locations);
526
- sort($locations);
527
-
528
- return $locations;
529
- }
530
 
531
- function relevanssi_count_matches($words, $fulltext) {
532
- $count = 0;
533
- foreach( $words as $word ) {
534
- $word = relevanssi_add_accent_variations($word);
535
 
536
- if (get_option('relevanssi_fuzzy') === 'never') {
537
- $pattern = '/([\s,\.:;\?!\']'.$word.'[\s,\.:;\?!\'])/i';
538
- if (preg_match($pattern, $fulltext, $matches, PREG_OFFSET_CAPTURE)) {
539
- $count += count($matches) - 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  }
541
  }
542
- else {
543
- $pattern = '/([\s,\.:;\?!\']'.$word.')/i';
544
- if (preg_match($pattern, $fulltext, $matches, PREG_OFFSET_CAPTURE)) {
545
- $count += count($matches) - 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
  }
547
- $pattern = '/('.$word.'[\s,\.:;\?!\'])/i';
548
- if (preg_match($pattern, $fulltext, $matches, PREG_OFFSET_CAPTURE)) {
549
- $count += count($matches) - 1;
 
550
  }
551
  }
552
  }
553
-
554
- return $count;
555
- }
556
 
557
- // Work out which is the most relevant portion to display
558
- // This is done by looping over each match and finding the smallest distance between two found
559
- // strings. The idea being that the closer the terms are the better match the snippet would be.
560
- // When checking for matches we only change the location if there is a better match.
561
- // The only exception is where we have only two matches in which case we just take the
562
- // first as will be equally distant.
563
- function relevanssi_determine_snip_location($locations, $prevcount) {
564
- if (!is_array($locations) || empty($locations)) return 0;
565
-
566
- // If we only have 1 match we dont actually do the for loop so set to the first
567
- $startpos = $locations[0];
568
- $loccount = count($locations);
569
- $smallestdiff = PHP_INT_MAX;
570
-
571
- // If we only have 2 skip as its probably equally relevant
572
- if(count($locations) > 2) {
573
- // skip the first as we check 1 behind
574
- for($i=1; $i < $loccount; $i++) {
575
- if($i === $loccount-1) { // at the end
576
- $diff = $locations[$i] - $locations[$i-1];
577
- }
578
- else {
579
- $diff = $locations[$i+1] - $locations[$i];
580
- }
581
-
582
- if($smallestdiff > $diff) {
583
- $smallestdiff = $diff;
584
- $startpos = $locations[$i];
585
- }
586
- }
587
- }
588
-
589
- $startpos = $startpos > $prevcount ? $startpos - $prevcount : 0;
590
 
591
  return $startpos;
592
  }
593
 
594
- // 1/6 ratio on prevcount tends to work pretty well and puts the terms
595
- // in the middle of the extract
596
- function relevanssi_extract_relevant($words, $fulltext, $rellength=300, $prevcount=50) {
597
- $textlength = relevanssi_strlen($fulltext);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
 
599
- if($textlength <= $rellength) {
600
- return array($fulltext, 1, 0);
601
- }
602
 
603
- $locations = relevanssi_extract_locations($words, $fulltext);
604
- $startpos = relevanssi_determine_snip_location($locations,$prevcount);
605
 
606
- // if we are going to snip too much...
607
- if($textlength-$startpos < $rellength) {
608
- $startpos = $startpos - ($textlength-$startpos)/2;
609
- }
610
 
611
- function_exists('mb_substr') ? $substr = 'mb_substr' : $substr = 'substr';
612
- function_exists('mb_strrpos') ? $strrpos = 'mb_strrpos' : $strrpos = 'strrpos';
 
 
 
 
 
 
613
 
614
- $reltext = call_user_func($substr, $fulltext, $startpos, $rellength);
615
 
616
- // check to ensure we dont snip the last word if thats the match
617
- if( $startpos + $rellength < $textlength) {
618
- $reltext = call_user_func($substr, $reltext, 0, call_user_func($strrpos, $reltext, " ")); // remove last word
619
- }
620
 
621
  $start = false;
622
- if($startpos === 0) $start = true;
 
 
623
 
624
- $besthits = count(relevanssi_extract_locations($words, $reltext));
625
 
626
- return array($reltext, $besthits, $start);
627
  }
628
 
629
- function relevanssi_add_accent_variations($word) {
630
- $replacement_arrays = apply_filters('relevanssi_accents_replacement_arrays', array(
631
- "from" => array('a','c','e','i','o','u','n','ss'),
632
- "to" => array('(a|á|à|â)','(c|ç)', '(e|é|è|ê|ë)','(i|í|ì|î|ï)','(o|ó|ò|ô|õ)','(u|ú|ù|ü|û)','(n|ñ)','(ss|ß)'),
633
- ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
 
635
- $word = str_ireplace($replacement_arrays['from'], $replacement_arrays['to'], $word);
636
 
637
- return $word;
638
  }
639
 
640
- function relevanssi_get_custom_field_content($post_id) {
641
- $custom_field_content = "";
 
 
 
 
 
 
 
642
  $remove_underscore_fields = false;
643
 
644
  $custom_fields = relevanssi_get_custom_fields();
645
- if (isset($custom_fields) && $custom_fields === 'all')
646
- $custom_fields = get_post_custom_keys($post_id);
647
- if (isset($custom_fields) && $custom_fields === 'visible') {
648
- $custom_fields = get_post_custom_keys($post_id);
 
649
  $remove_underscore_fields = true;
650
  }
651
- $custom_fields = apply_filters('relevanssi_index_custom_fields', $custom_fields);
 
652
 
653
- if (function_exists('relevanssi_get_child_pdf_content')) $custom_field_content .= " " . relevanssi_get_child_pdf_content($post_id);
 
 
654
 
655
- if (is_array($custom_fields)) {
656
- $custom_fields = array_unique($custom_fields); // no reason to index duplicates
657
 
658
  $repeater_fields = array();
659
- if (function_exists('relevanssi_add_repeater_fields')) relevanssi_add_repeater_fields($custom_fields, $post_id);
 
 
660
 
661
- foreach ($custom_fields as $field) {
662
- if ($remove_underscore_fields) {
663
- if (substr($field, 0, 1) === '_') continue;
 
 
664
  }
665
- $values = apply_filters('relevanssi_custom_field_value', get_post_meta($post_id, $field, false), $field, $post_id);
666
- if ("" === $values) continue;
667
- foreach ($values as $value) {
668
- // Quick hack : allow indexing of PODS relationship custom fields // TMV
669
- if (is_array($value) && isset($value['post_title'])) $value = $value['post_title'];
670
-
671
- // Flatten other array data
672
- if (is_array($value)) $value = implode(" ", $value);
673
- $custom_field_content .= " " . $value;
 
 
 
 
 
 
 
674
  }
675
  }
676
  }
677
- return apply_filters('relevanssi_excerpt_custom_field_content', $custom_field_content);
 
 
 
 
 
678
  }
679
 
680
- function relevanssi_remove_page_builder_shortcodes($content) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
681
  $search_array = apply_filters('relevanssi_page_builder_shortcodes', array(
682
- '/\[et_pb_code.*?\].*\[\/et_pb_code\]/', // Code and sidebars:
683
- '/\[et_pb_sidebar.*?\].*\[\/et_pb_sidebar\]/', // remove contents and tags
684
- '/\[\/?et_pb.*?\]/', // Everything else: keep content
685
- '/\[vc_raw_html.*?\].*\[\/vc_raw_html\]/', // Raw HTML: remove contents
 
 
686
  '/\[\/?vc.*?\]/',
687
  '/\[\/?mk.*?\]/',
688
  '/\[\/?cs_.*?\]/',
689
  '/\[\/?av_.*?\]/',
690
- '/\[maxmegamenu.*?\]/', // Max Mega Menu doesn't work in excerpts
 
 
691
  ));
692
- $content = preg_replace($search_array, '', $content);
693
  return $content;
694
  }
695
 
696
  /**
697
- * Kills the autoembed filter hook on the_content. It's an object hook, so this isn't as simple as doing remove_filter().
698
- * This needs to be done, because autoembed can take a very, very long time.
 
 
 
 
699
  */
700
  function relevanssi_kill_autoembed() {
701
  global $wp_filter;
702
- if (isset($wp_filter['the_content']->callbacks)) {
703
- foreach ($wp_filter['the_content']->callbacks as $priority => $bucket) {
704
- foreach ($bucket as $key => $value) {
705
- if (substr($key, -9) === "autoembed") {
706
- unset($wp_filter['the_content']->callbacks[$priority][$key]);
707
  }
708
  }
709
  }
710
-
711
  }
712
  }
713
-
714
- ?>
1
  <?php
2
+ /**
3
+ * /lib/excerpts-highlights.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
 
11
+ /**
12
+ * Prints out the post excerpt.
13
+ *
14
+ * Prints out the post excerpt from $post->post_excerpt, unless the post is
15
+ * protected. Only works in the Loop.
16
+ *
17
+ * @global $post The global post object.
18
+ */
19
  function relevanssi_the_excerpt() {
20
+ global $post;
21
+ if ( ! post_password_required( $post ) ) {
22
+ echo '<p>' . $post->post_excerpt . '</p>'; // WPCS: XSS ok.
23
+ } else {
24
+ echo esc_html__( 'There is no excerpt because this is a protected post.' );
 
25
  }
26
  }
27
 
28
+ /**
29
+ * Generates an excerpt for a post.
30
+ *
31
+ * @global $post The global post object.
32
+ *
33
+ * @param object $t_post The post object.
34
+ * @param string $query The search query.
35
+ *
36
+ * @return string The created excerpt.
37
+ */
38
+ function relevanssi_do_excerpt( $t_post, $query ) {
39
  global $post;
 
 
 
40
 
41
+ // Back up the global post object, and replace it with the post we're working on.
42
+ $old_global_post = null;
43
+ if ( null !== $post ) {
44
+ $old_global_post = $post;
45
+ }
46
+ $post = $t_post; // WPCS: override ok, must do because shortcodes etc. expect it.
47
 
48
+ $remove_stopwords = true;
 
 
49
 
50
+ /**
51
+ * Filters the search query before excerpt-building.
52
+ *
53
+ * Allows filtering the search query before generating an excerpt. This can
54
+ * useful if you modifications to the search query, and it may help when working
55
+ * with stemming.
56
+ *
57
+ * @param string $query The search query.
58
+ */
59
+ $query = apply_filters( 'relevanssi_excerpt_query', $query );
60
+
61
+ // Minimum word length is -1, we don't care about it right now.
62
+ $terms = relevanssi_tokenize( $query, $remove_stopwords, -1 );
63
+
64
+ // These shortcodes cause problems with Relevanssi excerpts.
65
+ $problem_shortcodes = array( 'layerslider', 'responsive-flipbook', 'breadcrumb', 'robogallery', 'gravityview' );
66
+ /**
67
+ * Filters the excerpt-building problem shortcodes.
68
+ *
69
+ * Some shortcodes cause problems in Relevanssi excerpt-building. These
70
+ * shortcodes are disabled before building the excerpt. This filter allows
71
+ * modifying the list of shortcodes.
72
+ *
73
+ * @param array $problem_shortcodes Array of problematic shortcode names.
74
+ */
75
+ $problem_shortcodes = apply_filters( 'relevanssi_disable_shortcodes_excerpt', $problem_shortcodes );
76
+ foreach ( $problem_shortcodes as $shortcode ) {
77
+ remove_shortcode( $shortcode );
78
+ }
79
 
80
+ /**
81
+ * Filters the post content before 'the_content'.
82
+ *
83
+ * Filters the post content in excerpt building process before 'the_content'
84
+ * filter is applied.
85
+ *
86
+ * @param string $content The post content.
87
+ * @param object $post The post object.
88
+ * @param string $query The search query.
89
+ */
90
+ $content = apply_filters( 'relevanssi_pre_excerpt_content', $post->post_content, $post, $query );
91
+
92
+ // Add the custom field content.
93
+ if ( 'on' === get_option( 'relevanssi_excerpt_custom_fields' ) ) {
94
+ $content .= relevanssi_get_custom_field_content( $post->ID );
95
  }
96
 
97
+ // Autoembed discovery can really slow down excerpt-building.
98
  relevanssi_kill_autoembed();
99
 
100
+ // This will print out the attachment file name in front of the excerpt, and we
101
+ // don't want that.
102
+ remove_filter( 'the_content', 'prepend_attachment' );
103
 
104
+ /** This filter is documented in wp-includes/post-template.php */
105
+ $content = apply_filters( 'the_content', $content );
 
106
 
107
+ /**
108
+ * Filters the post content after 'the_content'.
109
+ *
110
+ * Filters the post content in excerpt building process after 'the_content'
111
+ * filter is applied.
112
+ *
113
+ * @param string $content The post content.
114
+ * @param object $post The post object.
115
+ * @param string $query The search query.
116
+ */
117
+ $content = apply_filters( 'relevanssi_excerpt_content', $content, $post, $query );
118
 
119
+ // Removes <script>, <embed> &c with content.
120
+ $content = relevanssi_strip_invisibles( $content );
121
+
122
+ // Add spaces between tags to avoid getting words stuck together.
123
+ $content = preg_replace( '/(<\/[^>]+?>)(<[^>\/][^>]*?>)/', '$1 $2', $content );
124
+
125
+ // This removes the tags, but leaves the content.
126
+ $content = strip_tags( $content, get_option( 'relevanssi_excerpt_allowable_tags', '' ) );
127
+
128
+ // Replace linefeeds and carriage returns with spaces.
129
+ $content = preg_replace( "/\n\r|\r\n|\n|\r/", ' ', $content );
130
 
131
+ if ( 'OR' === get_option( 'relevanssi_implicit_operator' ) || 'on' === get_option( 'relevanssi_index_synonyms' ) ) {
132
+ $query = relevanssi_add_synonyms( $query );
133
+ }
134
 
135
+ // Find the appropriate spot from the post.
136
+ $excerpt_data = relevanssi_create_excerpt( $content, $terms, $query );
137
+
138
+ if ( 'none' !== get_option( 'relevanssi_index_comments' ) ) {
139
+ // Use comment content as source material for excerpts.
140
+ $comment_content = relevanssi_get_comments( $post->ID );
141
+ $comment_content = preg_replace( '/(<\/[^>]+?>)(<[^>\/][^>]*?>)/', '$1 $2', $comment_content );
142
+ $comment_content = strip_tags( $comment_content, get_option( 'relevanssi_excerpt_allowable_tags', '' ) );
143
+ if ( ! empty( $comment_content ) ) {
144
+ $comment_excerpts = relevanssi_create_excerpt( $comment_content, $terms, $query );
145
+ if ( $comment_excerpts[1] > $excerpt_data[1] ) {
146
+ // The excerpt created from comments is better than the one created from post data.
147
  $excerpt_data = $comment_excerpts;
148
  }
149
  }
150
  }
151
 
152
+ if ( 'off' !== get_option( 'relevanssi_index_excerpt' ) ) {
153
  $excerpt_content = $post->post_excerpt;
154
+ $excerpt_content = strip_tags( $excerpt_content, get_option( 'relevanssi_excerpt_allowable_tags', '' ) );
155
+
156
+ if ( ! empty( $excerpt_content ) ) {
157
+ $excerpt_excerpts = relevanssi_create_excerpt( $excerpt_content, $terms, $query );
158
+ if ( $excerpt_excerpts[1] > $excerpt_data[1] ) {
159
+ // The excerpt created from post excerpt is the best we found so far.
160
  $excerpt_data = $excerpt_excerpts;
161
  }
162
  }
163
  }
164
 
 
 
165
  $excerpt = $excerpt_data[0];
166
+ $excerpt = trim( $excerpt );
167
+ /**
168
+ * Filters the excerpt.
169
+ *
170
+ * Filters the post excerpt generated by Relevanssi before the highlighting is
171
+ * applied.
172
+ *
173
+ * @param string $excerpt The excerpt.
174
+ */
175
+ $excerpt = apply_filters( 'relevanssi_excerpt', $excerpt );
176
+
177
+ $whole_post_excerpted = false;
178
+ if ( $excerpt === $post->post_content ) {
179
+ $whole_post_excerpted = true;
180
+ }
181
 
182
+ if ( empty( $excerpt ) && ! empty( $post->post_excerpt ) ) {
 
183
  $excerpt = $post->post_excerpt;
184
+ $excerpt = strip_tags( $excerpt, get_option( 'relevanssi_excerpt_allowable_tags', '' ) );
185
+ }
 
 
186
 
187
+ /**
188
+ * Filters the ellipsis Relevanssi uses in excerpts.
189
+ *
190
+ * @param string $ellipsis Default '...'.
191
+ */
192
+ $ellipsis = apply_filters( 'relevanssi_ellipsis', '...' );
193
+
194
+ $highlight = get_option( 'relevanssi_highlight' );
195
+ if ( 'none' !== $highlight ) {
196
+ if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
197
+ $excerpt = relevanssi_highlight_terms( $excerpt, $query );
198
  }
199
  }
200
 
201
+ $excerpt = relevanssi_close_tags( $excerpt );
202
 
203
+ $excerpt_is_from_beginning_of_the_post = $excerpt_data[2];
204
+ if ( ! $whole_post_excerpted ) {
205
+ if ( ! $excerpt_is_from_beginning_of_the_post && ! empty( $excerpt ) ) {
206
  $excerpt = $ellipsis . $excerpt;
 
207
  }
208
 
209
+ if ( ! empty( $excerpt ) ) {
210
  $excerpt = $excerpt . $ellipsis;
211
+ }
212
  }
213
 
214
+ if ( null !== $old_global_post ) {
215
+ $post = $old_global_post; // WPCS: override ok, returning the overridden value.
216
+ }
217
 
218
  return $excerpt;
219
  }
221
  /**
222
  * Creates an excerpt from content.
223
  *
224
+ * @param string $content The content.
225
+ * @param array $terms The search terms, tokenized.
226
+ * @param string $query The search query.
227
+ *
228
+ * @return array Element 0 is the excerpt, element 1 the number of term hits, element 2 is
229
  * true, if the excerpt is from the start of the content.
230
  */
231
+ function relevanssi_create_excerpt( $content, $terms, $query ) {
232
+ // If you need to modify these on the go, use 'pre_option_relevanssi_excerpt_length'
233
+ // and 'pre_option_relevanssi_excerpt_type' filters.
234
+ $excerpt_length = get_option( 'relevanssi_excerpt_length' );
235
+ $type = get_option( 'relevanssi_excerpt_type' );
236
 
237
  $best_excerpt_term_hits = -1;
 
238
 
239
+ $excerpt = '';
240
+ $content = ' ' . preg_replace( '/\s+/u', ' ', $content );
241
 
242
+ // Finds all the phrases in the query.
243
+ $phrases = relevanssi_extract_phrases( stripslashes( $query ) );
244
 
245
+ /**
246
+ * This process generates an array of terms, which has single terms and all the
247
+ * phrases.
248
+ */
249
+ $remove_stopwords = false;
250
  $non_phrase_terms = array();
251
+ foreach ( $phrases as $phrase ) {
252
+ $phrase_terms = array_keys( relevanssi_tokenize( $phrase, $remove_stopwords ) );
253
+ foreach ( array_keys( $terms ) as $term ) { // array_keys(), because tokenized terms have the term as key.
254
+ if ( ! in_array( $term, $phrase_terms, true ) ) {
255
+ $non_phrase_terms[ $term ] = true;
256
  }
257
  }
258
 
259
+ $terms = $non_phrase_terms;
260
+ $terms[ $phrase ] = true;
261
  }
262
 
263
+ // Sort the longest search terms first, because those are generally more significant.
264
+ uksort( $terms, 'relevanssi_strlen_sort' );
265
 
266
  $start = false;
267
+ if ( 'chars' === $type ) {
268
+ /**
269
+ * Character-based excerpts use the relevanssi_extract_relevant() to figure out
270
+ * the best part of the post to use.
271
+ */
272
+ $prev_count = floor( $excerpt_length / 2 );
273
+
274
+ list( $excerpt, $best_excerpt_term_hits, $start ) = relevanssi_extract_relevant( array_keys( $terms ), $content, $excerpt_length, $prev_count );
275
+ } else {
276
+ /**
277
+ * Word-based excerpts split the content in an array of individual words and
278
+ * takes slices.
279
+ */
280
+ $words = array_filter( explode( ' ', $content ) );
281
+ $i = 0;
282
+ $tries = 0;
283
+ $count_words = count( $words );
284
+ while ( $i < $count_words ) {
285
+ if ( $i + $excerpt_length > $count_words ) {
286
+ $i = $count_words - $excerpt_length;
287
+ if ( $i < 0 ) {
288
+ $i = 0;
289
+ }
290
  }
291
 
292
+ $excerpt_slice = array_slice( $words, $i, $excerpt_length );
293
+ $excerpt_slice = ' ' . implode( ' ', $excerpt_slice );
294
 
295
+ $term_hits = 0;
296
+ $count_matches = relevanssi_count_matches( array_keys( $terms ), $excerpt_slice );
297
+ if ( $count_matches > 0 ) {
 
298
  $tries++;
299
  }
300
+ if ( $count_matches > 0 && $count_matches > $best_excerpt_term_hits ) {
301
+ $best_excerpt_term_hits = $count_matches;
302
+ $excerpt = $excerpt_slice;
303
  }
304
 
305
+ /**
306
+ * Enables the excerpt optimization.
307
+ *
308
+ * If your posts are very long, building excerpts can be really slow.
309
+ * To speed up the process, you can enable optimization, which means
310
+ * Relevanssi only creates 50 excerpt candidates.
311
+ *
312
+ * @param boolean Return true to enable optimization, default false.
313
+ */
314
+ if ( apply_filters( 'relevanssi_optimize_excerpts', false ) ) {
315
+ if ( $tries > 50 ) {
316
+ // An optimization trick: try only 50 times.
317
+ break;
318
+ }
319
  }
320
 
321
  $i += $excerpt_length;
322
  }
323
 
324
+ if ( '' === $excerpt ) {
325
+ // Nothing found, take the beginning of the post.
326
+ $excerpt = explode( ' ', $content, $excerpt_length );
327
+ array_pop( $excerpt );
328
+ $excerpt = implode( ' ', $excerpt );
329
+ $start = true;
330
  }
331
  }
332
 
333
+ return array( $excerpt, $best_excerpt_term_hits, $start );
334
  }
335
 
336
+ /**
337
+ * Manages the highlighting in documents.
338
+ *
339
+ * Uses relevanssi_highlight_terms() and relevanssi_nonlocal_highlighting() to do
340
+ * the highlighting. Attached to 'the_content' and 'comment_text' filter hooks.
341
+ *
342
+ * @global object $wp_query The global WP_Query object.
343
+ *
344
+ * @param string $content The content to highlight.
345
+ *
346
+ * @return string The content with highlights.
347
+ */
348
+ function relevanssi_highlight_in_docs( $content ) {
349
  global $wp_query;
350
+ if ( is_singular() && is_main_query() ) {
351
+ if ( isset( $wp_query->query_vars['highlight'] ) ) {
352
+ // Local search.
353
+ $query = relevanssi_add_synonyms( $wp_query->query_vars['highlight'] );
354
  $in_docs = true;
355
+
356
+ $highlighted_content = relevanssi_highlight_terms( $content, $query, $in_docs );
357
+ if ( ! empty( $highlighted_content ) ) {
358
+ // Sometimes the content comes back empty; until I figure out why, this tries to be a solution.
359
+ $content = $highlighted_content;
360
+ }
361
  }
362
 
363
+ if ( function_exists( 'relevanssi_nonlocal_highlighting' ) ) {
364
+ $content = relevanssi_nonlocal_highlighting( $content );
365
  }
366
  }
367
 
368
  return $content;
369
  }
370
 
371
+ /**
372
+ * Adds highlighting to content.
373
+ *
374
+ * Adds highlighting to content based on Relevanssi highlighting settings (if you
375
+ * want to override the settings, 'pre_option_relevanssi_highlight' filter hook
376
+ * is your friend).
377
+ *
378
+ * @param string $content The content to highlight.
379
+ * @param string $query The search query.
380
+ * @param boolean $in_docs Are we highlighting post content? Default false.
381
+ *
382
+ * @return string The $content with highlighting.
383
+ */
384
+ function relevanssi_highlight_terms( $content, $query, $in_docs = false ) {
385
+ $type = get_option( 'relevanssi_highlight' );
386
+ if ( 'none' === $type ) {
387
+ return $content;
388
  }
389
 
390
+ switch ( $type ) {
391
+ case 'mark':
392
+ $start_emp = '<mark>';
393
+ $end_emp = '</mark>';
394
  break;
395
+ case 'strong':
396
+ $start_emp = '<strong>';
397
+ $end_emp = '</strong>';
398
  break;
399
+ case 'em':
400
+ $start_emp = '<em>';
401
+ $end_emp = '</em>';
402
  break;
403
+ case 'col':
404
+ $col = get_option( 'relevanssi_txt_col' );
405
+ if ( ! $col ) {
406
+ $col = '#ff0000';
407
+ }
408
  $start_emp = "<span style='color: $col'>";
409
+ $end_emp = '</span>';
410
  break;
411
+ case 'bgcol':
412
+ $col = get_option( 'relevanssi_bg_col' );
413
+ if ( ! $col ) {
414
+ $col = '#ff0000';
415
+ }
416
  $start_emp = "<span style='background-color: $col'>";
417
+ $end_emp = '</span>';
418
  break;
419
+ case 'css':
420
+ $css = get_option( 'relevanssi_css' );
421
+ if ( ! $css ) {
422
+ $css = 'color: #ff0000';
423
+ }
424
  $start_emp = "<span style='$css'>";
425
+ $end_emp = '</span>';
426
  break;
427
+ case 'class':
428
+ $css = get_option( 'relevanssi_class' );
429
+ if ( ! $css ) {
430
+ $css = 'relevanssi-query-term';
431
+ }
432
  $start_emp = "<span class='$css'>";
433
+ $end_emp = '</span>';
434
  break;
435
  default:
436
+ return $content;
437
  }
438
 
439
+ $start_emp_token = '**{}[';
440
+ $end_emp_token = ']}**';
441
 
442
+ if ( function_exists( 'mb_internal_encoding' ) ) {
443
+ mb_internal_encoding( 'UTF-8' );
444
+ }
445
 
446
+ /**
447
+ * Runs before tokenizing the terms in highlighting.
448
+ */
449
+ do_action( 'relevanssi_highlight_tokenize' );
450
 
451
+ // Setting min_word_length to 2, in order to avoid 1-letter highlights.
452
  $min_word_length = 2;
453
+ /**
454
+ * Allows creating one-letter highlights.
455
+ *
456
+ * @param boolean Set to true to enable one-letter highlights.
457
+ */
458
+ if ( apply_filters( 'relevanssi_allow_one_letter_highlights', false ) ) {
459
+ $min_word_length = 1;
460
+ }
461
 
462
+ $remove_stopwords = true;
463
+ $terms = array_keys( relevanssi_tokenize( $query, $remove_stopwords, $min_word_length ) );
464
 
465
+ if ( is_array( $query ) ) {
466
+ $query = implode( ' ', $query );
467
+ }
468
+ $phrases = relevanssi_extract_phrases( stripslashes( $query ) );
469
 
470
+ $remove_stopwords = false;
471
  $non_phrase_terms = array();
472
+ foreach ( $phrases as $phrase ) {
473
+ $phrase_terms = array_keys( relevanssi_tokenize( $phrase, $remove_stopwords ) );
474
+ foreach ( $terms as $term ) {
475
+ if ( ! in_array( $term, $phrase_terms, true ) ) {
476
  $non_phrase_terms[] = $term;
477
  }
478
  }
479
+ $terms = $non_phrase_terms;
480
  $terms[] = $phrase;
481
  }
482
 
483
+ usort( $terms, 'relevanssi_strlen_sort' );
484
 
485
+ $word_boundaries = false;
486
+ if ( 'on' === get_option( 'relevanssi_word_boundaries', 'on' ) ) {
487
+ $word_boundaries = true;
488
+ }
 
489
 
490
+ foreach ( $terms as $term ) {
491
+ $pr_term = preg_quote( $term, '/' );
492
+ $pr_term = relevanssi_add_accent_variations( $pr_term );
493
 
494
+ $undecoded_content = $content;
495
+ $content = html_entity_decode( $content, ENT_QUOTES, 'UTF-8' );
 
 
496
 
497
+ if ( $word_boundaries ) {
498
+ $regex = "/(\b$pr_term\b)/iu";
499
+ if ( 'none' !== get_option( 'relevanssi_fuzzy' ) ) {
500
+ $regex = "/(\b$pr_term|$pr_term\b)/iu";
501
+ }
502
+
503
+ $content = preg_replace( $regex, $start_emp_token . '\\1' . $end_emp_token, $content );
504
+ if ( empty( $content ) ) {
505
+ $content = preg_replace( $regex, $start_emp_token . '\\1' . $end_emp_token, $undecoded_content );
506
+ }
507
+ } else {
508
+ $content = preg_replace( "/($pr_term)/iu", $start_emp_token . '\\1' . $end_emp_token, $content );
509
+ if ( empty( $content ) ) {
510
+ $content = preg_replace( "/($pr_term)/iu", $start_emp_token . '\\1' . $end_emp_token, $undecoded_content );
511
+ }
512
  }
513
 
514
+ $preg_start = preg_quote( $start_emp_token );
515
+ $preg_end = preg_quote( $end_emp_token );
516
 
517
+ if ( preg_match_all( '/<.*>/U', $content, $matches ) > 0 ) {
518
+ // Remove highlights from inside HTML tags.
519
+ foreach ( $matches as $match ) {
520
+ $new_match = str_replace( $start_emp_token, '', $match );
521
+ $new_match = str_replace( $end_emp_token, '', $new_match );
522
+ $content = str_replace( $match, $new_match, $content );
523
  }
524
  }
525
 
526
+ if ( preg_match_all( '/&.*;/U', $content, $matches ) > 0 ) {
527
+ // Remove highlights from inside HTML entities.
528
+ foreach ( $matches as $match ) {
529
+ $new_match = str_replace( $start_emp_token, '', $match );
530
+ $new_match = str_replace( $end_emp_token, '', $new_match );
531
+ $content = str_replace( $match, $new_match, $content );
532
  }
533
  }
534
 
535
+ if ( preg_match_all( '/<(style|script|object|embed)>.*<\/(style|script|object|embed)>/U', $content, $matches ) > 0 ) {
536
+ // Remove highlights in style, object, embed and script tags.
537
+ foreach ( $matches as $match ) {
538
+ $new_match = str_replace( $start_emp_token, '', $match );
539
+ $new_match = str_replace( $end_emp_token, '', $new_match );
540
+ $content = str_replace( $match, $new_match, $content );
541
  }
542
  }
543
  }
544
 
545
+ $content = relevanssi_remove_nested_highlights( $content, $start_emp_token, $end_emp_token );
546
+ $content = relevanssi_fix_entities( $content, $in_docs );
547
 
548
+ $content = str_replace( $start_emp_token, $start_emp, $content );
549
+ $content = str_replace( $end_emp_token, $end_emp, $content );
550
+ $content = str_replace( $end_emp . $start_emp, '', $content );
551
+ if ( function_exists( 'mb_ereg_replace' ) ) {
552
  $pattern = $end_emp . '\s*' . $start_emp;
553
+ $content = mb_ereg_replace( $pattern, ' ', $content );
554
  }
555
 
556
+ return $content;
557
  }
558
 
559
+ /**
560
+ * Fixes problems with entities.
561
+ *
562
+ * For excerpts, runs htmlentities() on the excerpt, then converts the allowed tags
563
+ * back into tags.
564
+ *
565
+ * @param string $excerpt The excerpt to fix.
566
+ * @param boolean $in_docs If true, we are manipulating post content, and need to
567
+ * work in a different fashion.
568
+ *
569
+ * @return string The $excerpt with entities fixed.
570
+ */
571
+ function relevanssi_fix_entities( $excerpt, $in_docs ) {
572
+ if ( ! $in_docs ) {
573
+ // For excerpts, use htmlentities().
574
+ $excerpt = htmlentities( $excerpt, ENT_NOQUOTES, 'UTF-8' );
575
 
576
+ // Except for allowed tags, which are turned back into tags.
577
+ $tags = get_option( 'relevanssi_excerpt_allowable_tags', '' );
578
+ $tags = trim( str_replace( '<', ' <', $tags ) );
579
+ $tags = explode( ' ', $tags );
580
 
581
+ $closing_tags = relevanssi_generate_closing_tags( $tags );
 
 
 
582
 
583
+ $tags_entitied = htmlentities( implode( ' ', $tags ), ENT_NOQUOTES, 'UTF-8' );
584
+ $tags_entitied = explode( ' ', $tags_entitied );
585
+
586
+ $closing_tags_entitied = htmlentities( implode( ' ', $closing_tags ), ENT_NOQUOTES, 'UTF-8' );
587
+ $closing_tags_entitied = explode( ' ', $closing_tags_entitied );
588
+
589
+ $tags_entitied_regexped = array();
590
+
591
+ $i = 0;
592
+ foreach ( $tags_entitied as $tag ) {
593
+ $tag = str_replace( '&gt;', '(.*?)&gt;', $tag );
594
+ $pattern = "~$tag~";
595
+
596
+ $tags_entitied_regexped[] = $pattern;
597
+
598
+ $matching_tag = $tags[ $i ];
599
+ $matching_tag = str_replace( '>', '\1>', $matching_tag );
600
+ $tags[ $i ] = $matching_tag;
601
+ $i++;
602
+ }
603
+
604
+ $closing_tags_entitied_regexped = array();
605
+ foreach ( $closing_tags_entitied as $tag ) {
606
+ $pattern = '~' . preg_quote( $tag ) . '~';
607
+
608
+ $closing_tags_entitied_regexped[] = $pattern;
609
+ }
610
+
611
+ $tags = array_merge( $tags, $closing_tags );
612
+ $tags_entitied = array_merge( $tags_entitied_regexped, $closing_tags_entitied_regexped );
613
+
614
+ $excerpt = preg_replace( $tags_entitied, $tags, $excerpt );
615
+
616
+ // In case there are attributes. This is the easiest solution, as
617
+ // using quotes and apostrophes un-entitied can't really break
618
+ // anything.
619
+ $excerpt = str_replace( '&quot;', '"', $excerpt );
620
+ $excerpt = str_replace( '&#039;', "'", $excerpt );
621
+ } else {
 
 
 
 
622
  // Running htmlentities() for whole posts tends to ruin things.
623
  // However, we want to run htmlentities() for anything inside
624
  // <pre> and <code> tags.
625
+ $excerpt = relevanssi_entities_inside( $excerpt, 'code' );
626
+ $excerpt = relevanssi_entities_inside( $excerpt, 'pre' );
627
  }
628
  return $excerpt;
629
  }
630
 
631
+ /**
632
+ * Runs htmlentities() for content inside specified tags.
633
+ *
634
+ * @param string $content The content.
635
+ * @param string $tag The tag.
636
+ *
637
+ * @return string $content The content with HTML code inside the $tag tags
638
+ * ran through htmlentities().
639
+ */
640
+ function relevanssi_entities_inside( $content, $tag ) {
641
+ $hits = preg_match_all( '/<' . $tag . '>(.*?)<\/' . $tag . '>/im', $content, $matches );
642
+ if ( $hits > 0 ) {
643
  $replacements = array();
644
+ foreach ( $matches[1] as $match ) {
645
+ if ( ! empty( $match ) ) {
646
+ $replacements[] = '<xxx' . $tag . '>' . htmlentities( $match, ENT_QUOTES, 'UTF-8' ) . '</xxx' . $tag . '>';
647
+ }
648
  }
649
+ if ( ! empty( $replacements ) ) {
650
+ $count_replacements = count( $replacements );
651
+ for ( $i = 0; $i < $count_replacements; $i++ ) {
652
+ $patterns[] = '/<' . $tag . '>(.*?)<\/' . $tag . '>/im';
653
  }
654
+ $content = preg_replace( $patterns, $replacements, $content, 1 );
655
  }
656
+ $content = str_replace( 'xxx' . $tag, $tag, $content );
657
  }
658
+ return $content;
659
  }
660
 
661
+ /**
662
+ * Generates closing tags for an array of tags.
663
+ *
664
+ * @param array $tags Array of tag names.
665
+ *
666
+ * @return array $closing_tags Array of closing tags.
667
+ */
668
+ function relevanssi_generate_closing_tags( $tags ) {
669
  $closing_tags = array();
670
+ foreach ( $tags as $tag ) {
671
+ $a = str_replace( '<', '</', $tag );
672
+ $b = str_replace( '>', '/>', $tag );
673
+
674
  $closing_tags[] = $a;
675
  $closing_tags[] = $b;
676
  }
677
  return $closing_tags;
678
  }
679
 
680
+ /**
681
+ * Removes nested highlights from a string.
682
+ *
683
+ * If there are highlights within highlights in a string, this function will clean
684
+ * out the nested highlights, leaving just the outmost highlight tokens.
685
+ *
686
+ * @param string $string The content.
687
+ * @param string $begin The beginning highlight token.
688
+ * @param string $end The ending highlight token.
689
+ *
690
+ * @return string The string with nested highlights cleaned out.
691
+ */
692
+ function relevanssi_remove_nested_highlights( $string, $begin, $end ) {
693
+ $offset = 0;
694
+ $bits = explode( $begin, $string );
695
+ $new_bits = array( $bits[0] );
696
+ $count_bits = count( $bits );
697
+ $in = false;
698
+ for ( $i = 1; $i < $count_bits; $i++ ) {
699
+ if ( '' === $bits[ $i ] ) {
700
+ continue;
701
+ }
702
+
703
+ if ( ! $in ) {
704
+ array_push( $new_bits, $begin );
705
  $in = true;
706
  }
707
+ if ( substr_count( $bits[ $i ], $end ) > 0 ) {
708
  $in = false;
709
  }
710
+ if ( substr_count( $bits[ $i ], $end ) > 1 ) {
711
+ $more_bits = explode( $end, $bits[ $i ] );
712
+ $j = 0;
713
+ $k = count( $more_bits ) - 2;
714
+ $whole_bit = '';
715
+ foreach ( $more_bits as $bit ) {
716
  $whole_bit .= $bit;
717
+ if ( $j === $k ) {
718
+ $whole_bit .= $end;
719
+ }
720
  $j++;
721
  }
722
+ $bits[ $i ] = $whole_bit;
723
  }
724
+ array_push( $new_bits, $bits[ $i ] );
725
  }
726
+ $whole = implode( '', $new_bits );
727
 
728
  return $whole;
729
  }
730
 
731
+ /**
732
+ * Finds the locations of each word.
733
+ *
734
+ * Originally lifted from http://www.boyter.org/2013/04/building-a-search-result-extract-generator-in-php/
735
+ * Finds the location of each word in the fulltext.
736
+ *
737
+ * @author Ben Boyter
738
+ *
739
+ * @param array $words An array of words to locate.
740
+ * @param string $fulltext The fulltext where to find them.
741
+ *
742
+ * @return array Array of locations.
743
  */
744
+ function relevanssi_extract_locations( $words, $fulltext ) {
 
 
 
 
745
  $locations = array();
746
+ foreach ( $words as $word ) {
747
  $count_locations = 0;
748
+ $wordlen = relevanssi_strlen( $word );
749
+ $loc = relevanssi_stripos( $fulltext, $word, 0 );
750
+ while ( false !== $loc ) {
751
+ $locations[] = $loc;
752
+ $loc = relevanssi_stripos( $fulltext, $word, $loc + $wordlen );
753
  $count_locations++;
754
+ /**
755
+ * Optimizes the excerpt creation.
756
+ *
757
+ * @param boolean If true, stop looking after ten locations are found.
758
+ */
759
+ if ( apply_filters( 'relevanssi_optimize_excerpts', false ) ) {
760
+ // If more than ten locations are found, quit: there's probably a
761
+ // good one in there, and this saves plenty of time.
762
+ if ( $count_locations > 10 ) {
763
+ break;
764
+ }
765
  }
766
+ }
767
+ }
768
+
769
+ $locations = array_unique( $locations );
770
+ sort( $locations );
 
 
771
 
772
+ return $locations;
773
+ }
 
 
774
 
775
+ /**
776
+ * Counts how many times the words appear in the text.
777
+ *
778
+ * @param array $words An array of words.
779
+ * @param string $complete_text The text where to count the words.
780
+ *
781
+ * @return int Number of times the words appear in the text.
782
+ */
783
+ function relevanssi_count_matches( $words, $complete_text ) {
784
+ $count = 0;
785
+ $lowercase_text = relevanssi_strtolower( $complete_text, 'UTF-8' );
786
+ $text = '';
787
+
788
+ $count_words = count( $words );
789
+ for ( $t = 0; $t < $count_words; $t++ ) {
790
+ $word_slice = relevanssi_strtolower( $words[ $t ], 'UTF-8' );
791
+ $lines = explode( $word_slice, $lowercase_text );
792
+ if ( count( $lines ) > 1 ) {
793
+ $count_lines = count( $lines );
794
+ for ( $tt = 0; $tt < $count_lines; $tt++ ) {
795
+ if ( $tt < ( count( $lines ) - 1 ) ) {
796
+ $text = $text . $lines[ $tt ] . '=***=';
797
+ } else {
798
+ $text = $text . $lines[ $tt ];
799
+ }
800
  }
801
  }
802
+ }
803
+
804
+ $lines = explode( '=***=', $text );
805
+ $count = count( $lines ) - 1;
806
+ if ( $count < 0 ) {
807
+ $count = 0;
808
+ }
809
+
810
+ return $count;
811
+ }
812
+
813
+ /**
814
+ * Works out which is the most relevant portion to display.
815
+ *
816
+ * This is done by looping over each match and finding the smallest distance
817
+ * between two found strings. The idea being that the closer the terms are the better
818
+ * match the snippet would be. When checking for matches we only change the location
819
+ * if there is a better match. The only exception is where we have only two matches
820
+ * in which case we just take the first as will be equally distant.
821
+ *
822
+ * @author Ben Boyter
823
+ *
824
+ * @param array $locations Locations of the words.
825
+ * @param int $prevcount How much text to include before the location.
826
+ *
827
+ * @return int Starting position for the snippet.
828
+ */
829
+ function relevanssi_determine_snip_location( $locations, $prevcount ) {
830
+ if ( ! is_array( $locations ) || empty( $locations ) ) {
831
+ return 0;
832
+ }
833
+
834
+ // If we only have 1 match we dont actually do the for loop so set to the first.
835
+ $startpos = $locations[0];
836
+ $loc_count = count( $locations );
837
+ $smallestdiff = PHP_INT_MAX;
838
+
839
+ // If we only have 2 skip as its probably equally relevant.
840
+ if ( $loc_count > 2 ) {
841
+ // Skip the first as we check 1 behind.
842
+ for ( $i = 1; $i < $loc_count; $i++ ) {
843
+ if ( $i === $loc_count - 1 ) { // At the end.
844
+ $diff = $locations[ $i ] - $locations[ $i - 1 ];
845
+ } else {
846
+ $diff = $locations[ $i + 1 ] - $locations[ $i ];
847
  }
848
+
849
+ if ( $smallestdiff > $diff ) {
850
+ $smallestdiff = $diff;
851
+ $startpos = $locations[ $i ];
852
  }
853
  }
854
  }
 
 
 
855
 
856
+ $startpos = 0;
857
+ if ( $startpos > $prevcount ) {
858
+ $startpos - $prevcount;
859
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
860
 
861
  return $startpos;
862
  }
863
 
864
+ /**
865
+ * Extracts relevant part of the full text.
866
+ *
867
+ * Finds the part of full text with as many relevant words as possible. 1/6 ratio on
868
+ * prevcount tends to work pretty well and puts the terms in the middle of the
869
+ * excerpt.
870
+ *
871
+ * @author Ben Boyter
872
+ *
873
+ * @param array $words An array of relevant words.
874
+ * @param string $fulltext The source text.
875
+ * @param int $excerpt_length The length of the excerpt, default 300 characters.
876
+ * @param int $prevcount How much text include before the words, default 50
877
+ * characters.
878
+ *
879
+ * @return array The excerpt, number of words in the excerpt, true if it's the start
880
+ * of the $fulltext.
881
+ */
882
+ function relevanssi_extract_relevant( $words, $fulltext, $excerpt_length = 300, $prevcount = 50 ) {
883
+ $text_length = relevanssi_strlen( $fulltext );
884
 
885
+ if ( $text_length <= $excerpt_length ) {
886
+ return array( $fulltext, 1, 0 );
887
+ }
888
 
889
+ $locations = relevanssi_extract_locations( $words, $fulltext );
890
+ $startpos = relevanssi_determine_snip_location( $locations, $prevcount );
891
 
892
+ // If we are going to snip too much...
893
+ if ( $text_length - $startpos < $excerpt_length ) {
894
+ $startpos = $startpos - ( $text_length - $startpos ) / 2;
895
+ }
896
 
897
+ $substr = 'substr';
898
+ if ( function_exists( 'mb_substr' ) ) {
899
+ $substr = 'mb_substr';
900
+ }
901
+ $strrpos = 'strrpos';
902
+ if ( function_exists( 'mb_strrpos' ) ) {
903
+ $strrpos = 'mb_strrpos';
904
+ }
905
 
906
+ $excerpt = call_user_func( $substr, $fulltext, $startpos, $excerpt_length );
907
 
908
+ // Check to ensure we don't snip the last word if that's the match.
909
+ if ( $startpos + $excerpt_length < $text_length ) {
910
+ $excerpt = call_user_func( $substr, $excerpt, 0, call_user_func( $strrpos, $excerpt, ' ' ) ); // Remove last word.
911
+ }
912
 
913
  $start = false;
914
+ if ( 0 === $startpos ) {
915
+ $start = true;
916
+ }
917
 
918
+ $besthits = count( relevanssi_extract_locations( $words, $excerpt ) );
919
 
920
+ return array( $excerpt, $besthits, $start );
921
  }
922
 
923
+ /**
924
+ * Adds accented variations to letters.
925
+ *
926
+ * In order to have non-accented letters in search terms match the accented terms in
927
+ * full text, this function adds accent variations to the search terms.
928
+ *
929
+ * @param string $word The word to manipulate.
930
+ *
931
+ * @return string The word with accent variations.
932
+ */
933
+ function relevanssi_add_accent_variations( $word ) {
934
+ /**
935
+ * Filters the accent replacement array.
936
+ *
937
+ * @param array Array of replacements. 'from' has the source characters, 'to' the replacements.
938
+ */
939
+ $replacement_arrays = apply_filters('relevanssi_accents_replacement_arrays', array(
940
+ 'from' => array( 'a', 'c', 'e', 'i', 'o', 'u', 'n', 'ss' ),
941
+ 'to' => array( '(a|á|à|â)', '(c|ç)', '(e|é|è|ê|ë)', '(i|í|ì|î|ï)', '(o|ó|ò|ô|õ)', '(u|ú|ù|ü|û)', '(n|ñ)', '(ss|ß)' ),
942
+ ));
943
 
944
+ $word = str_ireplace( $replacement_arrays['from'], $replacement_arrays['to'], $word );
945
 
946
+ return $word;
947
  }
948
 
949
+ /**
950
+ * Fetches the custom field content for a post.
951
+ *
952
+ * @param int $post_id The post ID.
953
+ *
954
+ * @return string The custom field content.
955
+ */
956
+ function relevanssi_get_custom_field_content( $post_id ) {
957
+ $custom_field_content = '';
958
  $remove_underscore_fields = false;
959
 
960
  $custom_fields = relevanssi_get_custom_fields();
961
+ if ( isset( $custom_fields ) && 'all' === $custom_fields ) {
962
+ $custom_fields = get_post_custom_keys( $post_id );
963
+ }
964
+ if ( isset( $custom_fields ) && 'visible' === $custom_fields ) {
965
+ $custom_fields = get_post_custom_keys( $post_id );
966
  $remove_underscore_fields = true;
967
  }
968
+ /* Documented in lib/indexing.php. */
969
+ $custom_fields = apply_filters( 'relevanssi_index_custom_fields', $custom_fields );
970
 
971
+ if ( function_exists( 'relevanssi_get_child_pdf_content' ) ) {
972
+ $custom_field_content .= ' ' . relevanssi_get_child_pdf_content( $post_id );
973
+ }
974
 
975
+ if ( is_array( $custom_fields ) ) {
976
+ $custom_fields = array_unique( $custom_fields ); // No reason to index duplicates.
977
 
978
  $repeater_fields = array();
979
+ if ( function_exists( 'relevanssi_add_repeater_fields' ) ) {
980
+ relevanssi_add_repeater_fields( $custom_fields, $post_id );
981
+ }
982
 
983
+ foreach ( $custom_fields as $field ) {
984
+ if ( $remove_underscore_fields ) {
985
+ if ( '_' === substr( $field, 0, 1 ) ) {
986
+ continue;
987
+ }
988
  }
989
+ /* Documented in lib/indexing.php. */
990
+ $values = apply_filters( 'relevanssi_custom_field_value', get_post_meta( $post_id, $field, false ), $field, $post_id );
991
+ if ( '' === $values ) {
992
+ continue;
993
+ }
994
+ foreach ( $values as $value ) {
995
+ // Quick hack : allow indexing of PODS relationship custom fields. @author TMV.
996
+ if ( is_array( $value ) && isset( $value['post_title'] ) ) {
997
+ $value = $value['post_title'];
998
+ }
999
+
1000
+ // Flatten other array data.
1001
+ if ( is_array( $value ) ) {
1002
+ $value = implode( ' ', $value );
1003
+ }
1004
+ $custom_field_content .= ' ' . $value;
1005
  }
1006
  }
1007
  }
1008
+ /**
1009
+ * Filters the custom field content for excerpt use.
1010
+ *
1011
+ * @param string $custom_field_content Custom field content for excerpts.
1012
+ */
1013
+ return apply_filters( 'relevanssi_excerpt_custom_field_content', $custom_field_content );
1014
  }
1015
 
1016
+ /**
1017
+ * Removes page builder short codes from content.
1018
+ *
1019
+ * Page builder shortcodes cause problems in excerpts. This function cleans them
1020
+ * out.
1021
+ *
1022
+ * @param string $content The content to clean.
1023
+ *
1024
+ * @return string The content without page builder shortcodes.
1025
+ */
1026
+ function relevanssi_remove_page_builder_shortcodes( $content ) {
1027
+ /**
1028
+ * Filters the page builder shortcode.
1029
+ *
1030
+ * @param array An array of page builder shortcode regexes.
1031
+ */
1032
  $search_array = apply_filters('relevanssi_page_builder_shortcodes', array(
1033
+ // Remove content.
1034
+ '/\[et_pb_code.*?\].*\[\/et_pb_code\]/',
1035
+ '/\[et_pb_sidebar.*?\].*\[\/et_pb_sidebar\]/',
1036
+ '/\[vc_raw_html.*?\].*\[\/vc_raw_html\]/',
1037
+ // Remove only the tags.
1038
+ '/\[\/?et_pb.*?\]/',
1039
  '/\[\/?vc.*?\]/',
1040
  '/\[\/?mk.*?\]/',
1041
  '/\[\/?cs_.*?\]/',
1042
  '/\[\/?av_.*?\]/',
1043
+ '/\[\/?fusion_.*?\]/',
1044
+ // Max Mega Menu doesn't work in excerpts.
1045
+ '/\[maxmegamenu.*?\]/',
1046
  ));
1047
+ $content = preg_replace( $search_array, '', $content );
1048
  return $content;
1049
  }
1050
 
1051
  /**
1052
+ * Kills the autoembed filter hook on 'the_content'.
1053
+ *
1054
+ * @global array $wp_filter The global filter array.
1055
+ *
1056
+ * It's an object hook, so this isn't as simple as doing remove_filter(). This
1057
+ * needs to be done, because autoembed discovery can take a very, very long time.
1058
  */
1059
  function relevanssi_kill_autoembed() {
1060
  global $wp_filter;
1061
+ if ( isset( $wp_filter['the_content']->callbacks ) ) {
1062
+ foreach ( $wp_filter['the_content']->callbacks as $priority => $bucket ) {
1063
+ foreach ( $bucket as $key => $value ) {
1064
+ if ( 'autoembed' === substr( $key, -9 ) ) {
1065
+ unset( $wp_filter['the_content']->callbacks[ $priority ][ $key ] );
1066
  }
1067
  }
1068
  }
 
1069
  }
1070
  }
 
 
lib/indexing.php CHANGED
@@ -1,49 +1,106 @@
1
  <?php
 
 
 
 
 
 
 
 
2
 
 
 
 
 
 
 
 
 
3
  function relevanssi_count_total_posts() {
4
- global $wpdb, $relevanssi_variables;
5
- $relevanssi_table = $relevanssi_variables['relevanssi_table'];
6
- $restriction = relevanssi_post_type_restriction();
7
- $valid_status = relevanssi_valid_status_array();
8
- $limit = "";
9
  $extend = false;
10
-
11
- $q = relevanssi_generate_indexing_query($valid_status, $extend, $restriction, $limit);
12
- $q = str_replace('SELECT post.ID', 'SELECT COUNT(post.ID)', $q);
13
-
14
- do_action('relevanssi_pre_indexing_query');
15
- $count = $wpdb->get_var($q);
16
-
17
- if (empty($count)) $count = 0;
18
-
19
- return $count;
20
  }
21
 
 
 
 
 
 
 
 
 
22
  function relevanssi_count_missing_posts() {
23
- global $wpdb, $relevanssi_variables;
24
- $relevanssi_table = $relevanssi_variables['relevanssi_table'];
25
- $restriction = relevanssi_post_type_restriction();
26
- $valid_status = relevanssi_valid_status_array();
27
- $limit = "";
28
  $extend = true;
 
 
29
 
30
- $q = relevanssi_generate_indexing_query($valid_status, $extend, $restriction, $limit);
31
- $q = str_replace('SELECT post.ID', 'SELECT COUNT(post.ID)', $q);
32
-
33
- error_log($q);
34
- do_action('relevanssi_pre_indexing_query');
35
- $count = $wpdb->get_var($q);
36
-
37
- if (empty($count)) $count = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  return $count;
40
  }
41
 
42
- function relevanssi_generate_indexing_query($valid_status, $extend = false, $restriction = "", $limit = "") {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  global $wpdb, $relevanssi_variables;
44
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
45
-
46
- if (!$extend) {
47
  $q = "SELECT post.ID
48
  FROM $wpdb->posts post
49
  LEFT JOIN $wpdb->posts parent ON (post.post_parent=parent.ID)
@@ -57,8 +114,7 @@ function relevanssi_generate_indexing_query($valid_status, $extend = false, $res
57
  )
58
  ))
59
  $restriction ORDER BY post.ID DESC $limit";
60
- }
61
- else {
62
  $q = "SELECT post.ID
63
  FROM $wpdb->posts post
64
  LEFT JOIN $wpdb->posts parent ON (post.post_parent=parent.ID)
@@ -81,571 +137,852 @@ function relevanssi_generate_indexing_query($valid_status, $extend = false, $res
81
  return $q;
82
  }
83
 
 
 
 
 
 
 
 
 
84
  function relevanssi_post_type_restriction() {
85
- $post_types = array();
86
- $restriction = "";
87
 
88
- $types = get_option("relevanssi_index_post_types");
89
- if (!is_array($types)) $types = array();
90
- foreach ($types as $type) {
91
- if ($type == "bogus") continue;
92
- array_push($post_types, "'$type'");
93
  }
94
-
95
- if (count($post_types) > 0) {
96
- $restriction = " AND post.post_type IN (" . implode(', ', $post_types) . ') ';
 
 
 
 
 
 
 
97
  }
98
- else {
99
- $restriction = "";
 
100
  }
101
 
102
  return $restriction;
103
  }
104
 
 
 
 
 
 
 
 
 
 
 
105
  function relevanssi_valid_status_array() {
106
- $valid_status_array = apply_filters('relevanssi_valid_status', array('publish', 'draft', 'private', 'pending', 'future'));
107
- $valid_status = array();
108
-
109
- if (is_array($valid_status_array) && count($valid_status_array) > 0) {
110
- foreach ($valid_status_array as $status) {
111
- $valid_status[] = "'$status'";
 
 
 
 
 
 
 
 
 
 
 
 
112
  }
113
- $valid_status = implode(',', $valid_status);
114
- }
115
- else {
116
- // this really should never happen
117
  $valid_status = "'publish', 'draft', 'private', 'pending', 'future'";
118
  }
119
 
120
  return $valid_status;
121
  }
122
 
123
- function relevanssi_build_index($extend_offset = false, $verbose = true, $post_limit = null, $is_ajax = false) {
124
- if (function_exists('wp_suspend_cache_addition'))
125
- wp_suspend_cache_addition(true); // Thanks to Julien Mession
126
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  global $wpdb, $relevanssi_variables;
128
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
129
 
130
- set_time_limit(0);
131
-
132
- $restriction = relevanssi_post_type_restriction();
133
 
 
 
134
  $valid_status = relevanssi_valid_status_array();
135
 
136
- $n = 0;
137
  $size = 0;
138
 
139
- if ($extend_offset === false) {
140
- // truncate table first
141
  relevanssi_truncate_index();
142
 
143
- if (function_exists('relevanssi_index_taxonomies')) {
144
- if (get_option('relevanssi_index_taxonomies') == 'on') {
 
145
  relevanssi_index_taxonomies();
146
  }
147
  }
148
 
149
- if (function_exists('relevanssi_index_users')) {
150
- if (get_option('relevanssi_index_users') == 'on') {
 
151
  relevanssi_index_users();
152
  }
153
  }
154
 
155
- // if post limit parameter is present, numeric and > 0, use that
156
- $limit = "";
157
- if (isset($post_limit) && is_numeric($post_limit) && $post_limit > 0) {
158
- $size = $post_limit;
159
  $limit = " LIMIT $post_limit";
160
  }
161
 
162
- $q = relevanssi_generate_indexing_query($valid_status, $extend_offset, $restriction, $limit);
163
 
164
- update_option('relevanssi_index', '');
165
- }
166
- else if (!is_numeric($extend_offset)) {
167
- // extending, so no truncate and skip the posts already in the index
168
- $limit = get_option('relevanssi_index_limit', 200);
169
 
170
- // if post limit parameter is present, numeric and > 0, use that
171
- if (isset($post_limit) && is_numeric($post_limit) && $post_limit > 0) $limit = $post_limit;
 
 
172
 
173
- if (is_numeric($limit) && $limit > 0) {
174
- $size = $limit;
175
  $limit = " LIMIT $limit";
176
- }
177
- else {
178
- $limit = "";
179
  }
180
 
181
  $extend = true;
 
 
 
 
 
 
 
 
 
182
 
183
- $q = relevanssi_generate_indexing_query($valid_status, $extend, $restriction, $limit);
184
- }
185
- else {
186
- // extending, so no truncate and skip the posts already in the index
187
- $limit = get_option('relevanssi_index_limit', 200);
188
-
189
- // if post limit parameter is present, numeric and > 0, use that
190
- if (isset($post_limit) && is_numeric($post_limit) && $post_limit > 0) $limit = $post_limit;
191
-
192
- if (is_numeric($limit) && $limit > 0) {
193
- $size = $limit;
194
  $limit = " LIMIT $limit OFFSET $extend_offset";
195
- }
196
- else {
197
- $limit = "";
198
  }
199
 
200
- $extend = true;
201
- $q = relevanssi_generate_indexing_query($valid_status, $extend, $restriction, $limit);
 
202
  }
203
 
204
  $custom_fields = relevanssi_get_custom_fields();
205
 
206
- do_action('relevanssi_pre_indexing_query');
207
- $content = $wpdb->get_results($q);
 
208
 
209
- if ( defined( 'WP_CLI' ) && WP_CLI && function_exists('relevanssi_generate_progress_bar') ) $progress = relevanssi_generate_progress_bar( 'Indexing posts', count($content) );
210
- foreach ($content as $post) {
211
- $result = relevanssi_index_doc($post->ID, false, $custom_fields, true);
212
- if (is_numeric($result) && $result > 0) $n++;
213
- // n calculates the number of posts indexed
214
- // $bypassglobalpost set to true, because at this point global $post should be NULL, but in some cases it is not
215
 
216
- if ( defined( 'WP_CLI' ) && WP_CLI && $progress ) $progress->tick();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
- if ( defined( 'WP_CLI' ) && WP_CLI && $progress ) $progress->finish();
219
 
220
- $wpdb->query("ANALYZE TABLE $relevanssi_table");
221
- // To prevent empty indices
222
 
223
  $complete = false;
224
- if ($verbose) {
225
- if (($size == 0) || (count($content) < $size)) {
226
- $message = __("Indexing complete!", "relevanssi");
227
- }
228
- else {
229
- $message = __("More to index...", "relevanssi");
230
- }
231
- echo '<div id="message" class="updated fade"><p>' . $message . '</p></div>';
232
  }
233
- else {
234
- if (($size == 0) || (count($content) < $size)) $complete = true;
235
  }
236
 
237
- update_option('relevanssi_indexed', 'done');
238
 
239
- // We always want to run this on init, if the index is finished building.
240
- $D = $wpdb->get_var("SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi");
241
- update_option( 'relevanssi_doc_count', $D);
242
 
243
- if (function_exists('wp_suspend_cache_addition'))
244
- wp_suspend_cache_addition(false); // Thanks to Julien Mession
245
 
246
- if ($is_ajax) {
247
  $response = array(
248
  'indexing_complete' => $complete,
249
- 'indexed' => $n,
250
  );
251
  return $response;
252
  }
253
 
254
- return array($complete, $n);
255
  }
256
 
257
- // BEGIN modified by renaissancehack
258
- // recieve $post argument as $indexpost, so we can make it the $post global. This will allow shortcodes
259
- // that need to know what post is calling them to access $post->ID
260
- /*
261
- Different cases:
262
-
263
- - Build index:
264
- global $post is NULL, $indexpost is a post object.
265
-
266
- - Update post:
267
- global $post has the original $post, $indexpost is the ID of revision.
268
-
269
- - Quick edit:
270
- global $post is an array, $indexpost is the ID of current revision.
271
- */
272
- function relevanssi_index_doc($indexpost, $remove_first = false, $custom_fields = false, $bypassglobalpost = false, $debug = false) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  global $wpdb, $post, $relevanssi_variables;
274
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
275
- $post_was_null = false;
276
- $previous_post = NULL;
277
-
278
- // Check if this is a Jetpack Contact Form entry
279
- if (isset($_REQUEST['contact-form-id'])) return;
280
 
281
- if ($bypassglobalpost) {
282
- // if $bypassglobalpost is set, relevanssi_index_doc() will index the post object or post
283
- // ID as specified in $indexpost
284
- isset($post) ?
285
- $previous_post = $post : $post_was_null = true;
286
- is_object($indexpost) ?
287
- $post = $indexpost : $post = get_post($indexpost);
288
  }
289
- else {
290
- // Quick edit has an array in the global $post, so fetch the post ID for the post to edit.
291
- if (is_array($post)) {
292
- $post = get_post($post['ID']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  }
294
 
295
- if (empty($post)) {
296
- // No $post set, so we need to use $indexpost, if it's a post object
297
  $post_was_null = true;
298
- if (is_object($indexpost)) {
299
- $post = $indexpost;
 
 
300
  }
301
- else {
302
- $post = get_post($indexpost);
303
- }
304
- }
305
- else {
306
  // $post was set, let's grab the previous value in case we need it
307
  $previous_post = $post;
308
  }
309
  }
310
 
311
- if ($post == NULL) {
312
  // At this point we should have something in $post; if not, quit.
313
- if ($post_was_null) $post = null;
314
- if ($previous_post) $post = $previous_post;
 
 
 
 
315
  return -1;
316
  }
317
 
318
- // Finally fetch the post again by ID. Complicated, yes, but unless we do this, we might end
319
- // up indexing the post before the updates come in.
320
- $post = get_post($post->ID);
321
 
322
- if (function_exists('relevanssi_hide_post')) {
323
- if (relevanssi_hide_post($post->ID)) {
324
- if ($debug) relevanssi_debug_echo("relevanssi_hide_post() returned true.");
325
- if ($post_was_null) $post = null;
326
- if ($previous_post) $post = $previous_post;
327
- return "hide";
 
 
 
 
 
 
 
328
  }
329
  }
330
 
331
- $index_this_post = false;
332
-
333
  $post->indexing_content = true;
334
- $index_types = get_option('relevanssi_index_post_types');
335
- if (!is_array($index_types)) $index_types = array();
336
- if (in_array($post->post_type, $index_types)) $index_this_post = true;
337
 
338
- if (true == apply_filters('relevanssi_do_not_index', false, $post->ID)) {
339
- // filter says no
340
- if ($debug) relevanssi_debug_echo("relevanssi_do_not_index returned true.");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
  $index_this_post = false;
342
  }
343
 
344
- if ($remove_first) {
345
- // we are updating a post, so remove the old stuff first
346
- relevanssi_remove_doc($post->ID, true);
347
- if (function_exists('relevanssi_remove_item')) {
348
- relevanssi_remove_item($post->ID, 'post');
 
 
 
349
  }
350
- if ($debug) relevanssi_debug_echo("Removed the post from the index.");
351
  }
352
 
353
- // This needs to be here, after the call to relevanssi_remove_doc(), because otherwise
354
- // a post that's in the index but shouldn't be there won't get removed.
355
- if (!$index_this_post) {
356
- if ($post_was_null) $post = null;
357
- if ($previous_post) $post = $previous_post;
358
- return "donotindex";
 
 
 
 
359
  }
360
 
361
  $n = 0;
362
 
363
- // The second parameter is useless here, but used elsewhere
364
- $post = apply_filters('relevanssi_post_to_index', $post, $post);
365
-
366
- $min_word_length = get_option('relevanssi_min_word_length', 3);
367
- $insert_data = array();
368
-
369
- //Added by OdditY - INDEX COMMENTS of the POST ->
370
- if ("none" != get_option("relevanssi_index_comments")) {
371
- if ($debug) relevanssi_debug_echo("Indexing comments.");
372
- $pcoms = relevanssi_get_comments($post->ID);
373
- if ($pcoms != "") {
374
- $pcoms = relevanssi_strip_invisibles($pcoms);
375
- $pcoms = preg_replace('/<[a-zA-Z\/][^>]*>/', ' ', $pcoms);
376
- $pcoms = strip_tags($pcoms);
377
- if ($debug) relevanssi_debug_echo("Comment content: $pcoms");
378
- $pcoms = relevanssi_tokenize($pcoms, true, $min_word_length);
379
- if (count($pcoms) > 0) {
380
- foreach ($pcoms as $pcom => $count) {
 
 
 
 
 
 
 
 
 
381
  $n++;
382
- $insert_data[$pcom]['comment'] = $count;
383
  }
384
  }
385
  }
386
- } //Added by OdditY END <-
387
-
388
-
389
- $taxonomies = get_option("relevanssi_index_taxonomies_list");
390
 
391
- // Then process all taxonomies, if any.
392
- foreach ($taxonomies as $taxonomy) {
393
- if ($debug) relevanssi_debug_echo("Indexing taxonomy terms for $taxonomy");
394
- $insert_data = relevanssi_index_taxonomy_terms($post, $taxonomy, $insert_data);
 
 
 
395
  }
396
 
397
- // index author
398
- if ("on" == get_option("relevanssi_index_author")) {
399
- $auth = $post->post_author;
400
- $display_name = $wpdb->get_var("SELECT display_name FROM $wpdb->users WHERE ID=$auth");
401
- $names = relevanssi_tokenize($display_name, false, $min_word_length);
402
- if ($debug) relevanssi_debug_echo("Indexing post author as: " . implode(" ", array_keys($names)));
403
- foreach($names as $name => $count) {
404
- isset($insert_data[$name]['author']) ? $insert_data[$name]['author'] += $count : $insert_data[$name]['author'] = $count;
 
 
 
 
 
 
405
  }
406
  }
407
 
 
408
  $remove_underscore_fields = false;
409
- if (isset($custom_fields) && $custom_fields == 'all')
410
- $custom_fields = get_post_custom_keys($post->ID);
411
- if (isset($custom_fields) && $custom_fields == 'visible') {
412
- $custom_fields = get_post_custom_keys($post->ID);
 
413
  $remove_underscore_fields = true;
414
  }
415
- $custom_fields = apply_filters('relevanssi_index_custom_fields', $custom_fields, $post->ID);
416
- if (is_array($custom_fields)) {
417
- if ($debug) relevanssi_debug_echo("Custom fields to index: " . implode(", ", $custom_fields));
418
- $custom_fields = array_unique($custom_fields); // no reason to index duplicates
 
 
 
 
 
 
 
 
419
 
 
420
  $repeater_fields = array();
421
- if (function_exists('relevanssi_add_repeater_fields')) relevanssi_add_repeater_fields($custom_fields, $post->ID);
 
 
 
 
 
 
 
 
 
 
422
 
423
- foreach ($custom_fields as $field) {
424
- if ($remove_underscore_fields) {
425
- if ($field !== '_relevanssi_pdf_content' && substr($field, 0, 1) === '_') continue;
 
 
 
 
 
 
 
 
426
  }
427
-
428
- $values = apply_filters('relevanssi_custom_field_value', get_post_meta($post->ID, $field, false), $field, $post->ID);
429
-
430
- if ("" == $values) continue;
431
- foreach ($values as $value) {
432
- // Quick hack : allow indexing of PODS relationship custom fields // TMV
433
- if (is_array($value) && isset($value['post_title'])) $value = $value['post_title'];
434
- relevanssi_index_acf($insert_data, $post->ID, $field, $value);
435
- if ($debug) relevanssi_debug_echo("\tKey: " . $field . " – value: " . $value);
436
-
437
- $value_tokens = relevanssi_tokenize($value, true, $min_word_length);
438
- foreach ($value_tokens as $token => $count) {
439
- isset($insert_data[$token]['customfield']) ? $insert_data[$token]['customfield'] += $count : $insert_data[$token]['customfield'] = $count;
440
- if (function_exists('relevanssi_customfield_detail')) {
441
- $insert_data = relevanssi_customfield_detail($insert_data, $token, $count, $field);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  }
443
  }
444
  }
445
  }
446
  }
447
 
448
- if (isset($post->post_excerpt) && ("on" == get_option("relevanssi_index_excerpt") || "attachment" == $post->post_type)) { // include excerpt for attachments which use post_excerpt for captions - modified by renaissancehack
449
- if ($debug) relevanssi_debug_echo("Indexing post excerpt: $post->post_excerpt");
450
- $excerpt_tokens = relevanssi_tokenize($post->post_excerpt, true, $min_word_length);
451
- foreach ($excerpt_tokens as $token => $count) {
452
- isset($insert_data[$token]['excerpt']) ? $insert_data[$token]['excerpt'] += $count : $insert_data[$token]['excerpt'] = $count;
 
 
 
 
 
 
 
453
  }
454
  }
455
 
456
- if (function_exists('relevanssi_index_mysql_columns')) {
457
- if ($debug) relevanssi_debug_echo("Indexing MySQL columns.");
458
- $insert_data = relevanssi_index_mysql_columns($insert_data, $post->ID);
 
 
 
459
  }
460
 
461
- if (function_exists('relevanssi_index_pdf_for_parent')) {
462
- if ($debug) relevanssi_debug_echo("Indexing PDF content for parent post.");
463
- $insert_data = relevanssi_index_pdf_for_parent($insert_data, $post->ID);
 
 
 
464
  }
465
 
466
  $index_titles = true;
467
- if (!empty($post->post_title)) {
468
- if (apply_filters('relevanssi_index_titles', $index_titles)) {
469
- if ($debug) relevanssi_debug_echo("Indexing post title.");
470
- $filtered_title = apply_filters('relevanssi_post_title_before_tokenize', $post->post_title, $post);
471
- $titles = relevanssi_tokenize(apply_filters('the_title', $filtered_title, $post->ID), apply_filters('relevanssi_remove_stopwords_in_titles', true), $min_word_length);
472
- if ($debug) relevanssi_debug_echo("\tTitle, tokenized: " . implode(" ", array_keys($titles)));
473
-
474
- if (count($titles) > 0) {
475
- foreach ($titles as $title => $count) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  $n++;
477
- isset($insert_data[$title]['title']) ? $insert_data[$title]['title'] += $count : $insert_data[$title]['title'] = $count;
 
 
 
478
  }
479
  }
480
  }
481
  }
482
 
 
483
  $index_content = true;
484
- if (apply_filters('relevanssi_index_content', $index_content)) {
485
- if ($debug) relevanssi_debug_echo("Indexing post content.");
486
- remove_shortcode('noindex');
487
- add_shortcode('noindex', 'relevanssi_noindex_shortcode_indexing');
488
-
489
- $contents = apply_filters('relevanssi_post_content', $post->post_content, $post);
490
- if ($debug) relevanssi_debug_echo("\tPost content after relevanssi_post_content:\n$contents");
491
-
492
- // Allow user to add extra content for Relevanssi to index
493
- // Thanks to Alexander Gieg
494
- $additional_content = trim(apply_filters('relevanssi_content_to_index', '', $post));
495
- if ('' != $additional_content) {
496
- $contents .= ' '.$additional_content;
497
- if ($debug) relevanssi_debug_echo("\tAdditional content from relevanssi_content_to_index:\n$contents");
498
- }
499
-
500
- if ('on' == get_option('relevanssi_expand_shortcodes')) {
501
- if (function_exists("do_shortcode")) {
502
- // WP Table Reloaded support
503
- if (defined('WP_TABLE_RELOADED_ABSPATH')) {
504
- include_once(WP_TABLE_RELOADED_ABSPATH . 'controllers/controller-frontend.php');
505
- $My_WP_Table_Reloaded = new WP_Table_Reloaded_Controller_Frontend();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
  }
507
- // TablePress support
508
- if (defined('TABLEPRESS_ABSPATH')) {
509
- if (!isset(TablePress::$model_options)) {
510
- include_once(TABLEPRESS_ABSPATH . 'classes/class-model.php');
511
- include_once(TABLEPRESS_ABSPATH . 'models/model-options.php');
512
  TablePress::$model_options = new TablePress_Options_Model();
513
  }
514
- $My_TablePress_Controller = TablePress::load_controller( 'frontend' );
515
- $My_TablePress_Controller->init_shortcodes();
516
  }
517
 
518
- $disable_shortcodes = get_option('relevanssi_disable_shortcodes');
519
- $shortcodes = explode(',', $disable_shortcodes);
520
- foreach ($shortcodes as $shortcode) {
521
- remove_shortcode(trim($shortcode));
522
  }
523
- remove_shortcode('contact-form'); // Jetpack Contact Form causes an error message
524
- remove_shortcode('starrater'); // GD Star Rating rater shortcode causes problems
525
- remove_shortcode('responsive-flipbook'); // Responsive Flipbook causes problems
526
- remove_shortcode('avatar_upload'); // WP User Avatar is incompatible
527
- remove_shortcode('product_categories'); // A problematic WooCommerce shortcode
528
- remove_shortcode('recent_products'); // A problematic WooCommerce shortcode
529
- remove_shortcode('php'); // PHP Code for Posts
530
- remove_shortcode('watupro'); // Watu PRO doesn't co-operate
531
- remove_shortcode('starbox'); // Starbox shortcode breaks Relevanssi
532
- remove_shortcode('cfdb-save-form-post'); // Contact Form DB
533
- remove_shortcode('cfdb-datatable');
534
- remove_shortcode('cfdb-table');
535
- remove_shortcode('cfdb-json');
536
- remove_shortcode('cfdb-value');
537
- remove_shortcode('cfdb-count');
538
- remove_shortcode('cfdb-html');
539
- remove_shortcode('woocommerce_cart'); // WooCommerce
540
- remove_shortcode('woocommerce_checkout');
541
- remove_shortcode('woocommerce_order_tracking');
542
- remove_shortcode('woocommerce_my_account');
543
- remove_shortcode('woocommerce_edit_account');
544
- remove_shortcode('woocommerce_change_password');
545
- remove_shortcode('woocommerce_view_order');
546
- remove_shortcode('woocommerce_logout');
547
- remove_shortcode('woocommerce_pay');
548
- remove_shortcode('woocommerce_thankyou');
549
- remove_shortcode('woocommerce_lost_password');
550
- remove_shortcode('woocommerce_edit_address');
551
- remove_shortcode('tc_process_payment');
552
- remove_shortcode('maxmegamenu'); // Max Mega Menu
553
- remove_shortcode('searchandfilter'); // Search and Filter
554
- remove_shortcode('downloads'); // Easy Digital Downloads
555
- remove_shortcode('download_history');
556
- remove_shortcode('purchase_history');
557
- remove_shortcode('download_checkout');
558
- remove_shortcode('purchase_link');
559
- remove_shortcode('download_cart');
560
- remove_shortcode('edd_profile_editor');
561
- remove_shortcode('edd_login');
562
- remove_shortcode('edd_register');
563
- remove_shortcode('swpm_protected'); // Simple Membership Partially Protected content
564
- remove_shortcode('gravityform'); // Gravity Forms
565
 
566
  $post_before_shortcode = $post;
567
- $contents = do_shortcode($contents);
568
- $post = $post_before_shortcode;
569
 
570
- if (defined('TABLEPRESS_ABSPATH')) {
571
- unset($My_TablePress_Controller);
572
  }
573
- if (defined('WP_TABLE_RELOADED_ABSPATH')) {
574
- unset($My_WP_Table_Reloaded);
575
  }
576
  }
577
- }
578
- else {
579
- $contents = strip_shortcodes($contents);
580
  }
581
 
582
- remove_shortcode('noindex');
583
- add_shortcode('noindex', 'relevanssi_noindex_shortcode');
584
 
585
- $contents = relevanssi_strip_invisibles($contents);
586
 
587
- if (function_exists('relevanssi_process_internal_links')) {
588
- $contents = relevanssi_process_internal_links($contents, $post->ID);
 
589
  }
590
 
591
- $contents = preg_replace('/<[a-zA-Z\/][^>]*>/', ' ', $contents);
592
- $contents = strip_tags($contents);
593
- if (function_exists('wp_encode_emoji')) $contents = wp_encode_emoji($contents);
594
- $contents = apply_filters('relevanssi_post_content_before_tokenize', $contents, $post);
595
- $contents = relevanssi_tokenize($contents, true, $min_word_length);
596
-
597
- if ($debug) relevanssi_debug_echo("\tContent, tokenized:\n" . implode(" ", array_keys($contents)));
 
 
 
 
 
 
 
 
 
 
598
 
599
- if (count($contents) > 0) {
600
- foreach ($contents as $content => $count) {
601
- $n++;
602
- isset($insert_data[$content]['content']) ? $insert_data[$content]['content'] += $count : $insert_data[$content]['content'] = $count;
 
 
 
603
  }
604
  }
605
  }
606
 
607
  $type = 'post';
608
- if ($post->post_type == 'attachment') $type = 'attachment';
 
 
609
 
610
- $insert_data = apply_filters('relevanssi_indexing_data', $insert_data, $post);
 
 
 
 
 
 
611
 
612
  $values = array();
613
- foreach ($insert_data as $term => $data) {
614
- $content = 0;
615
- $title = 0;
616
- $comment = 0;
617
- $tag = 0;
618
- $link = 0;
619
- $author = 0;
620
- $category = 0;
621
- $excerpt = 0;
622
- $taxonomy = 0;
623
- $customfield = 0;
624
- $taxonomy_detail = '';
625
- $customfield_detail = '';
626
- $mysqlcolumn = 0;
627
- extract($data);
628
-
629
- $term = trim($term);
630
-
631
- $value = $wpdb->prepare("(%d, %s, REVERSE(%s), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %d)",
632
- $post->ID, $term, $term, $content, $title, $comment, $tag, $link, $author, $category, $excerpt, $taxonomy, $customfield, $type, $taxonomy_detail, $customfield_detail, $mysqlcolumn);
633
-
634
- array_push($values, $value);
 
 
 
 
 
 
 
 
 
 
 
635
  }
636
 
637
- $values = apply_filters('relevanssi_indexing_values', $values, $post);
638
-
639
- if (!empty($values)) {
640
- $values = implode(', ', $values);
641
- $query = "INSERT IGNORE INTO $relevanssi_table (doc, term, term_reverse, content, title, comment, tag, link, author, category, excerpt, taxonomy, customfield, type, taxonomy_detail, customfield_detail, mysqlcolumn)
642
- VALUES $values";
643
- if ($debug) relevanssi_debug_echo("Final indexing query:\n\t$query");
644
- $wpdb->query($query);
 
 
 
 
 
 
 
645
  }
646
 
647
- if ($post_was_null) $post = null;
648
- if ($previous_post) $post = $previous_post;
 
 
 
 
649
 
650
  return $n;
651
  }
@@ -654,300 +991,421 @@ function relevanssi_index_doc($indexpost, $remove_first = false, $custom_fields
654
  * Index taxonomy terms for given post and given taxonomy.
655
  *
656
  * @since 1.8
657
- * @param object $post Post object.
658
- * @param string $taxonomy Taxonomy name.
659
- * @param array $insert_data Insert query data array.
 
 
660
  * @return array Updated insert query data array.
661
  */
662
- function relevanssi_index_taxonomy_terms($post = null, $taxonomy = "", $insert_data) {
663
  global $wpdb, $relevanssi_variables;
664
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
665
 
666
  $n = 0;
667
 
668
- if (null == $post) return $insert_data;
669
- if ("" == $taxonomy) return $insert_data;
 
670
 
671
- $min_word_length = get_option('relevanssi_min_word_length', 3);
672
- $ptagobj = get_the_terms($post->ID, $taxonomy);
673
- if ($ptagobj !== FALSE) {
674
- $tagstr = "";
675
- foreach ($ptagobj as $ptag) {
676
- if (is_object($ptag)) {
677
- $tagstr .= $ptag->name . ' ';
678
  }
679
  }
680
- $tagstr = apply_filters('relevanssi_tag_before_tokenize', trim($tagstr));
681
- $ptags = relevanssi_tokenize($tagstr, true, $min_word_length);
682
- if (count($ptags) > 0) {
683
- foreach ($ptags as $ptag => $count) {
684
  $n++;
685
 
686
- if ('post_tags' == $taxonomy) {
687
- $insert_data[$ptag]['tag'] = $count;
688
- }
689
- else if ('category' == $taxonomy) {
690
- $insert_data[$ptag]['category'] = $count;
691
- }
692
- else {
693
- if (isset($insert_data[$ptag]['taxonomy'])) {
694
- $insert_data[$ptag]['taxonomy'] += $count;
695
  }
696
- else {
697
- $insert_data[$ptag]['taxonomy'] = $count;
698
- }
699
- }
700
- if (isset($insert_data[$ptag]['taxonomy_detail'])) {
701
- $tax_detail = unserialize($insert_data[$ptag]['taxonomy_detail']);
702
  }
703
- else {
 
 
704
  $tax_detail = array();
705
  }
706
- if (isset($tax_detail[$taxonomy])) {
707
- $tax_detail[$taxonomy] += $count;
 
 
708
  }
709
- else {
710
- $tax_detail[$taxonomy] = $count;
711
- }
712
- $insert_data[$ptag]['taxonomy_detail'] = serialize($tax_detail);
713
  }
714
  }
715
  }
716
  return $insert_data;
717
  }
718
 
719
- // BEGIN added by renaissancehack
720
- function relevanssi_update_child_posts($new_status, $old_status, $post) {
721
- // called by 'transition_post_status' action hook when a post is edited/published/deleted
722
- // and calls appropriate indexing function on child posts/attachments
723
- global $wpdb;
724
-
725
- // Safety check, for WordPress Editorial Calendar incompatibility
726
- if (!isset($post) || !isset($post->ID)) return;
727
-
728
- $index_statuses = apply_filters('relevanssi_valid_status', array('publish', 'private', 'draft', 'pending', 'future'));
729
- if (($new_status == $old_status)
730
- || (in_array($new_status, $index_statuses) && in_array($old_status, $index_statuses))
731
- || (in_array($post->post_type, array('attachment', 'revision')))) {
732
- return;
733
- }
734
-
735
- $q = "SELECT * FROM $wpdb->posts WHERE post_parent=$post->ID AND post_type!='revision'";
736
- $child_posts = $wpdb->get_results($q);
737
- if ($child_posts) {
738
- if (!in_array($new_status, $index_statuses)) {
739
- foreach ($child_posts as $post) {
740
- relevanssi_delete($post->ID);
741
- }
742
- } else {
743
- foreach ($child_posts as $post) {
744
- relevanssi_publish($post->ID);
745
- }
746
- }
747
- }
748
- }
749
- // END added by renaissancehack
750
-
751
- function relevanssi_edit($post) {
752
- // Check if the post is public
753
  global $wpdb;
754
 
755
- $post_status = get_post_status($post);
756
- if ('auto-draft' == $post_status) return;
757
-
758
- // BEGIN added by renaissancehack
759
- // if post_status is "inherit", get post_status from parent
760
- if ($post_status == 'inherit') {
761
- $post_type = $wpdb->get_var("SELECT post_type FROM $wpdb->posts WHERE ID=$post");
762
- $post_status = $wpdb->get_var("SELECT p.post_status FROM $wpdb->posts p, $wpdb->posts c WHERE c.ID=$post AND c.post_parent=p.ID");
763
- }
764
- // END added by renaissancehack
765
-
766
- $index_statuses = apply_filters('relevanssi_valid_status', array('publish', 'private', 'draft', 'pending', 'future'));
767
- if (!in_array($post_status, $index_statuses)) {
768
- // The post isn't supposed to be indexed anymore, remove it from index
769
- relevanssi_remove_doc($post);
770
  }
771
- else {
772
- relevanssi_publish($post);
 
 
 
 
 
 
 
 
 
 
 
 
773
  }
774
- }
775
 
776
- function relevanssi_delete($post) {
777
- relevanssi_remove_doc($post);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
778
  }
779
 
780
- function relevanssi_publish($post, $bypassglobalpost = false) {
781
- global $relevanssi_publish_doc;
782
-
783
- $post_status = get_post_status($post);
784
- if ('auto-draft' == $post_status) return;
 
 
 
 
 
 
785
 
786
  $custom_fields = relevanssi_get_custom_fields();
787
- relevanssi_index_doc($post, true, $custom_fields, $bypassglobalpost);
788
  }
789
 
790
- // added by lumpysimon
791
- // when we're using wp_insert_post to update a post,
792
- // we don't want to use the global $post object
793
- function relevanssi_insert_edit($post_id) {
 
 
 
 
 
 
 
 
 
 
794
  global $wpdb;
795
 
796
  $post_status = get_post_status( $post_id );
797
- if ( 'auto-draft' == $post_status ) return;
 
 
798
 
799
- if ( $post_status == 'inherit' ) {
800
- // $post_type = $wpdb->get_var( "SELECT post_type FROM $wpdb->posts WHERE ID=$post_id" );
801
- $post_status = $wpdb->get_var( "SELECT p.post_status FROM $wpdb->posts p, $wpdb->posts c WHERE c.ID=$post_id AND c.post_parent=p.ID" );
802
- }
 
803
 
804
- $index_statuses = apply_filters('relevanssi_valid_status', array('publish', 'private', 'draft', 'future', 'pending'));
805
- if ( !in_array( $post_status, $index_statuses ) ) {
806
- // The post isn't supposed to be indexed anymore, remove it from index
807
  relevanssi_remove_doc( $post_id );
 
 
 
808
  }
809
- else {
810
- $bypassglobalpost = true;
811
- relevanssi_publish($post_id, $bypassglobalpost);
812
- }
813
  }
814
 
815
- //Added by OdditY ->
816
- function relevanssi_comment_edit($comID) {
817
- relevanssi_comment_index($comID,$action="update");
 
 
 
 
 
 
 
818
  }
819
 
820
- function relevanssi_comment_remove($comID) {
821
- relevanssi_comment_index($comID,$action="remove");
 
 
 
 
 
 
 
 
822
  }
823
 
824
- function relevanssi_comment_index($comID,$action="add") {
 
 
 
 
 
 
 
 
825
  global $wpdb;
826
- $comtype = get_option("relevanssi_index_comments");
827
- switch ($comtype) {
828
- case "all":
829
- // all (incl. customs, track-&pingbacks)
 
 
 
 
830
  break;
831
- case "normal":
832
- // normal (excl. customs, track-&pingbacks)
833
- $restriction=" AND comment_type='' ";
834
  break;
835
  default:
836
- // none (don't index)
837
- return ;
838
  }
839
- switch ($action) {
840
- case "update":
841
- //(update) comment status changed:
842
- $cpostID = $wpdb->get_var("SELECT comment_post_ID FROM $wpdb->comments WHERE comment_ID='$comID'".$restriction);
 
 
 
 
843
  break;
844
- case "remove":
845
- //(remove) approved comment will be deleted (if not approved, its not in index):
846
- $cpostID = $wpdb->get_var("SELECT comment_post_ID FROM $wpdb->comments WHERE comment_ID='$comID' AND comment_approved='1'".$restriction);
847
- if($cpostID!=NULL) {
848
- //empty comment_content & reindex, then let WP delete the empty comment
849
- $wpdb->query("UPDATE $wpdb->comments SET comment_content='' WHERE comment_ID='$comID'");
 
 
 
 
850
  }
851
  break;
852
  default:
853
- // (add) new comment:
854
- $cpostID = $wpdb->get_var("SELECT comment_post_ID FROM $wpdb->comments WHERE comment_ID='$comID' AND comment_approved='1'".$restriction);
 
 
 
 
 
 
 
 
855
  break;
856
  }
857
- if($cpostID!=NULL) relevanssi_publish($cpostID);
 
 
858
  }
859
- //Added by OdditY END <-
860
 
861
- function relevanssi_get_comments($postID) {
 
 
 
 
 
 
 
 
 
 
862
  global $wpdb;
863
 
864
- if (apply_filters('relevanssi_index_comments_exclude', false, $postID))
865
- return "";
 
 
 
 
 
 
 
866
 
867
- $comtype = get_option("relevanssi_index_comments");
868
- $restriction = "";
869
- $comment_string = "";
870
- switch ($comtype) {
871
- case "all":
872
- // all (incl. customs, track- & pingbacks)
873
- break;
874
- case "normal":
875
- // normal (excl. customs, track- & pingbacks)
876
- $restriction=" AND comment_type='' ";
877
- break;
878
- default:
879
- // none (don't index)
880
- return "";
881
  }
882
 
883
- $to = 20;
884
- $from = 0;
885
 
886
  while ( true ) {
887
- $sql = "SELECT comment_ID, comment_content, comment_author
888
- FROM $wpdb->comments
889
- WHERE comment_post_ID = '$postID'
890
- AND comment_approved = '1'
891
- ".$restriction."
892
- LIMIT $from, $to";
893
- $comments = $wpdb->get_results($sql);
894
- if (sizeof($comments) == 0) break;
895
- foreach($comments as $comment) {
896
- $comment_string .= apply_filters('relevanssi_comment_content_to_index', $comment->comment_author . ' ' . $comment->comment_content . ' ', $comment->comment_ID);
897
  }
898
- $from += $to;
 
 
 
 
 
 
 
 
 
899
  }
900
 
901
  return $comment_string;
902
  }
903
 
904
  /**
905
- * Indexes the human-readable value of "choice" options list ACF
906
- * Droz Raphaël, June 2016
 
 
 
 
 
 
907
  */
908
- function relevanssi_index_acf(&$insert_data, $post_id, $field_name, $field_value) {
909
- if (!is_admin() ) include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); // otherwise is_plugin_active will cause a fatal error
910
- if (!function_exists('is_plugin_active')) return;
911
- if (!is_plugin_active('advanced-custom-fields/acf.php') &&
912
- !is_plugin_active('advanced-custom-fields-pro/acf.php')) return;
913
-
914
- if (!function_exists('get_field_object')) return; // ACF is active, but not loaded.
 
 
 
 
 
 
915
 
916
- $field_object = get_field_object($field_name, $post_id);
917
- if (!isset($field_object['choices'])) return; // not a "select" field
918
- if (is_array($field_value)) return; // not handled (currently)
919
- if (!isset($field_object['choices'][$field_value])) return; // value does not exist
 
 
 
 
 
 
920
 
921
- if ( ($value = $field_object['choices'][$field_value]) ) {
922
- if (!isset($insert_data[$value]['customfield']) ) $insert_data[$value]['customfield'] = 0;
923
- $insert_data[$value]['customfield']++;
 
 
 
924
  }
925
  }
926
 
927
  /**
928
  * Truncates the Relevanssi index.
 
 
 
 
 
 
929
  */
930
  function relevanssi_truncate_index() {
931
  global $wpdb, $relevanssi_variables;
932
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
933
- return $wpdb->query("TRUNCATE TABLE $relevanssi_table");
934
  }
935
 
936
- function relevanssi_remove_doc($id, $keep_internal_links = false) {
937
- if (function_exists('relevanssi_premium_remove_doc')) {
938
- relevanssi_premium_remove_doc($id, $keep_internal_links);
939
- }
940
- else {
 
 
 
 
 
 
 
 
 
 
 
 
941
  global $wpdb, $relevanssi_variables;
942
-
943
- $D = get_option( 'relevanssi_doc_count');
944
-
945
- $q = "DELETE FROM " . $relevanssi_variables['relevanssi_table'] . " WHERE doc=$id";
946
- $wpdb->query($q);
947
- $rows_updated = $wpdb->query($q);
948
-
949
- if($rows_updated && $rows_updated > 0) {
950
- update_option('relevanssi_doc_count', $D - $rows_updated);
951
- }
 
 
 
 
952
  }
953
  }
1
  <?php
2
+ /**
3
+ * /lib/indexing.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
 
11
+ /**
12
+ * Returns the total number of posts to index.
13
+ *
14
+ * Counts the total number of posts to index, considering post type restrictions and
15
+ * the valid statuses.
16
+ *
17
+ * @return int The number of posts to index.
18
+ */
19
  function relevanssi_count_total_posts() {
 
 
 
 
 
20
  $extend = false;
21
+ return relevanssi_indexing_post_counter( $extend );
 
 
 
 
 
 
 
 
 
22
  }
23
 
24
+ /**
25
+ * Returns the number of posts missing from the index.
26
+ *
27
+ * Counts the total number of posts to index, considering post type restrictions and
28
+ * the valid statuses, and only looks at posts missing from the index.
29
+ *
30
+ * @return int The number of posts to index.
31
+ */
32
  function relevanssi_count_missing_posts() {
 
 
 
 
 
33
  $extend = true;
34
+ return relevanssi_indexing_post_counter( $extend );
35
+ }
36
 
37
+ /**
38
+ * Counts the total number of posts.
39
+ *
40
+ * Counts the total number of posts to index, considering post type restrictions and
41
+ * the valid statuses.
42
+ *
43
+ * @global object $wpdb The WordPress database interface.
44
+ * @global array $relevanssi_variables The Relevanssi global variables array, used
45
+ * for table names.
46
+ *
47
+ * @param boolean $extend If true, count only missing posts. If false, count all
48
+ * posts. Default false.
49
+ *
50
+ * @return int The number of posts to index.
51
+ */
52
+ function relevanssi_indexing_post_counter( $extend = false ) {
53
+ global $wpdb, $relevanssi_variables;
54
+ $relevanssi_table = $relevanssi_variables['relevanssi_table'];
55
+ $restriction = relevanssi_post_type_restriction();
56
+ $valid_status = relevanssi_valid_status_array();
57
+ $limit = '';
58
+
59
+ $query = relevanssi_generate_indexing_query( $valid_status, $extend, $restriction, $limit );
60
+ $query = str_replace( 'SELECT post.ID', 'SELECT COUNT(post.ID)', $query );
61
+
62
+ /**
63
+ * Allows actions to happen before the indexing query is run.
64
+ *
65
+ * The indexing query fetches a list of posts to index (either all posts or only
66
+ * those missing from the index, depending on the case).
67
+ */
68
+ do_action( 'relevanssi_pre_indexing_query' );
69
+ $count = $wpdb->get_var( $query ); // WPCS: unprepared SQL ok.
70
+
71
+ if ( empty( $count ) ) {
72
+ $count = 0;
73
+ }
74
 
75
  return $count;
76
  }
77
 
78
+ /**
79
+ * Generates the indexing query.
80
+ *
81
+ * Generates the query that fetches the list of posts to index. The parameters are
82
+ * assumed to be safely escaped. In regular use, the values are generated by
83
+ * Relevanssi functions which provide reliable source data.
84
+ *
85
+ * @global object $wpdb The WordPress database interface.
86
+ * @global array $relevanssi_variables The Relevanssi global variables array, used
87
+ * for table names.
88
+ *
89
+ * @param string $valid_status Comma-separated list of valid post statuses.
90
+ * @param boolean $extend If true, only care about posts missing from the
91
+ * index. If false, take all posts. Default false.
92
+ * @param string $restriction Query restrictions, MySQL code that restricts the
93
+ * posts fetched in the desired way. Default ''.
94
+ * @param string $limit MySQL code to set the LIMIT and OFFSET values.
95
+ * Default ''.
96
+ *
97
+ * @return string MySQL query to fetch the posts.
98
+ */
99
+ function relevanssi_generate_indexing_query( $valid_status, $extend = false, $restriction = '', $limit = '' ) {
100
  global $wpdb, $relevanssi_variables;
101
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
102
+
103
+ if ( ! $extend ) {
104
  $q = "SELECT post.ID
105
  FROM $wpdb->posts post
106
  LEFT JOIN $wpdb->posts parent ON (post.post_parent=parent.ID)
114
  )
115
  ))
116
  $restriction ORDER BY post.ID DESC $limit";
117
+ } else {
 
118
  $q = "SELECT post.ID
119
  FROM $wpdb->posts post
120
  LEFT JOIN $wpdb->posts parent ON (post.post_parent=parent.ID)
137
  return $q;
138
  }
139
 
140
+ /**
141
+ * Generates a post type restriction.
142
+ *
143
+ * Generates a post type restriction for the MySQL query based on the
144
+ * 'relevanssi_index_post_types' option.
145
+ *
146
+ * @return string MySQL code for the post type restriction.
147
+ */
148
  function relevanssi_post_type_restriction() {
149
+ $post_types = array();
150
+ $restriction = '';
151
 
152
+ $types = get_option( 'relevanssi_index_post_types' );
153
+ if ( ! is_array( $types ) ) {
154
+ $types = array();
 
 
155
  }
156
+ foreach ( $types as $type ) {
157
+ if ( 'bogus' === $type ) {
158
+ // 'bogus' is not a real post type Relevanssi uses to make sure
159
+ // the post type setting is saved, even if it's empty.
160
+ continue;
161
+ }
162
+ if ( post_type_exists( $type ) ) {
163
+ // Only accept post types that actually exist.
164
+ array_push( $post_types, "'$type'" );
165
+ }
166
  }
167
+
168
+ if ( count( $post_types ) > 0 ) {
169
+ $restriction = ' AND post.post_type IN (' . implode( ', ', $post_types ) . ') ';
170
  }
171
 
172
  return $restriction;
173
  }
174
 
175
+ /**
176
+ * Generates a list of valid post statuses.
177
+ *
178
+ * Generates a list of valid post statuses to use in indexing. By default, Relevanssi
179
+ * accepts 'publish', 'draft', 'private', 'pending', and 'future'. If you need to use
180
+ * a custom post status, you can use the 'relevanssi_valid_status' filter hook to add
181
+ * your own post status to the list of valid statuses.
182
+ *
183
+ * @return string A comma-separated list of valid post statuses.
184
+ */
185
  function relevanssi_valid_status_array() {
186
+ /**
187
+ * Filters the valid status array.
188
+ *
189
+ * Allows you to modify the array that contains all valid post statuses for
190
+ * Relevanssi post indexing.
191
+ *
192
+ * @return array Array of post statuses.
193
+ */
194
+ $valid_status_array = apply_filters( 'relevanssi_valid_status', array( 'publish', 'draft', 'private', 'pending', 'future' ) );
195
+ $valid_status = array();
196
+
197
+ if ( is_array( $valid_status_array ) && count( $valid_status_array ) > 0 ) {
198
+ $post_stati = get_post_stati( array(), 'names' );
199
+ foreach ( $valid_status_array as $status ) {
200
+ if ( in_array( $status, $post_stati, true ) ) {
201
+ // Only include post statuses that actually exist.
202
+ $valid_status[] = "'$status'";
203
+ }
204
  }
205
+ $valid_status = implode( ',', $valid_status );
206
+ } else {
207
+ // If the filter makes the setting a non-array, fall back to reasonable
208
+ // default values.
209
  $valid_status = "'publish', 'draft', 'private', 'pending', 'future'";
210
  }
211
 
212
  return $valid_status;
213
  }
214
 
215
+ /**
216
+ * Builds the index.
217
+ *
218
+ * @global object $wpdb The WordPress database interface.
219
+ * @global array $relevanssi_variables The Relevanssi global variables array, used
220
+ * for table names.
221
+ *
222
+ * @param boolean|int $extend_offset If numeric, offsets the indexing by that
223
+ * amount. If true, doesn't truncate the index before indexing. If false, truncates
224
+ * index before indexing. Default false.
225
+ * @param boolean $verbose If true, echoes out information. Default true.
226
+ * @param int $post_limit How many posts to index. Default null, no limit.
227
+ * @param boolean $is_ajax If true, indexing is done in AJAX context.
228
+ * Default false.
229
+ *
230
+ * @return array In AJAX context, returns array with two values: 'indexing_complete'
231
+ * tells whether indexing is completed or not, and 'indexed' returns the number of
232
+ * posts indexed. Outside AJAX context, these values are returned as an array in
233
+ * format of array(completed, posts indexed).
234
+ */
235
+ function relevanssi_build_index( $extend_offset = false, $verbose = true, $post_limit = null, $is_ajax = false ) {
236
  global $wpdb, $relevanssi_variables;
237
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
238
 
239
+ // Thanks to Julien Mession. This speeds up indexing a lot.
240
+ wp_suspend_cache_addition( true );
241
+ set_time_limit( 0 );
242
 
243
+ // The values generated by these functions are safe to use for MySQL.
244
+ $restriction = relevanssi_post_type_restriction();
245
  $valid_status = relevanssi_valid_status_array();
246
 
247
+ $n = 0;
248
  $size = 0;
249
 
250
+ if ( false === $extend_offset ) {
251
+ // Truncate the index first.
252
  relevanssi_truncate_index();
253
 
254
+ // Premium feature: index taxonomy terms.
255
+ if ( function_exists( 'relevanssi_index_taxonomies' ) ) {
256
+ if ( 'on' === get_option( 'relevanssi_index_taxonomies' ) ) {
257
  relevanssi_index_taxonomies();
258
  }
259
  }
260
 
261
+ // Premium feature: index user profiles.
262
+ if ( function_exists( 'relevanssi_index_users' ) ) {
263
+ if ( 'on' === get_option( 'relevanssi_index_users' ) ) {
264
  relevanssi_index_users();
265
  }
266
  }
267
 
268
+ // If $post_limit parameter is present, numeric and > 0, use that.
269
+ $limit = '';
270
+ if ( isset( $post_limit ) && is_numeric( $post_limit ) && $post_limit > 0 ) {
271
+ $size = $post_limit;
272
  $limit = " LIMIT $post_limit";
273
  }
274
 
275
+ $query = relevanssi_generate_indexing_query( $valid_status, $extend_offset, $restriction, $limit );
276
 
277
+ update_option( 'relevanssi_index', '' );
278
+ } elseif ( ! is_numeric( $extend_offset ) ) {
279
+ // Extending, so do not truncate and skip the posts already in the index.
280
+ $limit = get_option( 'relevanssi_index_limit', 200 );
 
281
 
282
+ // If $post_limit parameter is present, numeric and > 0, use that.
283
+ if ( isset( $post_limit ) && is_numeric( $post_limit ) && $post_limit > 0 ) {
284
+ $limit = $post_limit;
285
+ }
286
 
287
+ if ( is_numeric( $limit ) && $limit > 0 ) {
288
+ $size = $limit;
289
  $limit = " LIMIT $limit";
290
+ } else {
291
+ $limit = '';
 
292
  }
293
 
294
  $extend = true;
295
+ $query = relevanssi_generate_indexing_query( $valid_status, $extend, $restriction, $limit );
296
+ } else { // $extend_offset is numeric.
297
+ // Extending, so do not truncate and skip the posts already in the index.
298
+ $limit = get_option( 'relevanssi_index_limit', 200 );
299
+
300
+ // If $post_limit parameter is present, numeric and > 0, use that.
301
+ if ( isset( $post_limit ) && is_numeric( $post_limit ) && $post_limit > 0 ) {
302
+ $limit = $post_limit;
303
+ }
304
 
305
+ if ( is_numeric( $limit ) && $limit > 0 ) {
306
+ $size = $limit;
 
 
 
 
 
 
 
 
 
307
  $limit = " LIMIT $limit OFFSET $extend_offset";
308
+ } else {
309
+ $limit = '';
 
310
  }
311
 
312
+ // Extend is set to false, because $limit now has LIMIT and OFFSET.
313
+ $extend = false;
314
+ $query = relevanssi_generate_indexing_query( $valid_status, $extend, $restriction, $limit );
315
  }
316
 
317
  $custom_fields = relevanssi_get_custom_fields();
318
 
319
+ /* This action documented earlier in lib/indexing.php. */
320
+ do_action( 'relevanssi_pre_indexing_query' );
321
+ $content = $wpdb->get_results( $query ); // WPCS: unprepared SQL ok.
322
 
323
+ if ( defined( 'WP_CLI' ) && WP_CLI && function_exists( 'relevanssi_generate_progress_bar' ) ) {
324
+ $progress = relevanssi_generate_progress_bar( 'Indexing posts', count( $content ) );
325
+ }
 
 
 
326
 
327
+ $remove_first = false;
328
+ $bypass_global_post = true; // $bypassglobalpost set to true, because at this
329
+ // point global $post should be null, but in some cases it is not.
330
+ foreach ( $content as $post ) {
331
+ $result = relevanssi_index_doc( $post->ID, $remove_first, $custom_fields, $bypass_global_post );
332
+ if ( is_numeric( $result ) && $result > 0 ) {
333
+ // $n calculates the number of posts indexed.
334
+ $n++;
335
+ }
336
+ if ( defined( 'WP_CLI' ) && WP_CLI && $progress ) {
337
+ $progress->tick();
338
+ }
339
+ }
340
+ if ( defined( 'WP_CLI' ) && WP_CLI && $progress ) {
341
+ $progress->finish();
342
  }
 
343
 
344
+ // To prevent empty indices.
345
+ $wpdb->query( "ANALYZE TABLE $relevanssi_table" ); // WPCS: unprepared SQL ok, just Relevanssi table name.
346
 
347
  $complete = false;
348
+ if ( ( 0 === $size ) || ( count( $content ) < $size ) ) {
349
+ $message = __( 'Indexing complete!', 'relevanssi' );
350
+ $complete = true;
351
+ } else {
352
+ $message = __( 'More to index...', 'relevanssi' );
 
 
 
353
  }
354
+ if ( $verbose ) {
355
+ printf( '<div id="message" class="updated fade"><p>%s</p></div>', esc_html( $message ) );
356
  }
357
 
358
+ update_option( 'relevanssi_indexed', 'done' );
359
 
360
+ // Update the document count variable.
361
+ $document_count = $wpdb->get_var( "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi" ); // WPCS: unprepared SQL ok, Relevanssi table name.
362
+ update_option( 'relevanssi_doc_count', $document_count );
363
 
364
+ wp_suspend_cache_addition( false );
 
365
 
366
+ if ( $is_ajax ) {
367
  $response = array(
368
  'indexing_complete' => $complete,
369
+ 'indexed' => $n,
370
  );
371
  return $response;
372
  }
373
 
374
+ return array( $complete, $n );
375
  }
376
 
377
+ /**
378
+ * Indexes one document.
379
+ *
380
+ * Different cases:
381
+ *
382
+ * Build index:
383
+ * - global $post is null, $index_post is a post object.
384
+ *
385
+ * Update post:
386
+ * - global $post has the original $post, $index_post is the ID of revision.
387
+ *
388
+ * Quick edit:
389
+ * - global $post is an array, $index_post is the ID of current revision.
390
+ *
391
+ * @global object $wpdb The WordPress database interface.
392
+ * @global array $relevanssi_variables The Relevanssi global variables array, used
393
+ * for table names.
394
+ * @global object $post The global post object.
395
+ *
396
+ * @param object|int $index_post The post to index, either post object or
397
+ * post ID.
398
+ * @param boolean $remove_first If true, remove the post from the index
399
+ * before indexing. Default false.
400
+ * @param array $custom_fields The custom fields that are indexed for the
401
+ * post.
402
+ * @param boolean $bypass_global_post If true, do not use the global $post object.
403
+ * Default false.
404
+ * @param boolean $debug If true, echo out debugging information.
405
+ * Default false.
406
+ */
407
+ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_fields = false, $bypass_global_post = false, $debug = false ) {
408
  global $wpdb, $post, $relevanssi_variables;
409
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
410
+ $post_was_null = false;
411
+ $previous_post = null;
 
 
 
412
 
413
+ // Check if this is a Jetpack Contact Form entry.
414
+ if ( isset( $_REQUEST['contact-form-id'] ) ) { // WPCS: CSRF ok.
415
+ return;
 
 
 
 
416
  }
417
+
418
+ if ( $bypass_global_post ) {
419
+ // If $bypass_global_post is set, relevanssi_index_doc() will index the post
420
+ // object or post ID as specified in $index_post.
421
+ if ( isset( $post ) ) {
422
+ $previous_post = $post;
423
+ } else {
424
+ $post_was_null = true;
425
+ }
426
+
427
+ if ( is_object( $index_post ) ) {
428
+ $post = $index_post; // WPCS: override ok.
429
+ } else {
430
+ $post = get_post( $index_post ); // WPCS: override ok.
431
+ }
432
+ } else {
433
+ // Quick edit has an array in the global $post, so fetch the post ID for the
434
+ // post to edit.
435
+ if ( is_array( $post ) ) {
436
+ $post = get_post( $post['ID'] ); // WPCS: override ok.
437
  }
438
 
439
+ if ( empty( $post ) ) {
440
+ // No $post set, so we need to use $indexpost, if it's a post object.
441
  $post_was_null = true;
442
+ if ( is_object( $index_post ) ) {
443
+ $post = $index_post; // WPCS: override ok.
444
+ } else {
445
+ $post = get_post( $index_post ); // WPCS: override ok.
446
  }
447
+ } else {
 
 
 
 
448
  // $post was set, let's grab the previous value in case we need it
449
  $previous_post = $post;
450
  }
451
  }
452
 
453
+ if ( null === $post ) {
454
  // At this point we should have something in $post; if not, quit.
455
+ if ( $post_was_null ) {
456
+ $post = null; // WPCS: override ok.
457
+ }
458
+ if ( $previous_post ) {
459
+ $post = $previous_post; // WPCS: override ok.
460
+ }
461
  return -1;
462
  }
463
 
464
+ // Finally fetch the post again by ID. Complicated, yes, but unless we do this,
465
+ // we might end up indexing the post before the updates come in.
466
+ $post = get_post( $post->ID ); // WPCS: override ok.
467
 
468
+ // Post exclusion feature from Relevanssi Premium.
469
+ if ( function_exists( 'relevanssi_hide_post' ) ) {
470
+ if ( relevanssi_hide_post( $post->ID ) ) {
471
+ if ( $debug ) {
472
+ relevanssi_debug_echo( 'relevanssi_hide_post() returned true.' );
473
+ }
474
+ if ( $post_was_null ) {
475
+ $post = null; // WPCS: override ok.
476
+ }
477
+ if ( $previous_post ) {
478
+ $post = $previous_post; // WPCS: override ok.
479
+ }
480
+ return 'hide';
481
  }
482
  }
483
 
484
+ $index_this_post = false;
 
485
  $post->indexing_content = true;
 
 
 
486
 
487
+ $index_types = get_option( 'relevanssi_index_post_types' );
488
+ if ( ! is_array( $index_types ) ) {
489
+ $index_types = array();
490
+ }
491
+ if ( in_array( $post->post_type, $index_types, true ) ) {
492
+ $index_this_post = true;
493
+ }
494
+
495
+ /**
496
+ * Filters whether a post is indexed or not.
497
+ *
498
+ * Allows you to filter whether a post is indexed or not.
499
+ *
500
+ * @param boolean If true, the post is not indexed. Default false.
501
+ * @param int The post ID.
502
+ */
503
+ if ( true === apply_filters( 'relevanssi_do_not_index', false, $post->ID ) ) {
504
+ // Filter says no.
505
+ if ( $debug ) {
506
+ relevanssi_debug_echo( 'relevanssi_do_not_index returned true.' );
507
+ }
508
  $index_this_post = false;
509
  }
510
 
511
+ if ( $remove_first ) {
512
+ // We are updating a post, so remove the old stuff first.
513
+ relevanssi_remove_doc( $post->ID, true );
514
+ if ( function_exists( 'relevanssi_remove_item' ) ) {
515
+ relevanssi_remove_item( $post->ID, 'post' );
516
+ }
517
+ if ( $debug ) {
518
+ relevanssi_debug_echo( 'Removed the post from the index.' );
519
  }
 
520
  }
521
 
522
+ // This needs to be here, after the call to relevanssi_remove_doc(), because
523
+ // otherwise a post that's in the index but shouldn't be there won't get removed.
524
+ if ( ! $index_this_post ) {
525
+ if ( $post_was_null ) {
526
+ $post = null; // WPCS: override ok.
527
+ }
528
+ if ( $previous_post ) {
529
+ $post = $previous_post; // WPCS: override ok.
530
+ }
531
+ return 'donotindex';
532
  }
533
 
534
  $n = 0;
535
 
536
+ /**
537
+ * Allows filtering the indexed post before indexing.
538
+ *
539
+ * @param object $post The post object.
540
+ * @param object $post The post object again (in other uses for this filter, the
541
+ * second parameter actually makes sense).
542
+ */
543
+ $post = apply_filters( 'relevanssi_post_to_index', $post, $post ); // WPCS: override ok.
544
+
545
+ $min_word_length = get_option( 'relevanssi_min_word_length', 3 );
546
+ $insert_data = array();
547
+
548
+ if ( 'none' !== get_option( 'relevanssi_index_comments' ) ) {
549
+ if ( $debug ) {
550
+ relevanssi_debug_echo( 'Indexing comments.' );
551
+ }
552
+ $post_comments = relevanssi_get_comments( $post->ID );
553
+ if ( ! empty( $post_comments ) ) {
554
+ $post_comments = relevanssi_strip_invisibles( $post_comments );
555
+ $post_comments = preg_replace( '/<[a-zA-Z\/][^>]*>/', ' ', $post_comments );
556
+ $post_comments = strip_tags( $post_comments );
557
+ if ( $debug ) {
558
+ relevanssi_debug_echo( "Comment content: $post_comments" );
559
+ }
560
+ $post_comments_tokens = relevanssi_tokenize( $post_comments, true, $min_word_length );
561
+ if ( count( $post_comments_tokens ) > 0 ) {
562
+ foreach ( $post_comments_tokens as $token => $count ) {
563
  $n++;
564
+ $insert_data[ $token ]['comment'] = $count;
565
  }
566
  }
567
  }
568
+ }
 
 
 
569
 
570
+ // Process taxonomies.
571
+ $taxonomies = get_option( 'relevanssi_index_taxonomies_list', array() );
572
+ foreach ( $taxonomies as $taxonomy ) {
573
+ if ( $debug ) {
574
+ relevanssi_debug_echo( "Indexing taxonomy terms for $taxonomy" );
575
+ }
576
+ $insert_data = relevanssi_index_taxonomy_terms( $post, $taxonomy, $insert_data );
577
  }
578
 
579
+ // Index post author.
580
+ if ( 'on' === get_option( 'relevanssi_index_author' ) ) {
581
+ $author_id = $post->post_author;
582
+ $display_name = get_the_author_meta( 'display_name', $author_id );
583
+ $name_tokens = relevanssi_tokenize( $display_name, false, $min_word_length );
584
+ if ( $debug ) {
585
+ relevanssi_debug_echo( 'Indexing post author as: ' . implode( ' ', array_keys( $name_tokens ) ) );
586
+ }
587
+ foreach ( $name_tokens as $token => $count ) {
588
+ if ( isset( $insert_data[ $token ]['author'] ) ) {
589
+ $insert_data[ $token ]['author'] += $count;
590
+ } else {
591
+ $insert_data[ $token ]['author'] = $count;
592
+ }
593
  }
594
  }
595
 
596
+ // Indexing custom fields.
597
  $remove_underscore_fields = false;
598
+ if ( isset( $custom_fields ) && 'all' === $custom_fields ) {
599
+ $custom_fields = get_post_custom_keys( $post->ID );
600
+ }
601
+ if ( isset( $custom_fields ) && 'visible' === $custom_fields ) {
602
+ $custom_fields = get_post_custom_keys( $post->ID );
603
  $remove_underscore_fields = true;
604
  }
605
+ /**
606
+ * Filters the list of custom fields to index before indexing.
607
+ *
608
+ * @param array $custom_fields List of custom field names.
609
+ * @param int $post->ID The post ID.
610
+ */
611
+ $custom_fields = apply_filters( 'relevanssi_index_custom_fields', $custom_fields, $post->ID );
612
+ if ( is_array( $custom_fields ) ) {
613
+ if ( $debug ) {
614
+ relevanssi_debug_echo( 'Custom fields to index: ' . implode( ', ', $custom_fields ) );
615
+ }
616
+ $custom_fields = array_unique( $custom_fields );
617
 
618
+ // Premium includes some support for ACF repeater fields.
619
  $repeater_fields = array();
620
+ if ( function_exists( 'relevanssi_add_repeater_fields' ) ) {
621
+ relevanssi_add_repeater_fields( $custom_fields, $post->ID );
622
+ }
623
+
624
+ foreach ( $custom_fields as $field ) {
625
+ if ( $remove_underscore_fields ) {
626
+ if ( '_relevanssi_pdf_content' !== $field && '_' === substr( $field, 0, 1 ) ) {
627
+ // We always want to index _relevanssi_pdf_content.
628
+ continue;
629
+ }
630
+ }
631
 
632
+ /**
633
+ * Filters the custom field value before indexing.
634
+ *
635
+ * @param array Custom field values.
636
+ * @param string $field The custom field name.
637
+ * @param int $post->ID The post ID.
638
+ */
639
+ $values = apply_filters( 'relevanssi_custom_field_value', get_post_meta( $post->ID, $field, false ), $field, $post->ID );
640
+
641
+ if ( empty( $values ) || ! is_array( $values ) ) {
642
+ continue;
643
  }
644
+
645
+ foreach ( $values as $value ) {
646
+ // Quick hack : allow indexing of PODS relationship custom fields // TMV.
647
+ if ( is_array( $value ) && isset( $value['post_title'] ) ) {
648
+ $value = $value['post_title'];
649
+ }
650
+
651
+ // Handle ACF fields.
652
+ relevanssi_index_acf( $insert_data, $post->ID, $field, $value );
653
+
654
+ // Flatten other arrays.
655
+ if ( is_array( $value ) ) {
656
+ $value = relevanssi_flatten_array( $value );
657
+ }
658
+
659
+ if ( $debug ) {
660
+ relevanssi_debug_echo( "\tKey: " . $field . ' – value: ' . $value );
661
+ }
662
+
663
+ $value_tokens = relevanssi_tokenize( $value, true, $min_word_length );
664
+ foreach ( $value_tokens as $token => $count ) {
665
+ if ( ! isset( $insert_data[ $token ]['customfield'] ) ) {
666
+ $insert_data[ $token ]['customfield'] = 0;
667
+ }
668
+ $insert_data[ $token ]['customfield'] += $count;
669
+
670
+ // Premium indexes more detail about custom fields.
671
+ if ( function_exists( 'relevanssi_customfield_detail' ) ) {
672
+ $insert_data = relevanssi_customfield_detail( $insert_data, $token, $count, $field );
673
  }
674
  }
675
  }
676
  }
677
  }
678
 
679
+ // Indexing excerpts.
680
+ if ( isset( $post->post_excerpt ) && ( 'on' === get_option( 'relevanssi_index_excerpt' ) || 'attachment' === $post->post_type ) ) {
681
+ // Include excerpt for attachments which use post_excerpt for captions - modified by renaissancehack.
682
+ if ( $debug ) {
683
+ relevanssi_debug_echo( "Indexing post excerpt: $post->post_excerpt" );
684
+ }
685
+ $excerpt_tokens = relevanssi_tokenize( $post->post_excerpt, true, $min_word_length );
686
+ foreach ( $excerpt_tokens as $token => $count ) {
687
+ if ( ! isset( $insert_data[ $token ]['excerpt'] ) ) {
688
+ $insert_data[ $token ]['excerpt'] = 0;
689
+ }
690
+ $insert_data[ $token ]['excerpt'] += $count;
691
  }
692
  }
693
 
694
+ // Premium can index arbitrary MySQL columns.
695
+ if ( function_exists( 'relevanssi_index_mysql_columns' ) ) {
696
+ if ( $debug ) {
697
+ relevanssi_debug_echo( 'Indexing MySQL columns.' );
698
+ }
699
+ $insert_data = relevanssi_index_mysql_columns( $insert_data, $post->ID );
700
  }
701
 
702
+ // Premium can index PDF content for the parent post.
703
+ if ( function_exists( 'relevanssi_index_pdf_for_parent' ) ) {
704
+ if ( $debug ) {
705
+ relevanssi_debug_echo( 'Indexing PDF content for parent post.' );
706
+ }
707
+ $insert_data = relevanssi_index_pdf_for_parent( $insert_data, $post->ID );
708
  }
709
 
710
  $index_titles = true;
711
+ if ( ! empty( $post->post_title ) ) {
712
+ /**
713
+ * If this filter returns false, titles are not indexed at all.
714
+ *
715
+ * @param boolean Return false to prevent titles from being indexed. Default true.
716
+ */
717
+ if ( apply_filters( 'relevanssi_index_titles', $index_titles ) ) {
718
+ if ( $debug ) {
719
+ relevanssi_debug_echo( 'Indexing post title.' );
720
+ }
721
+ /**
722
+ * Filters the title before tokenizing and indexing.
723
+ *
724
+ * @param string $post->post_title The title.
725
+ * @param object $post The full post object.
726
+ */
727
+ $filtered_title = apply_filters( 'relevanssi_post_title_before_tokenize', $post->post_title, $post );
728
+
729
+ /** This filter is documented in wp-includes/post-template.php */
730
+ $filtered_title = apply_filters( 'the_title', $filtered_title, $post->ID );
731
+ /**
732
+ * Filters whether stopwords should be removed from titles in tokenizing or not.
733
+ *
734
+ * @param boolean If true, remove stopwords. Default true.
735
+ */
736
+ $title_tokens = relevanssi_tokenize( $filtered_title, apply_filters( 'relevanssi_remove_stopwords_in_titles', true ), $min_word_length );
737
+ if ( $debug ) {
738
+ relevanssi_debug_echo( "\tTitle, tokenized: " . implode( ' ', array_keys( $titles ) ) );
739
+ }
740
+
741
+ if ( count( $title_tokens ) > 0 ) {
742
+ foreach ( $title_tokens as $token => $count ) {
743
  $n++;
744
+ if ( ! isset( $insert_data[ $token ]['title'] ) ) {
745
+ $insert_data[ $token ]['title'] = 0;
746
+ }
747
+ $insert_data[ $token ]['title'] += $count;
748
  }
749
  }
750
  }
751
  }
752
 
753
+ // Content indexing.
754
  $index_content = true;
755
+ /**
756
+ * If this filter returns false, post content is not indexed at all.
757
+ *
758
+ * @param boolean Return false to prevent post content from being indexed. Default true.
759
+ */
760
+ if ( apply_filters( 'relevanssi_index_content', $index_content ) ) {
761
+ if ( $debug ) {
762
+ relevanssi_debug_echo( 'Indexing post content.' );
763
+ }
764
+ remove_shortcode( 'noindex' );
765
+ add_shortcode( 'noindex', 'relevanssi_noindex_shortcode_indexing' );
766
+
767
+ /**
768
+ * Filters the post content before indexing.
769
+ *
770
+ * @param string $post->post_content The post content.
771
+ * @param object $post The full post object.
772
+ */
773
+ $contents = apply_filters( 'relevanssi_post_content', $post->post_content, $post );
774
+ if ( $debug ) {
775
+ relevanssi_debug_echo( "\tPost content after relevanssi_post_content:\n$contents" );
776
+ }
777
+
778
+ /**
779
+ * Can be used to add extra content to the post before indexing.
780
+ *
781
+ * @author Alexander Gieg
782
+ *
783
+ * @param string The additional content.
784
+ * @param object $post The post object.
785
+ */
786
+ $additional_content = trim( apply_filters( 'relevanssi_content_to_index', '', $post ) );
787
+ if ( ! empty( $additional_content ) ) {
788
+ $contents .= ' ' . $additional_content;
789
+ if ( $debug ) {
790
+ relevanssi_debug_echo( "\tAdditional content from relevanssi_content_to_index:\n$contents" );
791
+ }
792
+ }
793
+
794
+ if ( 'on' === get_option( 'relevanssi_expand_shortcodes' ) ) {
795
+ if ( function_exists( 'do_shortcode' ) ) {
796
+ // WP Table Reloaded support.
797
+ if ( defined( 'WP_TABLE_RELOADED_ABSPATH' ) ) {
798
+ include_once WP_TABLE_RELOADED_ABSPATH . 'controllers/controller-frontend.php';
799
+ $my_wp_table_reloaded = new WP_Table_Reloaded_Controller_Frontend();
800
  }
801
+ // TablePress support.
802
+ if ( defined( 'TABLEPRESS_ABSPATH' ) ) {
803
+ if ( ! isset( TablePress::$model_options ) ) {
804
+ include_once TABLEPRESS_ABSPATH . 'classes/class-model.php';
805
+ include_once TABLEPRESS_ABSPATH . 'models/model-options.php';
806
  TablePress::$model_options = new TablePress_Options_Model();
807
  }
808
+ $my_tablepress_controller = TablePress::load_controller( 'frontend' );
809
+ $my_tablepress_controller->init_shortcodes();
810
  }
811
 
812
+ $disable_shortcodes = get_option( 'relevanssi_disable_shortcodes' );
813
+ $shortcodes = explode( ',', $disable_shortcodes );
814
+ foreach ( $shortcodes as $shortcode ) {
815
+ remove_shortcode( trim( $shortcode ) );
816
  }
817
+ remove_shortcode( 'contact-form' ); // Jetpack Contact Form causes an error message.
818
+ remove_shortcode( 'starrater' ); // GD Star Rating rater shortcode causes problems.
819
+ remove_shortcode( 'responsive-flipbook' ); // Responsive Flipbook causes problems.
820
+ remove_shortcode( 'avatar_upload' ); // WP User Avatar is incompatible.
821
+ remove_shortcode( 'product_categories' ); // A problematic WooCommerce shortcode.
822
+ remove_shortcode( 'recent_products' ); // A problematic WooCommerce shortcode.
823
+ remove_shortcode( 'php' ); // PHP Code for Posts.
824
+ remove_shortcode( 'watupro' ); // Watu PRO doesn't co-operate.
825
+ remove_shortcode( 'starbox' ); // Starbox shortcode breaks Relevanssi.
826
+ remove_shortcode( 'cfdb-save-form-post' ); // Contact Form DB.
827
+ remove_shortcode( 'cfdb-datatable' );
828
+ remove_shortcode( 'cfdb-table' );
829
+ remove_shortcode( 'cfdb-json' );
830
+ remove_shortcode( 'cfdb-value' );
831
+ remove_shortcode( 'cfdb-count' );
832
+ remove_shortcode( 'cfdb-html' );
833
+ remove_shortcode( 'woocommerce_cart' ); // WooCommerce.
834
+ remove_shortcode( 'woocommerce_checkout' );
835
+ remove_shortcode( 'woocommerce_order_tracking' );
836
+ remove_shortcode( 'woocommerce_my_account' );
837
+ remove_shortcode( 'woocommerce_edit_account' );
838
+ remove_shortcode( 'woocommerce_change_password' );
839
+ remove_shortcode( 'woocommerce_view_order' );
840
+ remove_shortcode( 'woocommerce_logout' );
841
+ remove_shortcode( 'woocommerce_pay' );
842
+ remove_shortcode( 'woocommerce_thankyou' );
843
+ remove_shortcode( 'woocommerce_lost_password' );
844
+ remove_shortcode( 'woocommerce_edit_address' );
845
+ remove_shortcode( 'tc_process_payment' );
846
+ remove_shortcode( 'maxmegamenu' ); // Max Mega Menu.
847
+ remove_shortcode( 'searchandfilter' ); // Search and Filter.
848
+ remove_shortcode( 'downloads' ); // Easy Digital Downloads.
849
+ remove_shortcode( 'download_history' );
850
+ remove_shortcode( 'purchase_history' );
851
+ remove_shortcode( 'download_checkout' );
852
+ remove_shortcode( 'purchase_link' );
853
+ remove_shortcode( 'download_cart' );
854
+ remove_shortcode( 'edd_profile_editor' );
855
+ remove_shortcode( 'edd_login' );
856
+ remove_shortcode( 'edd_register' );
857
+ remove_shortcode( 'swpm_protected' ); // Simple Membership Partially Protected content.
858
+ remove_shortcode( 'gravityform' ); // Gravity Forms.
859
 
860
  $post_before_shortcode = $post;
861
+ $contents = do_shortcode( $contents );
862
+ $post = $post_before_shortcode; // WPCS: override ok.
863
 
864
+ if ( defined( 'TABLEPRESS_ABSPATH' ) ) {
865
+ unset( $my_tablepress_controller );
866
  }
867
+ if ( defined( 'WP_TABLE_RELOADED_ABSPATH' ) ) {
868
+ unset( $my_wp_table_reloaded );
869
  }
870
  }
871
+ } else {
872
+ $contents = strip_shortcodes( $contents );
 
873
  }
874
 
875
+ remove_shortcode( 'noindex' );
876
+ add_shortcode( 'noindex', 'relevanssi_noindex_shortcode' );
877
 
878
+ $contents = relevanssi_strip_invisibles( $contents );
879
 
880
+ // Premium feature for better control over internal links.
881
+ if ( function_exists( 'relevanssi_process_internal_links' ) ) {
882
+ $contents = relevanssi_process_internal_links( $contents, $post->ID );
883
  }
884
 
885
+ $contents = preg_replace( '/<[a-zA-Z\/][^>]*>/', ' ', $contents );
886
+ $contents = strip_tags( $contents );
887
+ if ( function_exists( 'wp_encode_emoji' ) ) {
888
+ $contents = wp_encode_emoji( $contents );
889
+ }
890
+ /**
891
+ * Filters the post content in indexing before tokenization.
892
+ *
893
+ * @param string $contents The post content.
894
+ * @param object $post The full post object.
895
+ */
896
+ $contents = apply_filters( 'relevanssi_post_content_before_tokenize', $contents, $post );
897
+ $content_tokens = relevanssi_tokenize( $contents, true, $min_word_length );
898
+
899
+ if ( $debug ) {
900
+ relevanssi_debug_echo( "\tContent, tokenized:\n" . implode( ' ', array_keys( $contents ) ) );
901
+ }
902
 
903
+ if ( count( $content_tokens ) > 0 ) {
904
+ foreach ( $content_tokens as $token => $count ) {
905
+ $n++;
906
+ if ( ! isset( $insert_data[ $token ]['content'] ) ) {
907
+ $insert_data[ $token ]['content'] = 0;
908
+ }
909
+ $insert_data[ $token ]['content'] += $count;
910
  }
911
  }
912
  }
913
 
914
  $type = 'post';
915
+ if ( 'attachment' === $post->post_type ) {
916
+ $type = 'attachment';
917
+ }
918
 
919
+ /**
920
+ * Filters the indexing data before it is converted to INSERT queries.
921
+ *
922
+ * @param array $insert_data All the tokens and their counts.
923
+ * @param object $post The post object.
924
+ */
925
+ $insert_data = apply_filters( 'relevanssi_indexing_data', $insert_data, $post );
926
 
927
  $values = array();
928
+ foreach ( $insert_data as $term => $data ) {
929
+ $fields = array( 'content', 'title', 'comment', 'tag', 'link', 'author', 'category', 'excerpt', 'taxonomy', 'customfield', 'mysqlcolumn' );
930
+ foreach ( $fields as $field ) {
931
+ if ( ! isset( $data[ $field ] ) ) {
932
+ $data[ $field ] = 0;
933
+ }
934
+ }
935
+ if ( ! isset( $data['taxonomy_detail'] ) ) {
936
+ $data['taxonomy_detail'] = '';
937
+ }
938
+ if ( ! isset( $data['customfield_detail'] ) ) {
939
+ $data['customfield_detail'] = '';
940
+ }
941
+ $content = $data['content'];
942
+ $title = $data['title'];
943
+ $comment = $data['comment'];
944
+ $tag = $data['tag'];
945
+ $link = $data['link'];
946
+ $author = $data['author'];
947
+ $category = $data['category'];
948
+ $excerpt = $data['excerpt'];
949
+ $taxonomy = $data['taxonomy'];
950
+ $customfield = $data['customfield'];
951
+ $mysqlcolumn = $data['mysqlcolumn'];
952
+ $taxonomy_detail = $data['taxonomy_detail'];
953
+ $customfield_detail = $data['customfield_detail'];
954
+
955
+ $term = trim( $term );
956
+
957
+ $value = $wpdb->prepare('(%d, %s, REVERSE(%s), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %d)',
958
+ $post->ID, $term, $term, $content, $title, $comment, $tag, $link, $author, $category, $excerpt, $taxonomy, $customfield, $type, $taxonomy_detail, $customfield_detail, $mysqlcolumn);
959
+
960
+ array_push( $values, $value );
961
  }
962
 
963
+ /**
964
+ * Filters the INSERT query VALUES sections before they are inserted in the INSERT query.
965
+ *
966
+ * @param array $values Value sets.
967
+ * @param object $post The post object.
968
+ */
969
+ $values = apply_filters( 'relevanssi_indexing_values', $values, $post );
970
+
971
+ if ( ! empty( $values ) ) {
972
+ $values = implode( ', ', $values );
973
+ $query = "INSERT IGNORE INTO $relevanssi_table (doc, term, term_reverse, content, title, comment, tag, link, author, category, excerpt, taxonomy, customfield, type, taxonomy_detail, customfield_detail, mysqlcolumn) VALUES $values";
974
+ if ( $debug ) {
975
+ relevanssi_debug_echo( "Final indexing query:\n\t$query" );
976
+ }
977
+ $wpdb->query( $query ); // WPCS: unprepared sql ok. The values are Relevanssi-generated and safe.
978
  }
979
 
980
+ if ( $post_was_null ) {
981
+ $post = null; // WPCS: override ok.
982
+ }
983
+ if ( $previous_post ) {
984
+ $post = $previous_post; // WPCS: override ok.
985
+ }
986
 
987
  return $n;
988
  }
991
  * Index taxonomy terms for given post and given taxonomy.
992
  *
993
  * @since 1.8
994
+ *
995
+ * @param object $post Post object, default null.
996
+ * @param string $taxonomy Taxonomy name, default empty string.
997
+ * @param array $insert_data Insert query data array.
998
+ *
999
  * @return array Updated insert query data array.
1000
  */
1001
+ function relevanssi_index_taxonomy_terms( $post = null, $taxonomy = '', $insert_data ) {
1002
  global $wpdb, $relevanssi_variables;
1003
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
1004
 
1005
  $n = 0;
1006
 
1007
+ if ( null === $post || empty( $taxonomy ) ) {
1008
+ return $insert_data;
1009
+ }
1010
 
1011
+ $min_word_length = get_option( 'relevanssi_min_word_length', 3 );
1012
+ $post_taxonomy_terms = get_the_terms( $post->ID, $taxonomy );
1013
+ if ( false !== $post_taxonomy_terms ) {
1014
+ $tag_string = '';
1015
+ foreach ( $post_taxonomy_terms as $post_tag ) {
1016
+ if ( is_object( $post_tag ) ) {
1017
+ $tag_string .= $post_tag->name . ' ';
1018
  }
1019
  }
1020
+ $tag_string = apply_filters( 'relevanssi_tag_before_tokenize', trim( $tag_string ) );
1021
+ $tag_tokens = relevanssi_tokenize( $tag_string, true, $min_word_length );
1022
+ if ( count( $tag_tokens ) > 0 ) {
1023
+ foreach ( $tag_tokens as $token => $count ) {
1024
  $n++;
1025
 
1026
+ if ( 'post_tags' === $taxonomy ) {
1027
+ $insert_data[ $token ]['tag'] = $count;
1028
+ } elseif ( 'category' === $taxonomy ) {
1029
+ $insert_data[ $token ]['category'] = $count;
1030
+ } else {
1031
+ if ( ! isset( $insert_data[ $token ]['taxonomy'] ) ) {
1032
+ $insert_data[ $token ]['taxonomy'] = 0;
 
 
1033
  }
1034
+ $insert_data[ $token ]['taxonomy'] += $count;
 
 
 
 
 
1035
  }
1036
+ if ( isset( $insert_data[ $token ]['taxonomy_detail'] ) ) {
1037
+ $tax_detail = json_decode( $insert_data[ $token ]['taxonomy_detail'] );
1038
+ } else {
1039
  $tax_detail = array();
1040
  }
1041
+ if ( isset( $tax_detail[ $taxonomy ] ) ) {
1042
+ $tax_detail[ $taxonomy ] += $count;
1043
+ } else {
1044
+ $tax_detail[ $taxonomy ] = $count;
1045
  }
1046
+ $insert_data[ $token ]['taxonomy_detail'] = wp_json_encode( $tax_detail );
 
 
 
1047
  }
1048
  }
1049
  }
1050
  return $insert_data;
1051
  }
1052
 
1053
+ /**
1054
+ * Updates child posts when a parent post changes status.
1055
+ *
1056
+ * Called from 'transition_post_status' action hook when a post is edited, published,
1057
+ * or deleted. Will do the appropriate indexing action on the child posts and
1058
+ * attachments.
1059
+ *
1060
+ * @global object $wpdb The WP database interface.
1061
+ *
1062
+ * @author renaissancehack
1063
+ *
1064
+ * @param string $new_status The new status.
1065
+ * @param string $old_status The old status.
1066
+ * @param object $post The post object.
1067
+ */
1068
+ function relevanssi_update_child_posts( $new_status, $old_status, $post ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1069
  global $wpdb;
1070
 
1071
+ // Safety check, for WordPress Editorial Calendar incompatibility.
1072
+ if ( ! isset( $post ) || ! isset( $post->ID ) ) {
1073
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
1074
  }
1075
+
1076
+ /** Documented in lib/indexing.php. */
1077
+ $index_statuses = apply_filters( 'relevanssi_valid_status', array( 'publish', 'private', 'draft', 'pending', 'future' ) );
1078
+ if ( ( $new_status === $old_status ) || ( in_array( $new_status, $index_statuses, true ) && in_array( $old_status, $index_statuses, true ) ) || ( in_array( $post->post_type, array( 'attachment', 'revision' ), true ) ) ) {
1079
+ /**
1080
+ * Either:
1081
+ *
1082
+ * 1. New status equals old status.
1083
+ * 2. Both new and old status are in the list of stati to index.
1084
+ * 3. The post is an attachment or a revision.
1085
+ *
1086
+ * In any of these cases, do nothing.
1087
+ */
1088
+ return;
1089
  }
 
1090
 
1091
+ $post_types = get_option( 'relevanssi_index_post_types' );
1092
+ $args = array(
1093
+ 'post_parent' => $post->ID,
1094
+ 'post_type' => $post_types,
1095
+ );
1096
+ $child_posts = get_children( $args );
1097
+ if ( ! empty( $child_posts ) ) {
1098
+ if ( ! in_array( $new_status, $index_statuses, true ) ) {
1099
+ foreach ( $child_posts as $post ) {
1100
+ relevanssi_remove_doc( $post->ID );
1101
+ }
1102
+ } else {
1103
+ foreach ( $child_posts as $post ) {
1104
+ relevanssi_publish( $post->ID );
1105
+ }
1106
+ }
1107
+ }
1108
  }
1109
 
1110
+ /**
1111
+ * Indexes a published post.
1112
+ *
1113
+ * @param int $post_id The post ID.
1114
+ * @param boolean $bypass_global_post If tru, bypass the global $post object. Default false.
1115
+ */
1116
+ function relevanssi_publish( $post_id, $bypass_global_post = false ) {
1117
+ $post_status = get_post_status( $post_id );
1118
+ if ( 'auto-draft' === $post_status ) {
1119
+ return;
1120
+ }
1121
 
1122
  $custom_fields = relevanssi_get_custom_fields();
1123
+ relevanssi_index_doc( $post_id, true, $custom_fields, $bypass_global_post );
1124
  }
1125
 
1126
+ /**
1127
+ * Indexes a post after publishing or modification.
1128
+ *
1129
+ * Hooks on to 'wp_insert_post' action hook and triggers when wp_insert_post() is
1130
+ * used to add a post into the database. Doesn't use the global $post object, because
1131
+ * that doesn't have the correct post.
1132
+ *
1133
+ * @author Lumpysimon.
1134
+ *
1135
+ * @global object $wpdb The WP database interface.
1136
+ *
1137
+ * @param int $post_id The post ID.
1138
+ */
1139
+ function relevanssi_insert_edit( $post_id ) {
1140
  global $wpdb;
1141
 
1142
  $post_status = get_post_status( $post_id );
1143
+ if ( 'auto-draft' === $post_status ) {
1144
+ return;
1145
+ }
1146
 
1147
+ if ( 'inherit' === $post_status ) {
1148
+ // Get the post status from the parent post.
1149
+ $parent_id = wp_get_post_parent_id( $post_id );
1150
+ $post_status = get_post_status( $parent_id );
1151
+ }
1152
 
1153
+ $index_statuses = apply_filters( 'relevanssi_valid_status', array( 'publish', 'private', 'draft', 'future', 'pending' ) );
1154
+ if ( ! in_array( $post_status, $index_statuses, true ) ) {
1155
+ // The post isn't supposed to be indexed anymore, remove it from index.
1156
  relevanssi_remove_doc( $post_id );
1157
+ } else {
1158
+ $bypass_global_post = true;
1159
+ relevanssi_publish( $post_id, $bypass_global_post );
1160
  }
1161
+
1162
+ relevanssi_update_doc_count();
 
 
1163
  }
1164
 
1165
+ /**
1166
+ * Triggers comment indexing when a comment is edited.
1167
+ *
1168
+ * @author OdditY
1169
+ *
1170
+ * @param int $comment_id Comment id.
1171
+ */
1172
+ function relevanssi_comment_edit( $comment_id ) {
1173
+ $action = 'update';
1174
+ relevanssi_index_comment( $comment_id, $action );
1175
  }
1176
 
1177
+ /**
1178
+ * Triggers comment indexing when a comment is deleted.
1179
+ *
1180
+ * @author OdditY
1181
+ *
1182
+ * @param int $comment_id Comment ID.
1183
+ */
1184
+ function relevanssi_comment_remove( $comment_id ) {
1185
+ $action = 'remove';
1186
+ relevanssi_index_comment( $comment_id, $action );
1187
  }
1188
 
1189
+ /**
1190
+ * Updates comment indexing when comments are added, edited or deleted.
1191
+ *
1192
+ * @author OdditY
1193
+ *
1194
+ * @param int $comment_id Commend ID.
1195
+ * @param string $action What to do: 'add', 'update', 'remove'. Default 'add'.
1196
+ */
1197
+ function relevanssi_index_comment( $comment_id, $action = 'add' ) {
1198
  global $wpdb;
1199
+
1200
+ $comment_indexing_type = get_option( 'relevanssi_index_comments' );
1201
+ $no_pingbacks = false;
1202
+ $post_id = null;
1203
+
1204
+ switch ( $comment_indexing_type ) {
1205
+ case 'all':
1206
+ // All.
1207
  break;
1208
+ case 'normal':
1209
+ // Exclude trackbacks and pingbacks.
1210
+ $no_pingbacks = true;
1211
  break;
1212
  default:
1213
+ // No indexing.
1214
+ return;
1215
  }
1216
+ switch ( $action ) {
1217
+ case 'update':
1218
+ // Update, reindex the post.
1219
+ $comment = get_comment( $comment_id );
1220
+ if ( $no_pingbacks && ! empty( $comment->comment_type ) ) {
1221
+ break;
1222
+ }
1223
+ $post_id = $comment->comment_post_ID;
1224
  break;
1225
+ case 'remove':
1226
+ // Remove, empty the comment and reindex the post.
1227
+ $comment = get_comment( $comment_id );
1228
+ if ( $no_pingbacks && ! empty( $comment->comment_type ) ) {
1229
+ break;
1230
+ }
1231
+ $post_id = $comment->comment_post_ID;
1232
+ if ( $post_id ) {
1233
+ // Empty comment_content and reindex, then let WP delete the empty comment.
1234
+ $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->comments SET comment_content='' WHERE comment_ID=%d", $comment_id ) );
1235
  }
1236
  break;
1237
  default:
1238
+ // Add new comment.
1239
+ $comment = get_comment( $comment_id );
1240
+ if ( $no_pingbacks && ! empty( $comment->comment_type ) ) {
1241
+ break;
1242
+ }
1243
+ if ( 1 !== intval( $comment->comment_approved ) ) {
1244
+ // Comment isn't approved, do not index.
1245
+ break;
1246
+ }
1247
+ $post_id = $comment->comment_post_ID;
1248
  break;
1249
  }
1250
+ if ( $post_id ) {
1251
+ relevanssi_publish( $post_id );
1252
+ }
1253
  }
 
1254
 
1255
+ /**
1256
+ * Returns the comment text for a post.
1257
+ *
1258
+ * @global object $wpdb The WordPress database interface.
1259
+ *
1260
+ * @param int $post_id The post ID.
1261
+ *
1262
+ * @return string All the comment content as a string that has the comment author
1263
+ * and the comment text.
1264
+ */
1265
+ function relevanssi_get_comments( $post_id ) {
1266
  global $wpdb;
1267
 
1268
+ /**
1269
+ * If this filter returns true, the comments for the post are not indexed.
1270
+ *
1271
+ * @param boolean Return true to block the comment indexing. Default false.
1272
+ * @param int $post_id The post ID.
1273
+ */
1274
+ if ( apply_filters( 'relevanssi_index_comments_exclude', false, $post_id ) ) {
1275
+ return '';
1276
+ }
1277
 
1278
+ $comment_indexing = get_option( 'relevanssi_index_comments' );
1279
+ $comment_types = array( 'comment' );
1280
+ $comment_string = '';
1281
+
1282
+ if ( 'all' === $comment_indexing ) {
1283
+ $comment_types[] = 'pings';
1284
+ }
1285
+ if ( 'none' === $comment_indexing ) {
1286
+ return '';
 
 
 
 
 
1287
  }
1288
 
1289
+ $offset = 0;
1290
+ $limit = 20;
1291
 
1292
  while ( true ) {
1293
+ // Posts may have lots of comments. Do 20 at the time to avoid memory issues.
1294
+ $args = array(
1295
+ 'offset' => $offset,
1296
+ 'number' => $limit,
1297
+ 'type' => $comment_types,
1298
+ );
1299
+ $comments = get_approved_comments( $post_id, $args );
1300
+
1301
+ if ( count( $comments ) === 0 ) {
1302
+ break;
1303
  }
1304
+ foreach ( $comments as $comment ) {
1305
+ /**
1306
+ * Filters the comment content before indexing.
1307
+ *
1308
+ * @param string Comment author, a space, the comment content.
1309
+ * @param int The comment ID.
1310
+ */
1311
+ $comment_string .= apply_filters( 'relevanssi_comment_content_to_index', $comment->comment_author . ' ' . $comment->comment_content . ' ', $comment->comment_ID );
1312
+ }
1313
+ $offset += $limit;
1314
  }
1315
 
1316
  return $comment_string;
1317
  }
1318
 
1319
  /**
1320
+ * Indexes the human-readable value of "choice" options list from ACF.
1321
+ *
1322
+ * @author Droz Raphaël
1323
+ *
1324
+ * @param array $insert_data The insert data array.
1325
+ * @param int $post_id The post ID.
1326
+ * @param string $field_name Name of the field.
1327
+ * @param string $field_value The field value.
1328
  */
1329
+ function relevanssi_index_acf( &$insert_data, $post_id, $field_name, $field_value ) {
1330
+ if ( ! is_admin() ) {
1331
+ include_once ABSPATH . 'wp-admin/includes/plugin.php'; // Otherwise is_plugin_active() will cause a fatal error.
1332
+ }
1333
+ if ( ! function_exists( 'is_plugin_active' ) ) {
1334
+ return;
1335
+ }
1336
+ if ( ! is_plugin_active( 'advanced-custom-fields/acf.php' ) && ! is_plugin_active( 'advanced-custom-fields-pro/acf.php' ) ) {
1337
+ return;
1338
+ }
1339
+ if ( ! function_exists( 'get_field_object' ) ) {
1340
+ return; // ACF is active, but not loaded.
1341
+ }
1342
 
1343
+ $field_object = get_field_object( $field_name, $post_id );
1344
+ if ( ! isset( $field_object['choices'] ) ) {
1345
+ return; // Not a "select" field.
1346
+ }
1347
+ if ( is_array( $field_value ) ) {
1348
+ return; // Not handled (currently).
1349
+ }
1350
+ if ( ! isset( $field_object['choices'][ $field_value ] ) ) {
1351
+ return; // Value does not exist.
1352
+ }
1353
 
1354
+ $value = $field_object['choices'][ $field_value ];
1355
+ if ( $value ) {
1356
+ if ( ! isset( $insert_data[ $value ]['customfield'] ) ) {
1357
+ $insert_data[ $value ]['customfield'] = 0;
1358
+ }
1359
+ $insert_data[ $value ]['customfield']++;
1360
  }
1361
  }
1362
 
1363
  /**
1364
  * Truncates the Relevanssi index.
1365
+ *
1366
+ * @global object $wpdb The WordPress database interface.
1367
+ * @global array $relevanssi_variables The Relevanssi global variables array, used
1368
+ * for table names.
1369
+ *
1370
+ * @return boolean True on success, false on failure.
1371
  */
1372
  function relevanssi_truncate_index() {
1373
  global $wpdb, $relevanssi_variables;
1374
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
1375
+ return $wpdb->query( "TRUNCATE TABLE $relevanssi_table" ); // WPCS: unprepared SQL ok, Relevanssi table name.
1376
  }
1377
 
1378
+ /**
1379
+ * Remove post from the Relevanssi index.
1380
+ *
1381
+ * @global object $wpdb The WordPress database interface.
1382
+ * @global array $relevanssi_variables The Relevanssi global variables array, used
1383
+ * for table names.
1384
+ *
1385
+ * @param int $post_id The post ID.
1386
+ * @param boolean $keep_internal_links If true, keep internal link indexing (a
1387
+ * Premium feature). Default false.
1388
+ */
1389
+ function relevanssi_remove_doc( $post_id, $keep_internal_links = false ) {
1390
+ if ( function_exists( 'relevanssi_premium_remove_doc' ) ) {
1391
+ // Premium has a different method, because the index can include taxonomy
1392
+ // terms and user profiles.
1393
+ relevanssi_premium_remove_doc( $post_id, $keep_internal_links );
1394
+ } else {
1395
  global $wpdb, $relevanssi_variables;
1396
+
1397
+ $post_id = intval( $post_id );
1398
+ if ( empty( $post_id ) ) {
1399
+ // No post ID specified.
1400
+ return;
1401
+ }
1402
+
1403
+ $doc_count = get_option( 'relevanssi_doc_count' );
1404
+
1405
+ $rows_updated = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $relevanssi_variables['relevanssi_table'] . ' WHERE doc=%d', $post_id ) ); // WPCS: unprepared SQL ok, Relevanssi table name.
1406
+
1407
+ if ( $rows_updated && $rows_updated > 0 ) {
1408
+ update_option( 'relevanssi_doc_count', $doc_count - $rows_updated );
1409
+ }
1410
  }
1411
  }
lib/init.php CHANGED
@@ -1,112 +1,180 @@
1
  <?php
2
-
3
- add_action('admin_menu', 'relevanssi_menu');
4
- add_filter('the_posts', 'relevanssi_query', 99, 2);
5
- add_action('delete_post', 'relevanssi_delete');
6
- add_action('comment_post', 'relevanssi_comment_index'); //added by OdditY
7
- add_action('edit_comment', 'relevanssi_comment_edit'); //added by OdditY
8
- add_action('delete_comment', 'relevanssi_comment_remove'); //added by OdditY
9
- add_action('wp_insert_post', 'relevanssi_insert_edit', 99, 1 ); // added by lumpysimon
10
- // BEGIN added by renaissancehack
11
- // *_page and *_post hooks do not trigger on attachments
12
- add_action('delete_attachment', 'relevanssi_delete');
13
- add_action('add_attachment', 'relevanssi_publish', 12);
14
- add_action('edit_attachment', 'relevanssi_edit');
15
- // When a post status changes, check child posts that inherit their status from parent
16
- add_action('transition_post_status', 'relevanssi_update_child_posts',99,3);
17
- // END added by renaissancehack
18
- add_action('init', 'relevanssi_init');
19
- add_action('relevanssi_trim_logs', 'relevanssi_trim_logs');
20
- add_filter('relevanssi_hits_filter', 'relevanssi_wpml_filter');
21
- add_filter('relevanssi_modify_wp_query', 'relevanssi_polylang_filter');
22
- add_filter('posts_request', 'relevanssi_prevent_default_request', 10, 2 );
23
- add_filter('relevanssi_remove_punctuation', 'relevanssi_remove_punct');
24
- add_filter('relevanssi_post_ok', 'relevanssi_default_post_ok', 9, 2);
25
- add_filter('relevanssi_query_filter', 'relevanssi_limit_filter');
26
- add_filter('query_vars', 'relevanssi_query_vars');
27
- add_filter('relevanssi_indexing_values', 'relevanssi_update_doc_count', 98, 2);
28
- add_filter('relevanssi_pre_excerpt_content', 'relevanssi_remove_page_builder_shortcodes', 9);
29
- add_filter('rest_api_init', 'relevanssi_rest_api_disable');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  global $relevanssi_variables;
32
- register_activation_hook($relevanssi_variables['file'], 'relevanssi_install');
33
-
 
 
 
 
 
 
 
34
  function relevanssi_init() {
35
  global $pagenow, $relevanssi_variables, $wpdb;
36
- $plugin_dir = dirname(plugin_basename($relevanssi_variables['file']));
37
- load_plugin_textdomain('relevanssi', false, $plugin_dir . '/languages');
38
 
39
- isset($_POST['index']) ? $index = true : $index = false;
40
- if (get_option('relevanssi_indexed') != "done" && !$index) {
 
 
 
 
 
 
 
 
 
41
  function relevanssi_warning() {
42
- RELEVANSSI_PREMIUM ? $plugin = 'relevanssi-premium' : $plugin = 'relevanssi';
43
- echo "<div id='relevanssi-warning' class='update-nag'><p><strong>"
44
- . __('You do not have an index! Remember to build the index (click the "Build the index" button), otherwise searching won\'t work.', 'relevanssi')
45
- . "</strong></p></div>";
 
 
 
 
46
  }
47
- if ( 'options-general.php' == $pagenow and isset( $_GET['page'] ) and plugin_basename($relevanssi_variables['file']) == $_GET['page'] ) {
48
- add_action('admin_notices', 'relevanssi_warning');
49
- } else {
50
- // We always want to run this on init, if the index is finishd building.
51
- $relevanssi_table = $relevanssi_variables['relevanssi_table'];
52
- $D = $wpdb->get_var("SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi");
53
- update_option( 'relevanssi_doc_count', $D);
54
- }
55
- }
56
-
57
- if (!function_exists('mb_internal_encoding')) {
58
  function relevanssi_mb_warning() {
59
- echo "<div id='relevanssi-warning' class='error'><p><strong>"
60
- . __("Multibyte string functions are not available. Relevanssi may not work well without them. Please install (or ask your host to install) the mbstring extension.", 'relevanssi')
61
- . "</strong></p></div>";
 
62
  }
63
- if ( 'options-general.php' == $pagenow and isset( $_GET['page'] ) and plugin_basename($relevanssi_variables['file']) == $_GET['page'] )
64
- add_action('admin_notices', 'relevanssi_mb_warning');
65
  }
66
 
67
- if (get_option('relevanssi_highlight_docs', 'off') != 'off') {
68
- add_filter('the_content', 'relevanssi_highlight_in_docs', 11);
69
  }
70
- if (get_option('relevanssi_highlight_comments', 'off') != 'off') {
71
- add_filter('comment_text', 'relevanssi_highlight_in_docs', 11);
72
  }
73
 
74
- if (get_option('relevanssi_trim_logs') > 0) {
75
- if (! wp_next_scheduled ( 'relevanssi_trim_logs' )) {
76
- wp_schedule_event(time(), 'daily', 'relevanssi_trim_logs');
77
- }
78
- }
79
- else {
80
- if (wp_next_scheduled ( 'relevanssi_trim_logs' )) {
81
- wp_clear_scheduled_hook('relevanssi_trim_logs');
82
- }
83
  }
84
-
85
- return;
86
  }
87
 
 
 
 
 
 
88
  function relevanssi_menu() {
89
  global $relevanssi_variables;
90
- RELEVANSSI_PREMIUM ? $name = "Relevanssi Premium" : $name = "Relevanssi";
 
 
 
91
  $plugin_page = add_options_page(
92
  $name,
93
  $name,
94
- apply_filters('relevanssi_options_capability', 'manage_options'),
 
 
 
 
 
95
  $relevanssi_variables['file'],
96
  'relevanssi_options'
97
  );
98
  add_dashboard_page(
99
- __('User searches', 'relevanssi'),
100
- __('User searches', 'relevanssi'),
101
- apply_filters('relevanssi_user_searches_capability', 'edit_pages'),
 
 
 
 
 
102
  $relevanssi_variables['file'],
103
  'relevanssi_search_stats'
104
  );
105
  add_action( 'load-' . $plugin_page, 'relevanssi_admin_help' );
106
- if (function_exists('relevanssi_premium_plugin_page_actions')) relevanssi_premium_plugin_page_actions($plugin_page);
 
 
 
107
  }
108
 
109
- function relevanssi_query_vars($qv) {
 
 
 
 
 
 
 
 
 
 
110
  $qv[] = 'cats';
111
  $qv[] = 'tags';
112
  $qv[] = 'post_types';
@@ -116,35 +184,41 @@ function relevanssi_query_vars($qv) {
116
  return $qv;
117
  }
118
 
119
- function relevanssi_create_database_tables($relevanssi_db_version) {
 
 
 
 
 
 
 
120
  global $wpdb;
121
 
122
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
123
 
124
  $charset_collate_bin_column = '';
125
- $charset_collate = '';
126
 
127
- if (!empty($wpdb->charset)) {
128
- $charset_collate_bin_column = "CHARACTER SET $wpdb->charset";
129
- $charset_collate = "DEFAULT $charset_collate_bin_column";
130
  }
131
- if (strpos($wpdb->collate, "_") > 0) {
132
- $charset_collate_bin_column .= " COLLATE " . substr($wpdb->collate, 0, strpos($wpdb->collate, '_')) . "_bin";
133
- $charset_collate .= " COLLATE $wpdb->collate";
134
- } else {
135
- if ($wpdb->collate == '' && $wpdb->charset == "utf8") {
136
- $charset_collate_bin_column .= " COLLATE utf8_bin";
137
- }
138
- }
139
-
140
- $relevanssi_table = $wpdb->prefix . "relevanssi";
141
- $relevanssi_stopword_table = $wpdb->prefix . "relevanssi_stopwords";
142
- $relevanssi_log_table = $wpdb->prefix . "relevanssi_log";
143
-
144
- $current_db_version = get_option('relevanssi_db_version');
145
-
146
- if($current_db_version != $relevanssi_db_version) {
147
- $sql = "CREATE TABLE " . $relevanssi_table . " (doc bigint(20) NOT NULL DEFAULT '0',
148
  term varchar(50) NOT NULL DEFAULT '0',
149
  term_reverse varchar(50) NOT NULL DEFAULT '0',
150
  content mediumint(9) NOT NULL DEFAULT '0',
@@ -165,48 +239,56 @@ function relevanssi_create_database_tables($relevanssi_db_version) {
165
  item bigint(20) NOT NULL DEFAULT '0',
166
  UNIQUE KEY doctermitem (doc, term, item)) $charset_collate";
167
 
168
- dbDelta($sql);
169
 
170
- $sql = "SHOW INDEX FROM $relevanssi_table";
171
- $indices = $wpdb->get_results($sql);
172
 
173
- $terms_exists = false;
174
  $relevanssi_term_reverse_idx_exists = false;
175
- $docs_exists = false;
176
- $typeitem_exists = false;
177
- foreach ($indices as $index) {
178
- if ($index->Key_name == 'terms') $terms_exists = true;
179
- if ($index->Key_name == 'relevanssi_term_reverse_idx') $relevanssi_term_reverse_idx_exists = true;
180
- if ($index->Key_name == 'docs') $docs_exists = true;
181
- if ($index->Key_name == 'typeitem') $typeitem_exists = true;
 
 
 
 
 
 
 
 
182
  }
183
 
184
- if (!$terms_exists) {
185
  $sql = "CREATE INDEX terms ON $relevanssi_table (term(20))";
186
- $wpdb->query($sql);
187
  }
188
 
189
- if (!$relevanssi_term_reverse_idx_exists) {
190
  $sql = "CREATE INDEX relevanssi_term_reverse_idx ON $relevanssi_table (term_reverse(10))";
191
- $wpdb->query($sql);
192
  }
193
 
194
- if (!$docs_exists) {
195
  $sql = "CREATE INDEX docs ON $relevanssi_table (doc)";
196
- $wpdb->query($sql);
197
  }
198
 
199
- if (!$typeitem_exists) {
200
  $sql = "CREATE INDEX typeitem ON $relevanssi_table (type(190), item)";
201
- $wpdb->query($sql);
202
  }
203
 
204
- $sql = "CREATE TABLE " . $relevanssi_stopword_table . " (stopword varchar(50) $charset_collate_bin_column NOT NULL,
205
  UNIQUE KEY stopword (stopword)) $charset_collate;";
206
 
207
- dbDelta($sql);
208
 
209
- $sql = "CREATE TABLE " . $relevanssi_log_table . " (id bigint(9) NOT NULL AUTO_INCREMENT,
210
  query varchar(200) NOT NULL,
211
  hits mediumint(9) NOT NULL DEFAULT '0',
212
  time timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
@@ -214,43 +296,61 @@ function relevanssi_create_database_tables($relevanssi_db_version) {
214
  ip varchar(40) NOT NULL DEFAULT '',
215
  UNIQUE KEY id (id)) $charset_collate;";
216
 
217
- dbDelta($sql);
218
 
219
- $sql = "SHOW INDEX FROM $relevanssi_log_table";
220
- $indices = $wpdb->get_results($sql);
221
 
222
  $query_exists = false;
223
- foreach ($indices as $index) {
224
- if ($index->Key_name == 'query') $query_exists = true;
 
 
225
  }
226
-
227
- if (!$query_exists) {
228
  $sql = "CREATE INDEX query ON $relevanssi_log_table (query(190))";
229
- $wpdb->query($sql);
230
  }
231
 
232
- update_option('relevanssi_db_version', $relevanssi_db_version);
233
  }
234
 
235
- if ($wpdb->get_var("SELECT COUNT(*) FROM $relevanssi_stopword_table WHERE 1") < 1) {
236
  relevanssi_populate_stopwords();
237
  }
238
  }
239
 
240
- function relevanssi_action_links ($links) {
241
- $root = "relevanssi";
242
- if (RELEVANSSI_PREMIUM) $root = "relevanssi-premium";
 
 
 
 
 
 
 
 
 
 
 
243
  $relevanssi_links = array(
244
- '<a href="' . admin_url( 'options-general.php?page=' . $root . '/relevanssi.php' ) . '">' . __('Settings', 'relevanssi') . '</a>',
245
- );
246
- if (!RELEVANSSI_PREMIUM) {
247
- $relevanssi_links[] = '<a href="https://www.relevanssi.com/buy-premium/">' . __('Go Premium!', 'relevanssi') . '</a>';
248
  }
249
- return array_merge($links, $relevanssi_links);
250
  }
251
 
252
- /** Disable Relevanssi in REST API searches */
 
 
 
 
 
253
  function relevanssi_rest_api_disable() {
254
- remove_filter('posts_request', 'relevanssi_prevent_default_request');
255
- remove_filter('the_posts', 'relevanssi_query', 99);
256
- }
1
  <?php
2
+ /**
3
+ * /lib/init.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
+
11
+ // Setup.
12
+ add_action( 'init', 'relevanssi_init' );
13
+ add_filter( 'query_vars', 'relevanssi_query_vars' );
14
+ add_action( 'admin_menu', 'relevanssi_menu' );
15
+ add_filter( 'rest_api_init', 'relevanssi_rest_api_disable' );
16
+ add_action( 'switch_blog', 'relevanssi_switch_blog', 1, 2 );
17
+ add_action( 'admin_enqueue_scripts', 'relevanssi_add_admin_scripts' );
18
+
19
+ // Taking over the search.
20
+ add_filter( 'the_posts', 'relevanssi_query', 99, 2 );
21
+ add_filter( 'posts_request', 'relevanssi_prevent_default_request', 10, 2 );
22
+
23
+ // Multilingual plugin support.
24
+ add_filter( 'relevanssi_hits_filter', 'relevanssi_wpml_filter' );
25
+ add_filter( 'relevanssi_modify_wp_query', 'relevanssi_polylang_filter' );
26
+
27
+ // Post indexing.
28
+ add_action( 'wp_insert_post', 'relevanssi_insert_edit', 99, 1 );
29
+ add_action( 'delete_post', 'relevanssi_remove_doc' );
30
+
31
+ // Comment indexing.
32
+ add_action( 'comment_post', 'relevanssi_index_comment' );
33
+ add_action( 'edit_comment', 'relevanssi_comment_edit' );
34
+ add_action( 'delete_comment', 'relevanssi_comment_remove' );
35
+
36
+ // Attachment indexing.
37
+ add_action( 'delete_attachment', 'relevanssi_remove_doc' );
38
+ add_action( 'add_attachment', 'relevanssi_publish', 12 );
39
+ add_action( 'edit_attachment', 'relevanssi_insert_edit' );
40
+
41
+ // When a post status changes, check child posts that inherit their status from parent.
42
+ add_action( 'transition_post_status', 'relevanssi_update_child_posts', 99, 3 );
43
+
44
+ // Relevanssi features.
45
+ add_filter( 'relevanssi_remove_punctuation', 'relevanssi_remove_punct' );
46
+ add_filter( 'relevanssi_post_ok', 'relevanssi_default_post_ok', 9, 2 );
47
+ add_filter( 'relevanssi_query_filter', 'relevanssi_limit_filter' );
48
+ add_action( 'relevanssi_trim_logs', 'relevanssi_trim_logs' );
49
+
50
+ // Plugin and theme compatibility.
51
+ add_filter( 'relevanssi_pre_excerpt_content', 'relevanssi_remove_page_builder_shortcodes', 9 );
52
+ add_filter( 'relevanssi_search_ok', 'relevanssi_acf_relationship_fields' );
53
+
54
+ // Permalink handling.
55
+ add_filter( 'the_permalink', 'relevanssi_permalink' );
56
+ add_filter( 'post_link', 'relevanssi_permalink' );
57
+ add_filter( 'relevanssi_permalink', 'relevanssi_permalink' );
58
 
59
  global $relevanssi_variables;
60
+ register_activation_hook( $relevanssi_variables['file'], 'relevanssi_install' );
61
+
62
+ /**
63
+ * Initiates Relevanssi.
64
+ *
65
+ * @global string $pagenow Current admin page.
66
+ * @global array $relevanssi_variables The global Relevanssi variables array.
67
+ * @global object $wpdb The WP database interface.
68
+ */
69
  function relevanssi_init() {
70
  global $pagenow, $relevanssi_variables, $wpdb;
 
 
71
 
72
+ $plugin_dir = dirname( plugin_basename( $relevanssi_variables['file'] ) );
73
+ load_plugin_textdomain( 'relevanssi', false, $plugin_dir . '/languages' );
74
+ $page = '';
75
+ if ( isset( $_GET['page'] ) ) {
76
+ $page = $_GET['page']; // WPCS: CSRF ok; this value is read-only.
77
+ }
78
+
79
+ if ( 'done' !== get_option( 'relevanssi_indexed' ) ) {
80
+ /**
81
+ * Prints out the "You do not have an index!" warning.
82
+ */
83
  function relevanssi_warning() {
84
+ $plugin = 'relevanssi';
85
+ if ( RELEVANSSI_PREMIUM ) {
86
+ $plugin = 'relevanssi-premium';
87
+ }
88
+ printf( "<div id='relevanssi-warning' class='update-nag'><p><strong>%s</strong></p></div>", esc_html__( 'You do not have an index! Remember to build the index (click the "Build the index" button), otherwise searching won\'t work.', 'relevanssi' ) );
89
+ }
90
+ if ( 'options-general.php' === $pagenow && plugin_basename( $relevanssi_variables['file'] ) === $page ) {
91
+ add_action( 'admin_notices', 'relevanssi_warning' );
92
  }
93
+ }
94
+
95
+ if ( ! function_exists( 'mb_internal_encoding' ) ) {
96
+ /**
97
+ * Prints out the "Multibyte string functions are not available" warning.
98
+ */
 
 
 
 
 
99
  function relevanssi_mb_warning() {
100
+ printf( "<div id='relevanssi-warning' class='error'><p><strong>%s</strong></p></div>", esc_html__( 'Multibyte string functions are not available. Relevanssi may not work well without them. Please install (or ask your host to install) the mbstring extension.', 'relevanssi' ) );
101
+ }
102
+ if ( 'options-general.php' === $pagenow && plugin_basename( $relevanssi_variables['file'] ) === $page ) {
103
+ add_action( 'admin_notices', 'relevanssi_mb_warning' );
104
  }
 
 
105
  }
106
 
107
+ if ( 'off' !== get_option( 'relevanssi_highlight_docs', 'off' ) ) {
108
+ add_filter( 'the_content', 'relevanssi_highlight_in_docs', 11 );
109
  }
110
+ if ( 'off' !== get_option( 'relevanssi_highlight_comments', 'off' ) ) {
111
+ add_filter( 'comment_text', 'relevanssi_highlight_in_docs', 11 );
112
  }
113
 
114
+ if ( get_option( 'relevanssi_trim_logs' ) > 0 ) {
115
+ if ( ! wp_next_scheduled( 'relevanssi_trim_logs' ) ) {
116
+ wp_schedule_event( time(), 'daily', 'relevanssi_trim_logs' );
117
+ }
118
+ } else {
119
+ if ( wp_next_scheduled( 'relevanssi_trim_logs' ) ) {
120
+ wp_clear_scheduled_hook( 'relevanssi_trim_logs' );
121
+ }
 
122
  }
 
 
123
  }
124
 
125
+ /**
126
+ * Adds the Relevanssi menu items.
127
+ *
128
+ * @global array $relevanssi_variables The global Relevanssi variables array.
129
+ */
130
  function relevanssi_menu() {
131
  global $relevanssi_variables;
132
+ $name = 'Relevanssi';
133
+ if ( RELEVANSSI_PREMIUM ) {
134
+ $name = 'Relevanssi Premium';
135
+ }
136
  $plugin_page = add_options_page(
137
  $name,
138
  $name,
139
+ /**
140
+ * Filters the capability required to access Relevanssi options.
141
+ *
142
+ * @param string The capability required. Default 'manage_options'.
143
+ */
144
+ apply_filters( 'relevanssi_options_capability', 'manage_options' ),
145
  $relevanssi_variables['file'],
146
  'relevanssi_options'
147
  );
148
  add_dashboard_page(
149
+ __( 'User searches', 'relevanssi' ),
150
+ __( 'User searches', 'relevanssi' ),
151
+ /**
152
+ * Filters the capability required to access Relevanssi user searches page.
153
+ *
154
+ * @param string The capability required. Default 'edit_pages'.
155
+ */
156
+ apply_filters( 'relevanssi_user_searches_capability', 'edit_pages' ),
157
  $relevanssi_variables['file'],
158
  'relevanssi_search_stats'
159
  );
160
  add_action( 'load-' . $plugin_page, 'relevanssi_admin_help' );
161
+ if ( function_exists( 'relevanssi_premium_plugin_page_actions' ) ) {
162
+ // Loads contextual help and JS for Premium version.
163
+ relevanssi_premium_plugin_page_actions( $plugin_page );
164
+ }
165
  }
166
 
167
+ /**
168
+ * Introduces the Relevanssi query variables.
169
+ *
170
+ * Adds the Relevanssi query variables (cats, tags, post_types, by_date, and
171
+ * highlight) to the WordPress whitelist of accepted query variables.
172
+ *
173
+ * @param array $qv The query variable list.
174
+ *
175
+ * @return array The query variables.
176
+ */
177
+ function relevanssi_query_vars( $qv ) {
178
  $qv[] = 'cats';
179
  $qv[] = 'tags';
180
  $qv[] = 'post_types';
184
  return $qv;
185
  }
186
 
187
+ /**
188
+ * Creates the Relevanssi database tables.
189
+ *
190
+ * @global object $wpdb The WordPress database interface.
191
+ *
192
+ * @param int $relevanssi_db_version The Relevanssi database version number.
193
+ */
194
+ function relevanssi_create_database_tables( $relevanssi_db_version ) {
195
  global $wpdb;
196
 
197
+ require_once ABSPATH . 'wp-admin/includes/upgrade.php';
198
 
199
  $charset_collate_bin_column = '';
200
+ $charset_collate = '';
201
 
202
+ if ( ! empty( $wpdb->charset ) ) {
203
+ $charset_collate_bin_column = "CHARACTER SET $wpdb->charset";
204
+ $charset_collate = "DEFAULT $charset_collate_bin_column";
205
  }
206
+ if ( strpos( $wpdb->collate, '_' ) > 0 ) {
207
+ $charset_collate_bin_column .= ' COLLATE ' . substr( $wpdb->collate, 0, strpos( $wpdb->collate, '_' ) ) . '_bin';
208
+ $charset_collate .= " COLLATE $wpdb->collate";
209
+ } else {
210
+ if ( '' === $wpdb->collate && 'utf8' === $wpdb->charset ) {
211
+ $charset_collate_bin_column .= ' COLLATE utf8_bin';
212
+ }
213
+ }
214
+
215
+ $relevanssi_table = $wpdb->prefix . 'relevanssi';
216
+ $relevanssi_stopword_table = $wpdb->prefix . 'relevanssi_stopwords';
217
+ $relevanssi_log_table = $wpdb->prefix . 'relevanssi_log';
218
+ $current_db_version = get_option( 'relevanssi_db_version' );
219
+
220
+ if ( $current_db_version !== $relevanssi_db_version ) {
221
+ $sql = 'CREATE TABLE ' . $relevanssi_table . " (doc bigint(20) NOT NULL DEFAULT '0',
 
222
  term varchar(50) NOT NULL DEFAULT '0',
223
  term_reverse varchar(50) NOT NULL DEFAULT '0',
224
  content mediumint(9) NOT NULL DEFAULT '0',
239
  item bigint(20) NOT NULL DEFAULT '0',
240
  UNIQUE KEY doctermitem (doc, term, item)) $charset_collate";
241
 
242
+ dbDelta( $sql );
243
 
244
+ $sql = "SHOW INDEX FROM $relevanssi_table";
245
+ $indices = $wpdb->get_results( $sql ); // WPCS: unprepared SQL ok.
246
 
247
+ $terms_exists = false;
248
  $relevanssi_term_reverse_idx_exists = false;
249
+ $docs_exists = false;
250
+ $typeitem_exists = false;
251
+ foreach ( $indices as $index ) {
252
+ if ( 'terms' === $index->Key_name ) {
253
+ $terms_exists = true;
254
+ }
255
+ if ( 'relevanssi_term_reverse_idx' === $index->Key_name ) {
256
+ $relevanssi_term_reverse_idx_exists = true;
257
+ }
258
+ if ( 'docs' === $index->Key_name ) {
259
+ $docs_exists = true;
260
+ }
261
+ if ( 'typeitem' === $index->Key_name ) {
262
+ $typeitem_exists = true;
263
+ }
264
  }
265
 
266
+ if ( ! $terms_exists ) {
267
  $sql = "CREATE INDEX terms ON $relevanssi_table (term(20))";
268
+ $wpdb->query( $sql ); // WPCS: unprepared SQL ok.
269
  }
270
 
271
+ if ( ! $relevanssi_term_reverse_idx_exists ) {
272
  $sql = "CREATE INDEX relevanssi_term_reverse_idx ON $relevanssi_table (term_reverse(10))";
273
+ $wpdb->query( $sql ); // WPCS: unprepared SQL ok.
274
  }
275
 
276
+ if ( ! $docs_exists ) {
277
  $sql = "CREATE INDEX docs ON $relevanssi_table (doc)";
278
+ $wpdb->query( $sql ); // WPCS: unprepared SQL ok.
279
  }
280
 
281
+ if ( ! $typeitem_exists ) {
282
  $sql = "CREATE INDEX typeitem ON $relevanssi_table (type(190), item)";
283
+ $wpdb->query( $sql ); // WPCS: unprepared SQL ok.
284
  }
285
 
286
+ $sql = 'CREATE TABLE ' . $relevanssi_stopword_table . " (stopword varchar(50) $charset_collate_bin_column NOT NULL,
287
  UNIQUE KEY stopword (stopword)) $charset_collate;";
288
 
289
+ dbDelta( $sql );
290
 
291
+ $sql = 'CREATE TABLE ' . $relevanssi_log_table . " (id bigint(9) NOT NULL AUTO_INCREMENT,
292
  query varchar(200) NOT NULL,
293
  hits mediumint(9) NOT NULL DEFAULT '0',
294
  time timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
296
  ip varchar(40) NOT NULL DEFAULT '',
297
  UNIQUE KEY id (id)) $charset_collate;";
298
 
299
+ dbDelta( $sql );
300
 
301
+ $sql = "SHOW INDEX FROM $relevanssi_log_table";
302
+ $indices = $wpdb->get_results( $sql ); // WPCS: unprepared SQL ok.
303
 
304
  $query_exists = false;
305
+ foreach ( $indices as $index ) {
306
+ if ( 'query' === $index->Key_name ) {
307
+ $query_exists = true;
308
+ }
309
  }
310
+
311
+ if ( ! $query_exists ) {
312
  $sql = "CREATE INDEX query ON $relevanssi_log_table (query(190))";
313
+ $wpdb->query( $sql ); // WPCS: unprepared SQL ok.
314
  }
315
 
316
+ update_option( 'relevanssi_db_version', $relevanssi_db_version );
317
  }
318
 
319
+ if ( $wpdb->get_var( "SELECT COUNT(*) FROM $relevanssi_stopword_table WHERE 1" ) < 1 ) { // WPCS: unprepared SQL ok.
320
  relevanssi_populate_stopwords();
321
  }
322
  }
323
 
324
+ /**
325
+ * Prints out the action links on the Plugins page.
326
+ *
327
+ * Hooked on to the 'plugin_action_links_' filter hook.
328
+ *
329
+ * @param array $links Action links for Relevanssi.
330
+ *
331
+ * @return array Updated action links.
332
+ */
333
+ function relevanssi_action_links( $links ) {
334
+ $root = 'relevanssi';
335
+ if ( RELEVANSSI_PREMIUM ) {
336
+ $root = 'relevanssi-premium';
337
+ }
338
  $relevanssi_links = array(
339
+ '<a href="' . admin_url( 'options-general.php?page=' . $root . '/relevanssi.php' ) . '">' . __( 'Settings', 'relevanssi' ) . '</a>',
340
+ );
341
+ if ( ! RELEVANSSI_PREMIUM ) {
342
+ $relevanssi_links[] = '<a href="https://www.relevanssi.com/buy-premium/">' . __( 'Go Premium!', 'relevanssi' ) . '</a>';
343
  }
344
+ return array_merge( $links, $relevanssi_links );
345
  }
346
 
347
+ /**
348
+ * Disables Relevanssi in REST API searches.
349
+ *
350
+ * Relevanssi doesn't work in the REST API context, so disable and allow the
351
+ * default search to work.
352
+ */
353
  function relevanssi_rest_api_disable() {
354
+ remove_filter( 'posts_request', 'relevanssi_prevent_default_request' );
355
+ remove_filter( 'the_posts', 'relevanssi_query', 99 );
356
+ }
lib/install.php CHANGED
@@ -1,94 +1,138 @@
1
  <?php
 
 
 
 
 
 
 
 
2
 
3
- function relevanssi_new_blog($blog_id, $user_id, $domain, $path, $site_id, $meta ) {
 
 
 
 
 
 
 
 
4
  global $wpdb;
5
 
6
- if (is_plugin_active_for_network('relevanssi-premium/relevanssi.php') || is_plugin_active_for_network('relevanssi/relevanssi.php')) {
7
- switch_to_blog($blog_id);
8
  _relevanssi_install();
9
  restore_current_blog();
10
  }
11
  }
12
 
13
- function relevanssi_install($network_wide = false) {
14
- global $wpdb;
15
-
16
- if ($network_wide) {
17
- $blogids = $wpdb->get_col($wpdb->prepare("
18
- SELECT blog_id
19
- FROM $wpdb->blogs
20
- WHERE site_id = %d
21
- AND deleted = 0
22
- AND spam = 0
23
- ", $wpdb->siteid));
 
 
 
 
 
 
24
 
25
- foreach ($blogids as $blog_id) {
26
- switch_to_blog($blog_id);
27
  _relevanssi_install();
28
  restore_current_blog();
29
  }
30
-
31
  } else {
32
  _relevanssi_install();
33
  }
34
  }
35
 
 
 
 
 
 
 
 
 
36
  function _relevanssi_install() {
37
  global $relevanssi_variables;
38
 
39
- add_option('relevanssi_content_boost', $relevanssi_variables['content_boost_default']);
40
- add_option('relevanssi_title_boost', $relevanssi_variables['title_boost_default']);
41
- add_option('relevanssi_comment_boost', $relevanssi_variables['comment_boost_default']);
42
- add_option('relevanssi_index_post_types', array('post', 'page'));
43
- add_option('relevanssi_admin_search', 'off');
44
- add_option('relevanssi_highlight', 'strong');
45
- add_option('relevanssi_txt_col', '#ff0000');
46
- add_option('relevanssi_bg_col', '#ffaf75');
47
- add_option('relevanssi_css', 'text-decoration: underline; text-color: #ff0000');
48
- add_option('relevanssi_class', 'relevanssi-query-term');
49
- add_option('relevanssi_excerpts', 'on');
50
- add_option('relevanssi_excerpt_length', '30');
51
- add_option('relevanssi_excerpt_type', 'words');
52
- add_option('relevanssi_excerpt_allowable_tags', '');
53
- add_option('relevanssi_excerpt_custom_fields', 'off');
54
- add_option('relevanssi_log_queries', 'off');
55
- add_option('relevanssi_log_queries_with_ip', 'off');
56
- add_option('relevanssi_cat', '0');
57
- add_option('relevanssi_excat', '0');
58
- add_option('relevanssi_extag', '0');
59
- add_option('relevanssi_index_fields', '');
60
- add_option('relevanssi_exclude_posts', ''); //added by OdditY
61
- add_option('relevanssi_hilite_title', ''); //added by OdditY
62
- add_option('relevanssi_highlight_docs', 'off');
63
- add_option('relevanssi_highlight_docs_external', 'off');
64
- add_option('relevanssi_highlight_comments', 'off');
65
- add_option('relevanssi_index_comments', 'none'); //added by OdditY
66
- add_option('relevanssi_show_matches', '');
67
- 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%)');
68
- add_option('relevanssi_fuzzy', 'sometimes');
69
- add_option('relevanssi_indexed', '');
70
- add_option('relevanssi_expand_shortcodes', 'on');
71
- add_option('relevanssi_index_author', '');
72
- add_option('relevanssi_implicit_operator', 'OR');
73
- add_option('relevanssi_omit_from_logs', '');
74
- add_option('relevanssi_synonyms', '');
75
- add_option('relevanssi_index_excerpt', 'off');
76
- add_option('relevanssi_index_limit', '500');
77
- add_option('relevanssi_disable_or_fallback', 'off');
78
- add_option('relevanssi_respect_exclude', 'on');
79
- add_option('relevanssi_min_word_length', '3');
80
- add_option('relevanssi_throttle', 'on');
81
- add_option('relevanssi_throttle_limit', '500');
82
- add_option('relevanssi_db_version', '0');
83
- add_option('relevanssi_wpml_only_current', 'on');
84
- add_option('relevanssi_polylang_all_languages', 'off');
85
- add_option('relevanssi_word_boundaries', 'on');
86
- add_option('relevanssi_default_orderby', 'relevance');
87
- add_option('relevanssi_punctuation', array('quotes' => 'replace', 'hyphens' => 'replace', 'ampersands' => 'replace'));
 
 
 
 
 
 
88
 
89
- if (function_exists('relevanssi_premium_install')) relevanssi_premium_install();
 
 
 
90
 
91
- do_action('relevanssi_update_options');
 
 
 
 
 
 
92
 
93
- relevanssi_create_database_tables($relevanssi_variables['database_version']);
94
- }
1
  <?php
2
+ /**
3
+ * /lib/install.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
 
11
+ /**
12
+ * Installs Relevanssi on a new plugin if Relevanssi is network active.
13
+ *
14
+ * Hooks on to 'wpmu_new_blog' action hook and runs '_relevanssi_install' on the
15
+ * new blog.
16
+ *
17
+ * @param int $blog_id The blog ID.
18
+ */
19
+ function relevanssi_new_blog( $blog_id ) {
20
  global $wpdb;
21
 
22
+ if ( is_plugin_active_for_network( 'relevanssi-premium/relevanssi.php' ) || is_plugin_active_for_network( 'relevanssi/relevanssi.php' ) ) {
23
+ switch_to_blog( $blog_id );
24
  _relevanssi_install();
25
  restore_current_blog();
26
  }
27
  }
28
 
29
+ /**
30
+ * Runs _relevanssi_install() on one blog or for the whole network.
31
+ *
32
+ * If Relevanssi is network active, this installs Relevanssi on all blogs in the
33
+ * network, running the _relevanssi_install() function.
34
+ *
35
+ * @param boolean $network_wide If true, install on all sites. Default false.
36
+ */
37
+ function relevanssi_install( $network_wide = false ) {
38
+ if ( $network_wide ) {
39
+ $args = array(
40
+ 'spam' => 0,
41
+ 'deleted' => 0,
42
+ 'archived' => 0,
43
+ 'fields' => 'ids',
44
+ );
45
+ $blog_ids = get_sites( $args );
46
 
47
+ foreach ( $blog_ids as $blog_id ) {
48
+ switch_to_blog( $blog_id );
49
  _relevanssi_install();
50
  restore_current_blog();
51
  }
 
52
  } else {
53
  _relevanssi_install();
54
  }
55
  }
56
 
57
+ /**
58
+ * Installs Relevanssi on the blog.
59
+ *
60
+ * Adds Relevanssi options and sets their default values and generates the
61
+ * database tables.
62
+ *
63
+ * @global array $relevanssi_variables The global Relevanssi variables array.
64
+ */
65
  function _relevanssi_install() {
66
  global $relevanssi_variables;
67
 
68
+ add_option( 'relevanssi_admin_search', 'off' );
69
+ add_option( 'relevanssi_bg_col', '#ffaf75' );
70
+ add_option( 'relevanssi_cat', '0' );
71
+ add_option( 'relevanssi_class', 'relevanssi-query-term' );
72
+ add_option( 'relevanssi_comment_boost', $relevanssi_variables['comment_boost_default'] );
73
+ add_option( 'relevanssi_content_boost', $relevanssi_variables['content_boost_default'] );
74
+ add_option( 'relevanssi_css', 'text-decoration: underline; text-color: #ff0000' );
75
+ add_option( 'relevanssi_db_version', '0' );
76
+ add_option( 'relevanssi_default_orderby', 'relevance' );
77
+ add_option( 'relevanssi_disable_or_fallback', 'off' );
78
+ add_option( 'relevanssi_exact_match_bonus', 'on' );
79
+ add_option( 'relevanssi_excat', '0' );
80
+ add_option( 'relevanssi_excerpt_allowable_tags', '' );
81
+ add_option( 'relevanssi_excerpt_custom_fields', 'off' );
82
+ add_option( 'relevanssi_excerpt_length', '30' );
83
+ add_option( 'relevanssi_excerpt_type', 'words' );
84
+ add_option( 'relevanssi_excerpts', 'on' );
85
+ add_option( 'relevanssi_exclude_posts', '' );
86
+ add_option( 'relevanssi_expand_shortcodes', 'on' );
87
+ add_option( 'relevanssi_extag', '0' );
88
+ add_option( 'relevanssi_fuzzy', 'sometimes' );
89
+ add_option( 'relevanssi_highlight', 'strong' );
90
+ add_option( 'relevanssi_highlight_comments', 'off' );
91
+ add_option( 'relevanssi_highlight_docs', 'off' );
92
+ add_option( 'relevanssi_highlight_docs_external', 'off' );
93
+ add_option( 'relevanssi_hilite_title', '' );
94
+ add_option( 'relevanssi_implicit_operator', 'OR' );
95
+ add_option( 'relevanssi_index_author', '' );
96
+ add_option( 'relevanssi_index_comments', 'none' );
97
+ add_option( 'relevanssi_index_excerpt', 'off' );
98
+ add_option( 'relevanssi_index_fields', '' );
99
+ add_option( 'relevanssi_index_limit', '500' );
100
+ add_option( 'relevanssi_index_post_types', array( 'post', 'page' ) );
101
+ add_option( 'relevanssi_index_taxonomies_list', array() );
102
+ add_option( 'relevanssi_indexed', '' );
103
+ add_option( 'relevanssi_log_queries', 'off' );
104
+ add_option( 'relevanssi_log_queries_with_ip', 'off' );
105
+ add_option( 'relevanssi_min_word_length', '3' );
106
+ add_option( 'relevanssi_omit_from_logs', '' );
107
+ add_option( 'relevanssi_polylang_all_languages', 'off' );
108
+ add_option( 'relevanssi_punctuation', array(
109
+ 'quotes' => 'replace',
110
+ 'hyphens' => 'replace',
111
+ 'ampersands' => 'replace',
112
+ ));
113
+ add_option( 'relevanssi_respect_exclude', 'on' );
114
+ add_option( 'relevanssi_show_matches', '' );
115
+ 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%)' );
116
+ add_option( 'relevanssi_synonyms', '' );
117
+ add_option( 'relevanssi_throttle', 'on' );
118
+ add_option( 'relevanssi_throttle_limit', '500' );
119
+ add_option( 'relevanssi_title_boost', $relevanssi_variables['title_boost_default'] );
120
+ add_option( 'relevanssi_txt_col', '#ff0000' );
121
+ add_option( 'relevanssi_word_boundaries', 'on' );
122
+ add_option( 'relevanssi_wpml_only_current', 'on' );
123
 
124
+ if ( function_exists( 'relevanssi_premium_install' ) ) {
125
+ // Do some Relevanssi Premium additions.
126
+ relevanssi_premium_install();
127
+ }
128
 
129
+ /**
130
+ * Runs after Relevanssi options are added in the installation process.
131
+ *
132
+ * This action hook can be used to adjust the options to set your own default
133
+ * settings, for example.
134
+ */
135
+ do_action( 'relevanssi_update_options' );
136
 
137
+ relevanssi_create_database_tables( $relevanssi_variables['database_version'] );
138
+ }
lib/interface.php CHANGED
@@ -1,499 +1,659 @@
1
  <?php
2
-
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  function relevanssi_options() {
4
  global $relevanssi_variables;
5
- if (RELEVANSSI_PREMIUM) {
6
- $options_txt = __('Relevanssi Premium Search Options', 'relevanssi');
7
- }
8
- else {
9
- $options_txt = __('Relevanssi Search Options', 'relevanssi');
10
  }
11
 
12
- printf("<div class='wrap'><h2>%s</h2>", $options_txt);
13
- if (!empty($_POST)) {
14
- if (isset($_REQUEST['submit'])) {
15
- check_admin_referer(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
16
- update_relevanssi_options();
17
- }
18
-
19
- if (isset($_REQUEST['index'])) {
20
- check_admin_referer(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
21
- update_relevanssi_options();
22
- relevanssi_build_index();
23
- }
24
-
25
- if (isset($_REQUEST['index_extend'])) {
26
- check_admin_referer(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
27
  update_relevanssi_options();
28
- relevanssi_build_index(true);
29
  }
30
 
31
- if (isset($_REQUEST['import_options'])) {
32
- if (function_exists('relevanssi_import_options')) {
33
- check_admin_referer(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
34
  $options = $_REQUEST['relevanssi_settings'];
35
- relevanssi_import_options($options);
36
  }
37
  }
38
 
39
- if (isset($_REQUEST['search'])) {
40
- relevanssi_search($_REQUEST['q']);
41
  }
42
 
43
- if (isset($_REQUEST['dowhat'])) {
44
- if ("add_stopword" === $_REQUEST['dowhat']) {
45
- if (isset($_REQUEST['term'])) {
46
- check_admin_referer(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
47
- relevanssi_add_stopword($_REQUEST['term']);
48
  }
49
  }
50
  }
51
 
52
- if (isset($_REQUEST['addstopword'])) {
53
- check_admin_referer(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
54
- relevanssi_add_stopword($_REQUEST['addstopword']);
55
  }
56
 
57
- if (isset($_REQUEST['removestopword'])) {
58
- check_admin_referer(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
59
- relevanssi_remove_stopword($_REQUEST['removestopword']);
60
  }
61
 
62
- if (isset($_REQUEST['removeallstopwords'])) {
63
- check_admin_referer(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
64
  relevanssi_remove_all_stopwords();
65
  }
66
  }
67
- relevanssi_options_form();
68
 
69
- echo "<div style='clear:both'></div>";
70
 
71
- echo "</div>";
72
  }
73
 
 
 
 
74
  function relevanssi_search_stats() {
75
  $relevanssi_hide_branding = get_option( 'relevanssi_hide_branding' );
76
 
77
- if ( 'on' === $relevanssi_hide_branding )
78
- $options_txt = __('User Searches', 'relevanssi');
79
- else
80
- $options_txt = __('Relevanssi User Searches', 'relevanssi');
 
81
 
82
- if (isset($_REQUEST['relevanssi_reset']) and current_user_can('manage_options')) {
83
- check_admin_referer('relevanssi_reset_logs', '_relresnonce');
84
- if (isset($_REQUEST['relevanssi_reset_code'])) {
85
- if ($_REQUEST['relevanssi_reset_code'] === 'reset') {
86
  $verbose = true;
87
- relevanssi_truncate_logs($verbose);
88
  }
89
  }
90
  }
91
 
92
- wp_enqueue_style('dashboard');
93
- wp_print_styles('dashboard');
94
- wp_enqueue_script('dashboard');
95
- wp_print_scripts('dashboard');
96
 
97
- printf("<div class='wrap'><h2>%s</h2>", $options_txt);
98
 
99
- //echo '<div class="postbox-container">';
100
-
101
- if ('on' === get_option('relevanssi_log_queries')) {
102
  relevanssi_query_log();
 
 
103
  }
104
- else {
105
- echo "<p>" . __('Enable query logging to see stats here.', 'relevanssi') . "</p>";
106
- }
107
-
108
- //echo "</div>";
109
  }
110
 
111
- function relevanssi_truncate_logs($verbose = true) {
 
 
 
 
 
 
 
 
 
 
112
  global $wpdb, $relevanssi_variables;
113
 
114
- $query = "TRUNCATE " . $relevanssi_variables['log_table'];
115
- $result = $wpdb->query($query);
116
 
117
- if ($verbose) {
118
- if ($result !== false) {
119
- echo "<div id='relevanssi-warning' class='updated fade'>" . __('Logs clear!', 'relevanssi') . "</div>";
120
- }
121
- else {
122
- echo "<div id='relevanssi-warning' class='updated fade'>" . __('Clearing the logs failed.', 'relevanssi') . "</div>";
123
  }
124
  }
125
 
126
  return $result;
127
  }
128
 
 
 
 
 
 
 
129
  function update_relevanssi_options() {
130
- if (isset($_REQUEST['relevanssi_content_boost'])) {
131
- $boost = floatval($_REQUEST['relevanssi_content_boost']);
132
- update_option('relevanssi_content_boost', $boost);
 
133
  }
134
 
135
- if (isset($_REQUEST['relevanssi_title_boost'])) {
136
- $boost = floatval($_REQUEST['relevanssi_title_boost']);
137
- update_option('relevanssi_title_boost', $boost);
138
  }
139
 
140
- if (isset($_REQUEST['relevanssi_comment_boost'])) {
141
- $boost = floatval($_REQUEST['relevanssi_comment_boost']);
142
- update_option('relevanssi_comment_boost', $boost);
143
  }
144
 
145
- if (isset($_REQUEST['relevanssi_min_word_length'])) {
146
- $value = intval($_REQUEST['relevanssi_min_word_length']);
147
- if ($value === 0) $value = 3;
148
- update_option('relevanssi_min_word_length', $value);
 
 
149
  }
150
 
151
- if ($_REQUEST['tab'] === "indexing") {
152
- if (!isset($_REQUEST['relevanssi_index_author'])) {
153
- $_REQUEST['relevanssi_index_author'] = "off";
154
  }
155
-
156
- if (!isset($_REQUEST['relevanssi_index_excerpt'])) {
157
- $_REQUEST['relevanssi_index_excerpt'] = "off";
158
  }
159
 
160
- if (!isset($_REQUEST['relevanssi_expand_shortcodes'])) {
161
- $_REQUEST['relevanssi_expand_shortcodes'] = "off";
162
  }
163
  }
164
 
165
- if ($_REQUEST['tab'] === "searching") {
166
- if (!isset($_REQUEST['relevanssi_admin_search'])) {
167
- $_REQUEST['relevanssi_admin_search'] = "off";
168
  }
169
 
170
- if (!isset($_REQUEST['relevanssi_throttle'])) {
171
- $_REQUEST['relevanssi_throttle'] = "off";
172
  }
173
 
174
- if (!isset($_REQUEST['relevanssi_disable_or_fallback'])) {
175
- $_REQUEST['relevanssi_disable_or_fallback'] = "off";
176
  }
177
 
178
- if (!isset($_REQUEST['relevanssi_respect_exclude'])) {
179
- $_REQUEST['relevanssi_respect_exclude'] = "off";
180
  }
181
-
182
- if (!isset($_REQUEST['relevanssi_wpml_only_current'])) {
183
- $_REQUEST['relevanssi_wpml_only_current'] = "off";
184
  }
185
-
186
- if (!isset($_REQUEST['relevanssi_polylang_all_languages'])) {
187
- $_REQUEST['relevanssi_polylang_all_languages'] = "off";
188
  }
189
  }
190
 
191
- if ($_REQUEST['tab'] === "logging") {
192
- if (!isset($_REQUEST['relevanssi_log_queries'])) {
193
- $_REQUEST['relevanssi_log_queries'] = "off";
194
  }
195
-
196
- if (!isset($_REQUEST['relevanssi_log_queries_with_ip'])) {
197
- $_REQUEST['relevanssi_log_queries_with_ip'] = "off";
198
  }
199
  }
200
 
201
- if ($_REQUEST['tab'] === "excerpts") {
202
- if (!isset($_REQUEST['relevanssi_excerpts'])) {
203
- $_REQUEST['relevanssi_excerpts'] = "off";
204
  }
205
 
206
- if (!isset($_REQUEST['relevanssi_show_matches'])) {
207
- $_REQUEST['relevanssi_show_matches'] = "off";
208
  }
209
 
210
- if (!isset($_REQUEST['relevanssi_hilite_title'])) {
211
- $_REQUEST['relevanssi_hilite_title'] = "off";
212
  }
213
-
214
- if (!isset($_REQUEST['relevanssi_highlight_docs'])) {
215
- $_REQUEST['relevanssi_highlight_docs'] = "off";
216
  }
217
-
218
- if (!isset($_REQUEST['relevanssi_highlight_comments'])) {
219
- $_REQUEST['relevanssi_highlight_comments'] = "off";
220
  }
221
 
222
- if (!isset($_REQUEST['relevanssi_excerpt_custom_fields'])) {
223
- $_REQUEST['relevanssi_excerpt_custom_fields'] = "off";
224
  }
225
 
226
- if (!isset($_REQUEST['relevanssi_word_boundaries'])) {
227
- $_REQUEST['relevanssi_word_boundaries'] = "off";
228
  }
229
  }
230
 
231
- if (isset($_REQUEST['relevanssi_excerpt_length'])) {
232
- $value = intval($_REQUEST['relevanssi_excerpt_length']);
233
- if ($value != 0) {
234
- update_option('relevanssi_excerpt_length', $value);
235
  }
236
  }
237
 
238
- if (isset($_REQUEST['relevanssi_synonyms'])) {
239
- $linefeeds = array("\r\n", "\n", "\r");
240
- $value = str_replace($linefeeds, ";", $_REQUEST['relevanssi_synonyms']);
241
- $value = stripslashes($value);
242
- update_option('relevanssi_synonyms', $value);
243
  }
244
 
245
- if (isset($_REQUEST['relevanssi_show_matches'])) update_option('relevanssi_show_matches', $_REQUEST['relevanssi_show_matches']);
246
- if (isset($_REQUEST['relevanssi_show_matches_text'])) {
 
 
247
  $value = $_REQUEST['relevanssi_show_matches_text'];
248
- $value = str_replace('"', "'", $value);
249
- update_option('relevanssi_show_matches_text', $value);
250
  }
251
 
252
  $relevanssi_punct = array();
253
- if (isset($_REQUEST['relevanssi_punct_quotes'])) $relevanssi_punct['quotes'] = $_REQUEST['relevanssi_punct_quotes'];
254
- if (isset($_REQUEST['relevanssi_punct_hyphens'])) $relevanssi_punct['hyphens'] = $_REQUEST['relevanssi_punct_hyphens'];
255
- if (isset($_REQUEST['relevanssi_punct_ampersands'])) $relevanssi_punct['ampersands'] = $_REQUEST['relevanssi_punct_ampersands'];
256
- if (!empty($relevanssi_punct)) update_option('relevanssi_punctuation', $relevanssi_punct);
 
 
 
 
 
 
 
 
 
 
 
257
 
258
- $post_type_weights = array();
259
- $index_post_types = array();
260
  $index_taxonomies_list = array();
261
- $index_terms_list = array();
262
- foreach ($_REQUEST as $key => $value) {
263
- if (substr($key, 0, strlen('relevanssi_weight_')) === 'relevanssi_weight_') {
264
- $type = substr($key, strlen('relevanssi_weight_'));
265
- $post_type_weights[$type] = $value;
266
  }
267
- if (substr($key, 0, strlen('relevanssi_index_type_')) === 'relevanssi_index_type_') {
268
- $type = substr($key, strlen('relevanssi_index_type_'));
269
- if ('on' === $value) $index_post_types[$type] = true;
 
 
270
  }
271
- if (substr($key, 0, strlen('relevanssi_index_taxonomy_')) === 'relevanssi_index_taxonomy_') {
272
- $type = substr($key, strlen('relevanssi_index_taxonomy_'));
273
- if ('on' === $value) $index_taxonomies_list[$type] = true;
 
 
274
  }
275
- if (substr($key, 0, strlen('relevanssi_index_terms_')) === 'relevanssi_index_terms_') {
276
- $type = substr($key, strlen('relevanssi_index_terms_'));
277
- if ('on' === $value) $index_terms_list[$type] = true;
 
 
278
  }
279
  }
280
 
281
- if (count($post_type_weights) > 0) {
282
- update_option('relevanssi_post_type_weights', $post_type_weights);
 
 
 
283
  }
284
 
285
- if (count($index_post_types) > 0) {
286
- update_option('relevanssi_index_post_types', array_keys($index_post_types));
287
  }
288
 
289
- update_option('relevanssi_index_taxonomies_list', array_keys($index_taxonomies_list));
290
- if (RELEVANSSI_PREMIUM) update_option('relevanssi_index_terms', array_keys($index_terms_list));
 
291
 
292
- if (isset($_REQUEST['relevanssi_index_fields_select'])) {
293
- $fields_option = "";
294
- if ($_REQUEST['relevanssi_index_fields_select'] === "all") {
295
- $fields_option = "all";
296
  }
297
- if ($_REQUEST['relevanssi_index_fields_select'] === "visible") {
298
- $fields_option = "visible";
299
  }
300
- if ($_REQUEST['relevanssi_index_fields_select'] === "some") {
301
- if (isset($_REQUEST['relevanssi_index_fields'])) $fields_option = $_REQUEST['relevanssi_index_fields'];
 
 
302
  }
303
- update_option('relevanssi_index_fields', $fields_option);
304
  }
305
-
306
- if (isset($_REQUEST['relevanssi_trim_logs'])) {
307
  $trim_logs = $_REQUEST['relevanssi_trim_logs'];
308
- if (!is_numeric($trim_logs)) $trim_logs = 0;
309
- if ($trim_logs < 0) $trim_logs = 0;
310
- update_option('relevanssi_trim_logs', $trim_logs);
 
311
  }
312
 
313
- if (isset($_REQUEST['relevanssi_cat'])) {
314
- if (is_array($_REQUEST['relevanssi_cat'])) {
315
- $csv_cats = implode(",", $_REQUEST['relevanssi_cat']);
316
- update_option('relevanssi_cat', $csv_cats);
317
  }
318
  } else {
319
- if (isset($_REQUEST['relevanssi_cat_active'])) {
320
- update_option('relevanssi_cat', "");
321
  }
322
  }
323
 
324
- if (isset($_REQUEST['relevanssi_excat'])) {
325
- if (is_array($_REQUEST['relevanssi_excat'])) {
326
- $csv_cats = implode(",", $_REQUEST['relevanssi_excat']);
327
- update_option('relevanssi_excat', $csv_cats);
328
  }
329
  } else {
330
- if (isset($_REQUEST['relevanssi_excat_active'])) {
331
- update_option('relevanssi_excat', "");
332
  }
333
  }
334
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
 
336
- if (isset($_REQUEST['relevanssi_admin_search'])) update_option('relevanssi_admin_search', $_REQUEST['relevanssi_admin_search']);
337
- if (isset($_REQUEST['relevanssi_excerpts'])) update_option('relevanssi_excerpts', $_REQUEST['relevanssi_excerpts']);
338
- if (isset($_REQUEST['relevanssi_excerpt_type'])) update_option('relevanssi_excerpt_type', $_REQUEST['relevanssi_excerpt_type']);
339
- if (isset($_REQUEST['relevanssi_excerpt_allowable_tags'])) update_option('relevanssi_excerpt_allowable_tags', $_REQUEST['relevanssi_excerpt_allowable_tags']);
340
- if (isset($_REQUEST['relevanssi_log_queries'])) update_option('relevanssi_log_queries', $_REQUEST['relevanssi_log_queries']);
341
- if (isset($_REQUEST['relevanssi_log_queries_with_ip'])) update_option('relevanssi_log_queries_with_ip', $_REQUEST['relevanssi_log_queries_with_ip']);
342
- if (isset($_REQUEST['relevanssi_highlight'])) update_option('relevanssi_highlight', $_REQUEST['relevanssi_highlight']);
343
- if (isset($_REQUEST['relevanssi_highlight_docs'])) update_option('relevanssi_highlight_docs', $_REQUEST['relevanssi_highlight_docs']);
344
- if (isset($_REQUEST['relevanssi_highlight_comments'])) update_option('relevanssi_highlight_comments', $_REQUEST['relevanssi_highlight_comments']);
345
- if (isset($_REQUEST['relevanssi_txt_col'])) update_option('relevanssi_txt_col', $_REQUEST['relevanssi_txt_col']);
346
- if (isset($_REQUEST['relevanssi_bg_col'])) update_option('relevanssi_bg_col', $_REQUEST['relevanssi_bg_col']);
347
- if (isset($_REQUEST['relevanssi_css'])) update_option('relevanssi_css', $_REQUEST['relevanssi_css']);
348
- if (isset($_REQUEST['relevanssi_class'])) update_option('relevanssi_class', $_REQUEST['relevanssi_class']);
349
- if (isset($_REQUEST['relevanssi_expst'])) update_option('relevanssi_exclude_posts', $_REQUEST['relevanssi_expst']); //added by OdditY
350
- if (isset($_REQUEST['relevanssi_hilite_title'])) update_option('relevanssi_hilite_title', $_REQUEST['relevanssi_hilite_title']); //added by OdditY
351
- if (isset($_REQUEST['relevanssi_index_comments'])) update_option('relevanssi_index_comments', $_REQUEST['relevanssi_index_comments']); //added by OdditY
352
- if (isset($_REQUEST['relevanssi_index_author'])) update_option('relevanssi_index_author', $_REQUEST['relevanssi_index_author']);
353
- if (isset($_REQUEST['relevanssi_index_excerpt'])) update_option('relevanssi_index_excerpt', $_REQUEST['relevanssi_index_excerpt']);
354
- if (isset($_REQUEST['relevanssi_fuzzy'])) update_option('relevanssi_fuzzy', $_REQUEST['relevanssi_fuzzy']);
355
- if (isset($_REQUEST['relevanssi_expand_shortcodes'])) update_option('relevanssi_expand_shortcodes', $_REQUEST['relevanssi_expand_shortcodes']);
356
- if (isset($_REQUEST['relevanssi_implicit_operator'])) update_option('relevanssi_implicit_operator', $_REQUEST['relevanssi_implicit_operator']);
357
- if (isset($_REQUEST['relevanssi_omit_from_logs'])) update_option('relevanssi_omit_from_logs', $_REQUEST['relevanssi_omit_from_logs']);
358
- if (isset($_REQUEST['relevanssi_index_limit'])) update_option('relevanssi_index_limit', $_REQUEST['relevanssi_index_limit']);
359
- if (isset($_REQUEST['relevanssi_disable_or_fallback'])) update_option('relevanssi_disable_or_fallback', $_REQUEST['relevanssi_disable_or_fallback']);
360
- if (isset($_REQUEST['relevanssi_respect_exclude'])) update_option('relevanssi_respect_exclude', $_REQUEST['relevanssi_respect_exclude']);
361
- if (isset($_REQUEST['relevanssi_throttle'])) update_option('relevanssi_throttle', $_REQUEST['relevanssi_throttle']);
362
- if (isset($_REQUEST['relevanssi_wpml_only_current'])) update_option('relevanssi_wpml_only_current', $_REQUEST['relevanssi_wpml_only_current']);
363
- if (isset($_REQUEST['relevanssi_polylang_all_languages'])) update_option('relevanssi_polylang_all_languages', $_REQUEST['relevanssi_polylang_all_languages']);
364
- if (isset($_REQUEST['relevanssi_word_boundaries'])) update_option('relevanssi_word_boundaries', $_REQUEST['relevanssi_word_boundaries']);
365
- if (isset($_REQUEST['relevanssi_default_orderby'])) update_option('relevanssi_default_orderby', $_REQUEST['relevanssi_default_orderby']);
366
- if (isset($_REQUEST['relevanssi_excerpt_custom_fields'])) update_option('relevanssi_excerpt_custom_fields', $_REQUEST['relevanssi_excerpt_custom_fields']);
367
-
368
- if (function_exists('relevanssi_update_premium_options')) {
369
  relevanssi_update_premium_options();
370
  }
 
371
  }
372
 
373
- function relevanssi_add_stopword($term) {
 
 
 
 
 
 
 
 
 
 
 
374
  global $wpdb;
375
- if ('' === $term) return; // do not add empty $term to stopwords - added by renaissancehack
 
 
376
 
377
  $n = 0;
378
  $s = 0;
379
 
380
- $terms = explode(',', $term);
381
- if (count($terms) > 1) {
382
- foreach($terms as $term) {
383
  $n++;
384
- $term = trim($term);
385
- $success = relevanssi_add_single_stopword($term);
386
- if ($success) $s++;
 
 
387
  }
388
- printf(__("<div id='message' class='updated fade'><p>Successfully added %d/%d terms to stopwords!</p></div>", "relevanssi"), $s, $n);
389
- }
390
- else {
391
- // add to stopwords
392
- $success = relevanssi_add_single_stopword($term);
393
-
394
- if ($success) {
395
- printf(__("<div id='message' class='updated fade'><p>Term '%s' added to stopwords!</p></div>", "relevanssi"), stripslashes($term));
396
  }
397
- else {
398
- printf(__("<div id='message' class='updated fade'><p>Couldn't add term '%s' to stopwords!</p></div>", "relevanssi"), stripslashes($term));
 
 
 
 
 
 
 
 
 
 
 
 
399
  }
400
  }
 
 
401
  }
402
 
403
- function relevanssi_add_single_stopword($term) {
 
 
 
 
 
 
 
 
 
 
404
  global $wpdb, $relevanssi_variables;
405
- if ('' === $term) return;
406
-
407
- $term = stripslashes($term);
408
-
409
- if (method_exists($wpdb, 'esc_like')) {
410
- $term = esc_sql($wpdb->esc_like($term));
411
- }
412
- else {
413
- // Compatibility for pre-4.0 WordPress
414
- $term = esc_sql(like_escape($term));
415
  }
416
 
417
- $q = $wpdb->prepare("INSERT INTO " . $relevanssi_variables['stopword_table'] . " (stopword) VALUES (%s)", $term);
418
- // Clean: escaped.
419
- $success = $wpdb->query($q);
 
420
 
421
- if ($success) {
422
- // remove from index
423
- $q = $wpdb->prepare("DELETE FROM " . $relevanssi_variables['relevanssi_table'] . " WHERE term=%s", $term);
424
- $wpdb->query($q);
425
  return true;
426
- }
427
- else {
428
  return false;
429
  }
430
  }
431
 
 
 
 
 
 
 
 
 
432
  function relevanssi_remove_all_stopwords() {
433
  global $wpdb, $relevanssi_variables;
434
 
435
- $success = $wpdb->query("TRUNCATE " . $relevanssi_variables['stopword_table']);
436
 
437
- printf(__("<div id='message' class='updated fade'><p>Stopwords removed! Remember to re-index.</p></div>", "relevanssi"), $term);
 
 
 
 
438
  }
439
 
440
- function relevanssi_remove_stopword($term, $verbose = true) {
 
 
 
 
 
 
 
 
 
 
 
441
  global $wpdb, $relevanssi_variables;
442
 
443
- $q = $wpdb->prepare("DELETE FROM " . $relevanssi_variables['stopword_table'] . " WHERE stopword=%s", $term);
444
- $success = $wpdb->query($q);
445
 
446
- if ($success) {
447
- if ($verbose) {
448
- echo "<div id='message' class='updated fade'><p>";
449
- printf(__("Term '%s' removed from stopwords! Re-index to get it back to index.", "relevanssi"), stripslashes($term));
450
- echo "</p></div>";
451
  }
452
- else {
453
- return true;
454
- }
455
- }
456
- else {
457
- if ($verbose) {
458
- echo "<div id='message' class='updated fade'><p>";
459
- printf(__("Couldn't remove term '%s' from stopwords!", "relevanssi"), stripslashes($term));
460
- echo "</p></div>";
461
- }
462
- else {
463
- return false;
464
  }
 
465
  }
466
  }
467
 
468
- function relevanssi_common_words($limit = 25, $wp_cli = false) {
469
- global $wpdb, $relevanssi_variables, $wp_version;
 
 
 
 
 
 
 
 
 
 
 
 
470
 
471
- RELEVANSSI_PREMIUM ? $plugin = 'relevanssi-premium' : $plugin = 'relevanssi';
 
 
 
472
 
473
- if (!is_numeric($limit)) $limit = 25;
 
 
474
 
475
- $words = $wpdb->get_results("SELECT COUNT(*) as cnt, term FROM " . $relevanssi_variables['relevanssi_table'] . " GROUP BY term ORDER BY cnt DESC LIMIT $limit");
476
- // Clean: $limit is numeric.
477
 
478
- if (!$wp_cli) {
479
- echo "<h2>" . __("25 most common words in the index", 'relevanssi') . "</h2>";
480
- echo "<p>" . __("These words are excellent stopword material. A word that appears in most of the posts in the database is quite pointless when searching. This is also an easy way to create a completely new stopword list, if one isn't available in your language. Click the icon after the word to add the word to the stopword list. The word will also be removed from the index, so rebuilding the index is not necessary.", 'relevanssi') . "</p>";
481
 
482
  ?>
483
  <input type="hidden" name="dowhat" value="add_stopword" />
484
  <table class="form-table">
485
  <tr>
486
- <th scope="row"><?php _e("Stopword Candidates", "relevanssi"); ?></th>
487
  <td>
488
  <ul>
489
- <?php
490
-
491
- $src = plugins_url('delete.png', $relevanssi_variables['file']);
492
 
493
- foreach ($words as $word) {
494
- $stop = __('Add to stopwords', 'relevanssi');
495
- printf('<li>%s (%d) <input style="padding: 0; margin: 0" type="image" src="%s" alt="%s" name="term" value="%s"/></li>', $word->term, $word->cnt, $src, $stop, $word->term);
496
- }
497
  ?>
498
  </ul>
499
  </td>
@@ -502,1325 +662,1536 @@ function relevanssi_common_words($limit = 25, $wp_cli = false) {
502
  <?php
503
 
504
  }
505
- else {
506
- // WP CLI gets the list of words
507
- return $words;
508
- }
509
  }
510
 
 
 
 
 
 
511
  function relevanssi_query_log() {
512
- global $wpdb;
 
 
 
 
 
513
 
514
- $days30 = apply_filters('relevanssi_30days', 30);
515
 
516
- echo '<h3>' . __("Total Searches", 'relevanssi') . '</h3>';
517
-
518
- echo "<div style='width: 50%; overflow: auto'>";
519
- relevanssi_total_queries( __("Totals", 'relevanssi') );
520
- echo '</div>';
521
 
522
  echo '<div style="clear: both"></div>';
523
 
524
- echo '<h3>' . __("Common Queries", 'relevanssi') . '</h3>';
 
 
 
 
 
 
 
525
 
526
- $limit = apply_filters('relevanssi_user_searches_limit', 20);
527
-
528
- printf("<p>" . __("Here you can see the %d most common user search queries, how many times those queries were made and how many results were found for those queries.", 'relevanssi') . "</p>", $limit);
529
 
530
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
531
- relevanssi_date_queries(1, __("Today and yesterday", 'relevanssi'));
532
  echo '</div>';
533
 
534
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
535
- relevanssi_date_queries(7, __("Last 7 days", 'relevanssi'));
536
  echo '</div>';
537
 
538
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
539
- relevanssi_date_queries($days30, sprintf(__("Last %d days", 'relevanssi'), $days30));
 
540
  echo '</div>';
541
 
542
  echo '<div style="clear: both"></div>';
543
 
544
- echo '<h3>' . __("Unsuccessful Queries", 'relevanssi') . '</h3>';
545
 
546
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
547
- relevanssi_date_queries(1, __("Today and yesterday", 'relevanssi'), 'bad');
548
  echo '</div>';
549
 
550
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
551
- relevanssi_date_queries(7, __("Last 7 days", 'relevanssi'), 'bad');
552
  echo '</div>';
553
 
554
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
555
- relevanssi_date_queries($days30, sprintf(__("Last %d days", 'relevanssi'), $days30), 'bad');
 
556
  echo '</div>';
557
 
558
- if ( current_user_can('manage_options') ) {
559
 
560
  echo '<div style="clear: both"></div>';
561
- $nonce = wp_nonce_field('relevanssi_reset_logs', '_relresnonce', true, false);
562
- echo '<h3>' . __('Reset Logs', 'relevanssi') . "</h3>\n";
563
- echo "<form method='post'>\n$nonce";
564
- echo "<p>";
565
- printf(__('To reset the logs, type "reset" into the box here %s and click %s', 'relevanssi'), ' <input type="text" name="relevanssi_reset_code" />', ' <input type="submit" name="relevanssi_reset" value="Reset" class="button" />');
566
- echo "</p></form>";
567
 
568
  }
569
 
570
- echo "</div>";
571
  }
572
 
 
 
 
 
 
 
 
 
573
  function relevanssi_total_queries( $title ) {
574
  global $wpdb, $relevanssi_variables;
575
  $log_table = $relevanssi_variables['log_table'];
576
 
577
- $count = array();
 
578
 
579
- $count[__('Today and yesterday', 'relevanssi')] = $wpdb->get_var("SELECT COUNT(id) FROM $log_table WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= 1;");
580
- $count[__('Last 7 days', 'relevanssi')] = $wpdb->get_var("SELECT COUNT(id) FROM $log_table WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= 7;");
581
- $count[__('Last 30 days', 'relevanssi')] = $wpdb->get_var("SELECT COUNT(id) FROM $log_table WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= 30;");
582
- $count[__('Forever', 'relevanssi')] = $wpdb->get_var("SELECT COUNT(id) FROM $log_table;");
583
 
584
- echo "<table class='widefat'><thead><tr><th colspan='2'>$title</th></tr></thead><tbody><tr><th>" . __('When', 'relevanssi') . "</th><th style='text-align: center'>" . __('Searches', 'relevanssi') . "</th></tr>";
585
- foreach ($count as $when => $searches) {
586
- echo "<tr><td>$when</td><td style='text-align: center'>$searches</td></tr>";
587
- }
588
- echo "</tbody></table>";
 
 
589
 
 
 
 
 
 
590
  }
591
 
592
- function relevanssi_date_queries($d, $title, $version = 'good') {
 
 
 
 
 
 
 
 
 
 
 
593
  global $wpdb, $relevanssi_variables;
594
  $log_table = $relevanssi_variables['log_table'];
595
 
596
- $limit = apply_filters('relevanssi_user_searches_limit', 20);
597
-
598
- if ($version === 'good')
599
- $queries = $wpdb->get_results("SELECT COUNT(DISTINCT(id)) as cnt, query, hits
600
- FROM $log_table
601
- WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= $d
602
- GROUP BY query
603
- ORDER BY cnt DESC
604
- LIMIT $limit");
605
-
606
- if ($version === 'bad')
607
- $queries = $wpdb->get_results("SELECT COUNT(DISTINCT(id)) as cnt, query, hits
608
- FROM $log_table
609
- WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= $d
610
- AND hits = 0
611
- GROUP BY query
612
- ORDER BY cnt DESC
613
- LIMIT $limit");
614
-
615
- if (count($queries) > 0) {
616
- echo "<table class='widefat'><thead><tr><th colspan='3'>$title</th></tr></thead><tbody><tr><th>" . __('Query', 'relevanssi') . "</th><th style='text-align: center'>#</th><th style='text-align: center'>" . __('Hits', 'relevanssi') . "</th></tr>";
617
- foreach ($queries as $query) {
618
- $url = get_bloginfo('url');
619
- $u_q = urlencode($query->query);
620
- echo "<tr><td><a href='$url/?s=$u_q'>" . esc_attr($query->query) . "</a></td><td style='padding: 3px 5px; text-align: center'>" . $query->cnt . "</td><td style='padding: 3px 5px; text-align: center'>" . $query->hits . "</td></tr>";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  }
622
- echo "</tbody></table>";
623
  }
624
  }
625
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
626
  function relevanssi_options_form() {
627
  global $relevanssi_variables, $wpdb;
628
 
629
- wp_enqueue_style('dashboard');
630
- wp_print_styles('dashboard');
631
- wp_enqueue_script('dashboard');
632
- wp_print_scripts('dashboard');
633
 
634
  $serialize_options = array();
635
 
636
- $content_boost = get_option('relevanssi_content_boost');
637
- $serialize_options['relevanssi_content_boost'] = $content_boost;
638
- $title_boost = get_option('relevanssi_title_boost');
639
- $serialize_options['relevanssi_title_boost'] = $title_boost;
640
- $comment_boost = get_option('relevanssi_comment_boost');
641
- $serialize_options['relevanssi_comment_boost'] = $comment_boost;
642
- $admin_search = get_option('relevanssi_admin_search');
643
- $serialize_options['relevanssi_admin_search'] = $admin_search;
644
- if ('on' === $admin_search) {
645
- $admin_search = 'checked="checked"';
646
- }
647
- else {
648
- $admin_search = '';
649
- }
650
-
651
- $index_limit = get_option('relevanssi_index_limit');
652
- $serialize_options['relevanssi_index_limit'] = $index_limit;
653
-
654
- $excerpts = get_option('relevanssi_excerpts');
655
- $serialize_options['relevanssi_excerpts'] = $excerpts;
656
- if ('on' === $excerpts) {
657
- $excerpts = 'checked="checked"';
658
- }
659
- else {
660
- $excerpts = '';
661
- }
662
-
663
- $excerpt_length = get_option('relevanssi_excerpt_length');
664
- $serialize_options['relevanssi_excerpt_length'] = $excerpt_length;
665
- $excerpt_type = get_option('relevanssi_excerpt_type');
666
- $serialize_options['relevanssi_excerpt_type'] = $excerpt_type;
667
- $excerpt_chars = "";
668
- $excerpt_words = "";
669
- switch ($excerpt_type) {
670
- case "chars":
671
- $excerpt_chars = 'selected="selected"';
672
- break;
673
- case "words":
674
- $excerpt_words = 'selected="selected"';
675
- break;
676
- }
677
- $excerpt_allowable_tags = get_option('relevanssi_excerpt_allowable_tags');
678
- $serialize_options['relevanssi_excerpt_allowable_tags'] = $excerpt_allowable_tags;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
679
 
680
- $excerpt_custom_fields = ('on' === get_option('relevanssi_excerpt_custom_fields') ? 'checked="checked"' : '');
681
- $serialize_options['relevanssi_excerpt_custom_fields'] = get_option('relevanssi_excerpt_custom_fields');
682
-
683
- $log_queries = get_option('relevanssi_log_queries');
684
- $serialize_options['relevanssi_log_queries'] = $log_queries;
685
- if ('on' === $log_queries) {
686
- $log_queries = 'checked="checked"';
687
- }
688
- else {
689
- $log_queries = '';
690
- }
691
-
692
- $log_queries_with_ip = get_option('relevanssi_log_queries_with_ip');
693
- $serialize_options['relevanssi_log_queries_with_ip'] = $log_queries_with_ip;
694
- if ('on' === $log_queries_with_ip) {
695
- $log_queries_with_ip = 'checked="checked"';
696
- }
697
- else {
698
- $log_queries_with_ip = '';
699
- }
700
-
701
- $trim_logs = get_option('relevanssi_trim_logs');
702
- $serialize_options['relevanssi_trim_logs'] = $trim_logs;
703
-
704
- $hide_branding = get_option('relevanssi_hide_branding');
705
- $serialize_options['relevanssi_hide_branding'] = $hide_branding;
706
- if ('on' === $hide_branding) {
707
- $hide_branding = 'checked="checked"';
708
- }
709
- else {
710
- $hide_branding = '';
711
- }
712
-
713
- $highlight = get_option('relevanssi_highlight');
714
- $serialize_options['relevanssi_highlight'] = $highlight;
715
- $highlight_none = "";
716
- $highlight_mark = "";
717
- $highlight_em = "";
718
- $highlight_strong = "";
719
- $highlight_col = "";
720
- $highlight_bgcol = "";
721
- $highlight_style = "";
722
- $highlight_class = "";
723
- $txt_col_display = "class='screen-reader-text'";
724
- $bg_col_display = "class='screen-reader-text'";
725
- $css_display = "class='screen-reader-text'";
726
- $class_display = "class='screen-reader-text'";
727
- switch ($highlight) {
728
- case "no":
729
- $highlight_none = 'selected="selected"';
730
- break;
731
- case "mark":
732
- $highlight_mark = 'selected="selected"';
733
- break;
734
- case "em":
735
- $highlight_em = 'selected="selected"';
736
- break;
737
- case "strong":
738
- $highlight_strong = 'selected="selected"';
739
- break;
740
- case "col":
741
- $highlight_col = 'selected="selected"';
742
- $txt_col_display = '';
743
- break;
744
- case "bgcol":
745
- $highlight_bgcol = 'selected="selected"';
746
- $bg_col_display = '';
747
- break;
748
- case "css":
749
- $highlight_style = 'selected="selected"';
750
- $css_display = '';
751
- break;
752
- case "class":
753
- $highlight_class = 'selected="selected"';
754
- $class_display = '';
755
- break;
756
- }
757
-
758
- $index_fields = get_option('relevanssi_index_fields');
759
- $serialize_options['relevanssi_index_fields'] = $index_fields;
760
-
761
- $fields_select_all = "";
762
- $fields_select_none = "";
763
- $fields_select_some = "selected='selected'";
764
- $fields_select_visible = "";
765
- $original_index_fields = $index_fields;
766
 
767
- if (empty($index_fields)) {
768
- $fields_select_none = "selected='selected'";
769
- $fields_select_some = "";
770
- }
771
- if ($index_fields === "all") {
772
- $fields_select_all = "selected='selected'";
773
- $fields_select_some = "";
774
- $index_fields = "";
775
- }
776
- if ($index_fields === "visible") {
777
- $fields_select_visible = "selected='selected'";
778
- $fields_select_some = "";
779
- $index_fields = "";
780
- }
781
-
782
- $txt_col = get_option('relevanssi_txt_col');
783
- if (substr($txt_col, 0, 1) != "#") $txt_col = "#" . $txt_col;
784
- $txt_col = relevanssi_sanitize_hex_color($txt_col);
785
- $serialize_options['relevanssi_txt_col'] = $txt_col;
786
-
787
- $bg_col = get_option('relevanssi_bg_col');
788
- if (substr($bg_col, 0, 1) != "#") $bg_col = "#" . $bg_col;
789
- $bg_col = relevanssi_sanitize_hex_color($bg_col);
790
- $serialize_options['relevanssi_bg_col'] = $bg_col;
791
-
792
- $css = get_option('relevanssi_css');
793
- $serialize_options['relevanssi_css'] = $css;
794
- $class = get_option('relevanssi_class');
795
- $serialize_options['relevanssi_class'] = $class;
796
-
797
- $cat = get_option('relevanssi_cat');
798
- $serialize_options['relevanssi_cat'] = $cat;
799
- $excat = get_option('relevanssi_excat');
800
- $serialize_options['relevanssi_excat'] = $excat;
801
-
802
- $fuzzy = get_option('relevanssi_fuzzy');
803
- $serialize_options['relevanssi_fuzzy'] = $fuzzy;
804
- $fuzzy_sometimes = ('sometimes' === $fuzzy ? 'selected="selected"' : '');
805
- $fuzzy_always = ('always' === $fuzzy ? 'selected="selected"' : '');
806
- $fuzzy_never = ('never' === $fuzzy ? 'selected="selected"' : '');
807
-
808
- $implicit = get_option('relevanssi_implicit_operator');
809
- $serialize_options['relevanssi_implicit_operator'] = $implicit;
810
- $implicit_and = ('AND' === $implicit ? 'selected="selected"' : '');
811
- $implicit_or = ('OR' === $implicit ? 'selected="selected"' : '');
812
- $orfallback_visibility = "class='screen-reader-text'";
813
- if ($implicit === "AND") $orfallback_visibility = "";
814
-
815
- $expand_shortcodes = ('on' === get_option('relevanssi_expand_shortcodes') ? 'checked="checked"' : '');
816
- $serialize_options['relevanssi_expand_shortcodes'] = get_option('relevanssi_expand_shortcodes');
817
- $disablefallback = ('on' === get_option('relevanssi_disable_or_fallback') ? 'checked="checked"' : '');
818
- $serialize_options['relevanssi_disable_or_fallback'] = get_option('relevanssi_disable_or_fallback');
819
-
820
- $throttle = ('on' === get_option('relevanssi_throttle') ? 'checked="checked"' : '');
821
- $serialize_options['relevanssi_throttle'] = get_option('relevanssi_throttle');
822
-
823
- $throttle_limit = get_option('relevanssi_throttle_limit');
824
- $serialize_options['relevanssi_throttle_limit'] = $throttle_limit;
825
-
826
- $omit_from_logs = get_option('relevanssi_omit_from_logs');
827
- $serialize_options['relevanssi_omit_from_logs'] = $omit_from_logs;
828
-
829
- $synonyms = get_option('relevanssi_synonyms');
830
- $serialize_options['relevanssi_synonyms'] = $synonyms;
831
- isset($synonyms) ? $synonyms = str_replace(';', "\n", $synonyms) : $synonyms = "";
832
-
833
- //Added by OdditY ->
834
- $expst = get_option('relevanssi_exclude_posts');
835
- $serialize_options['relevanssi_exclude_posts'] = $expst;
836
- $hititle = ('on' === get_option('relevanssi_hilite_title') ? 'checked="checked"' : '');
837
- $serialize_options['relevanssi_hilite_title'] = get_option('relevanssi_hilite_title');
838
- $incom_type = get_option('relevanssi_index_comments');
839
- $serialize_options['relevanssi_index_comments'] = $incom_type;
840
- $incom_type_all = "";
841
- $incom_type_normal = "";
842
- $incom_type_none = "";
843
- switch ($incom_type) {
844
- case "all":
845
- $incom_type_all = 'selected="selected"';
846
- break;
847
- case "normal":
848
- $incom_type_normal = 'selected="selected"';
849
- break;
850
- case "none":
851
- $incom_type_none = 'selected="selected"';
852
- break;
853
- }//added by OdditY END <-
854
-
855
- $highlight_docs = ('on' === get_option('relevanssi_highlight_docs') ? 'checked="checked"' : '');
856
- $highlight_coms = ('on' === get_option('relevanssi_highlight_comments') ? 'checked="checked"' : '');
857
- $serialize_options['relevanssi_highlight_docs'] = get_option('relevanssi_highlight_docs');
858
- $serialize_options['relevanssi_highlight_comments'] = get_option('relevanssi_highlight_comments');
859
-
860
- $respect_exclude = ('on' === get_option('relevanssi_respect_exclude') ? 'checked="checked"' : '');
861
- $serialize_options['relevanssi_respect_exclude'] = get_option('relevanssi_respect_exclude');
862
-
863
- $min_word_length = get_option('relevanssi_min_word_length');
864
- $serialize_options['relevanssi_min_word_length'] = $min_word_length;
865
-
866
- $index_author = ('on' === get_option('relevanssi_index_author') ? 'checked="checked"' : '');
867
- $serialize_options['relevanssi_index_author'] = get_option('relevanssi_index_author');
868
- $index_excerpt = ('on' === get_option('relevanssi_index_excerpt') ? 'checked="checked"' : '');
869
- $serialize_options['relevanssi_index_excerpt'] = get_option('relevanssi_index_excerpt');
870
-
871
- $show_matches = ('on' === get_option('relevanssi_show_matches') ? 'checked="checked"' : '');
872
- $serialize_options['relevanssi_show_matches'] = get_option('relevanssi_show_matches');
873
- $show_matches_text = stripslashes(get_option('relevanssi_show_matches_text'));
874
- $serialize_options['relevanssi_show_matches_text'] = get_option('relevanssi_show_matches_text');
875
-
876
- $wpml_only_current = ('on' === get_option('relevanssi_wpml_only_current') ? 'checked="checked"' : '');
877
- $serialize_options['relevanssi_wpml_only_current'] = get_option('relevanssi_wpml_only_current');
878
-
879
- $polylang_allow_all = ('on' === get_option('relevanssi_polylang_all_languages') ? 'checked="checked"' : '');
880
- $serialize_options['relevanssi_polylang_all_languages'] = get_option('relevanssi_polylang_all_languages');
881
-
882
- $word_boundaries = ('on' === get_option('relevanssi_word_boundaries') ? 'checked="checked"' : '');
883
- $serialize_options['relevanssi_word_boundaries'] = get_option('relevanssi_word_boundaries');
884
-
885
- $post_type_weights = get_option('relevanssi_post_type_weights');
886
- $serialize_options['relevanssi_post_type_weights'] = $post_type_weights;
887
-
888
- $index_post_types = get_option('relevanssi_index_post_types');
889
- if (empty($index_post_types)) $index_post_types = array();
890
- $serialize_options['relevanssi_index_post_types'] = $index_post_types;
891
-
892
- $index_taxonomies_list = get_option('relevanssi_index_taxonomies_list');
893
- if (empty($index_taxonomies_list)) $index_taxonomies_list = array();
894
- $serialize_options['relevanssi_index_taxonomies_list'] = $index_taxonomies_list;
895
-
896
- $orderby = get_option('relevanssi_default_orderby');
897
- $serialize_options['relevanssi_default_orderby'] = $orderby;
898
- $orderby_relevance = ('relevance' === $orderby ? 'selected="selected"' : '');
899
- $orderby_date = ('post_date' === $orderby ? 'selected="selected"' : '');
900
-
901
- $punctuation = get_option('relevanssi_punctuation');
902
- $serialize_options['relevanssi_punctuation'] = $punctuation;
903
- $punct_quotes_remove = "";
904
- $punct_quotes_replace = "";
905
- $punct_ampersands_keep = "";
906
- $punct_ampersands_remove = "";
907
- $punct_ampersands_replace = "";
908
- $punct_hyphens_keep = "";
909
- $punct_hyphens_remove = "";
910
- $punct_hyphens_replace = "";
911
- if (isset($punctuation['quotes'])) {
912
- $quotes = $punctuation['quotes'];
913
- switch ($quotes) {
914
- case 'replace':
915
- $punct_quotes_remove = "";
916
- $punct_quotes_replace = 'selected="selected"';
917
- break;
918
- case 'remove':
919
- $punct_quotes_remove = 'selected="selected"';
920
- $punct_quotes_replace = "";
921
- break;
922
- default:
923
- $punct_quotes_remove = 'selected="selected"';
924
- $punct_quotes_replace = "";
925
- }
926
  }
927
- if (isset($punctuation['ampersands'])) {
928
- $ampersands = $punctuation['ampersands'];
929
- switch ($ampersands) {
930
- case 'replace':
931
- $punct_ampersands_keep = "";
932
- $punct_ampersands_remove = "";
933
- $punct_ampersands_replace = 'selected="selected"';
934
- break;
935
- case 'remove':
936
- $punct_ampersands_keep = "";
937
- $punct_ampersands_remove = 'selected="selected"';
938
- $punct_ampersands_replace = "";
939
- break;
940
- case 'keep':
941
- $punct_ampersands_keep = 'selected="selected"';
942
- $punct_ampersands_remove = "";
943
- $punct_ampersands_replace = "";
944
- break;
945
- default:
946
- $punct_ampersands_keep = "";
947
- $punct_ampersands_remove = "";
948
- $punct_ampersands_replace = 'selected="selected"';
949
- }
950
  }
951
- if (isset($punctuation['hyphens'])) {
952
- $hyphens = $punctuation['hyphens'];
953
- switch ($hyphens) {
954
- case 'replace':
955
- $punct_hyphens_keep = "";
956
- $punct_hyphens_remove = "";
957
- $punct_hyphens_replace = 'selected="selected"';
958
- break;
959
- case 'remove':
960
- $punct_hyphens_keep = "";
961
- $punct_hyphens_remove = 'selected="selected"';
962
- $punct_hyphens_replace = "";
963
- break;
964
- case 'keep':
965
- $punct_hyphens_keep = 'selected="selected"';
966
- $punct_hyphens_remove = "";
967
- $punct_hyphens_replace = "";
968
- break;
969
- default:
970
- $punct_hyphens_keep = "";
971
- $punct_hyphens_remove = "";
972
- $punct_hyphens_replace = 'selected="selected"';
973
- }
974
  }
975
- if (RELEVANSSI_PREMIUM) {
976
- $api_key = get_option('relevanssi_api_key');
977
- $serialize_options['relevanssi_api_key'] = $api_key;
978
-
979
- $link_boost = get_option('relevanssi_link_boost');
980
- $serialize_options['relevanssi_link_boost'] = $link_boost;
981
-
982
- $intlinks = get_option('relevanssi_internal_links');
983
- $serialize_options['relevanssi_internal_links'] = $intlinks;
984
- $intlinks_strip = ('strip' === $intlinks ? 'selected="selected"' : '');
985
- $intlinks_nostrip = ('nostrip' === $intlinks ? 'selected="selected"' : '');
986
- $intlinks_noindex = ('noindex' === $intlinks ? 'selected="selected"' : '');
987
-
988
- $highlight_docs_ext = ('on' === get_option('relevanssi_highlight_docs_external') ? 'checked="checked"' : '');
989
- $serialize_options['relevanssi_highlight_docs_external'] = get_option('relevanssi_highlight_docs_external');
990
-
991
- $thousand_separator = get_option('relevanssi_thousand_separator');
992
- $serialize_options['relevanssi_thousand_separator'] = $thousand_separator;
993
-
994
- $disable_shortcodes = get_option('relevanssi_disable_shortcodes');
995
- $serialize_options['relevanssi_disable_shortcodes'] = $disable_shortcodes;
996
-
997
- $index_users = ('on' === get_option('relevanssi_index_users') ? 'checked="checked"' : '');
998
- $serialize_options['relevanssi_index_users'] = get_option('relevanssi_index_users');
999
-
1000
- $index_user_fields = get_option('relevanssi_index_user_fields');
1001
- $serialize_options['relevanssi_index_user_fields'] = $index_user_fields;
1002
 
1003
- $index_subscribers = ('on' === get_option('relevanssi_index_subscribers') ? 'checked="checked"' : '');
1004
- $serialize_options['relevanssi_index_subscribers'] = get_option('relevanssi_index_subscribers');
 
 
1005
 
1006
- $index_synonyms = ('on' === get_option('relevanssi_index_synonyms') ? 'checked="checked"' : '');
1007
- $serialize_options['relevanssi_index_synonyms'] = get_option('relevanssi_index_synonyms');
 
 
 
1008
 
1009
- $index_taxonomies = ('on' === get_option('relevanssi_index_taxonomies') ? 'checked="checked"' : '');
1010
- $serialize_options['relevanssi_index_taxonomies'] = get_option('relevanssi_index_taxonomies');
 
 
 
 
 
 
 
 
 
 
 
 
1011
 
1012
- $index_terms = get_option('relevanssi_index_terms');
1013
- if (empty($index_terms)) $index_terms = array();
1014
- $serialize_options['relevanssi_index_terms'] = $index_terms;
 
 
1015
 
1016
- $hide_post_controls = ('on' === get_option('relevanssi_hide_post_controls') ? 'checked="checked"' : '');
1017
- $serialize_options['relevanssi_hide_post_controls'] = get_option('relevanssi_hide_post_controls');
1018
- $show_post_controls = ('on' === get_option('relevanssi_show_post_controls') ? 'checked="checked"' : '');
1019
- $serialize_options['relevanssi_show_post_controls'] = get_option('relevanssi_show_post_controls');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1020
 
1021
- $recency_bonus_array = get_option('relevanssi_recency_bonus');
1022
- $serialize_options['recency_bonus'] = $recency_bonus_array;
1023
- $recency_bonus = $recency_bonus_array['bonus'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1024
  $recency_bonus_days = $recency_bonus_array['days'];
1025
 
1026
- $mysql_columns = get_option('relevanssi_mysql_columns');
1027
- $serialize_options['relevanssi_mysql_columns'] = $mysql_columns;
1028
-
1029
- $index_pdf_parent = ('on' === get_option('relevanssi_index_pdf_parent') ? 'checked="checked"' : '');
1030
- $serialize_options['relevanssi_index_pdf_parent'] = get_option('relevanssi_index_pdf_parent');
1031
-
1032
- $serialize_options['relevanssi_send_pdf_files'] = get_option('relevanssi_send_pdf_files');
1033
- $serialize_options['relevanssi_read_new_files'] = get_option('relevanssi_read_new_files');
1034
- $serialize_options['relevanssi_link_pdf_files'] = get_option('relevanssi_link_pdf_files');
1035
-
1036
- $serialized_options = json_encode($serialize_options);
1037
  }
1038
 
1039
  echo "<div class='postbox-container'>";
1040
-
1041
- $this_page = "?page=" . plugin_basename($relevanssi_variables['file']);
1042
  echo "<form method='post'>";
1043
-
1044
- wp_nonce_field(plugin_basename($relevanssi_variables['file']), 'relevanssi_options');
1045
 
1046
  $display_save_button = true;
1047
 
1048
- $active_tab = "overview";
1049
- if( isset( $_REQUEST[ 'tab' ] ) ) {
1050
- $active_tab = $_REQUEST[ 'tab' ];
1051
- } // end if
1052
 
1053
- if ($active_tab === "stopwords") $display_save_button = false;
 
 
1054
 
1055
- echo "<input type='hidden' name='tab' value='$active_tab' />";
1056
 
 
1057
  ?>
1058
 
1059
  <h2 class="nav-tab-wrapper">
1060
- <a href="<?php echo $this_page; ?>&amp;tab=overview" class="nav-tab <?php echo $active_tab === 'overview' ? 'nav-tab-active' : ''; ?>"><?php _e('Overview', 'relevanssi'); ?></a>
1061
- <a href="<?php echo $this_page; ?>&amp;tab=indexing" class="nav-tab <?php echo $active_tab === 'indexing' ? 'nav-tab-active' : ''; ?>"><?php _e('Indexing', 'relevanssi'); ?></a>
1062
- <a href="<?php echo $this_page; ?>&amp;tab=attachments" class="nav-tab <?php echo $active_tab === 'attachments' ? 'nav-tab-active' : ''; ?>"><?php _e('Attachments', 'relevanssi'); ?></a>
1063
- <a href="<?php echo $this_page; ?>&amp;tab=searching" class="nav-tab <?php echo $active_tab === 'searching' ? 'nav-tab-active' : ''; ?>"><?php _e('Searching', 'relevanssi'); ?></a>
1064
- <a href="<?php echo $this_page; ?>&amp;tab=logging" class="nav-tab <?php echo $active_tab === 'logging' ? 'nav-tab-active' : ''; ?>"><?php _e('Logging', 'relevanssi'); ?></a>
1065
- <a href="<?php echo $this_page; ?>&amp;tab=excerpts" class="nav-tab <?php echo $active_tab === 'excerpts' ? 'nav-tab-active' : ''; ?>"><?php _e('Excerpts and highlights', 'relevanssi'); ?></a>
1066
- <a href="<?php echo $this_page; ?>&amp;tab=synonyms" class="nav-tab <?php echo $active_tab === 'synonyms' ? 'nav-tab-active' : ''; ?>"><?php _e('Synonyms', 'relevanssi'); ?></a>
1067
- <a href="<?php echo $this_page; ?>&amp;tab=stopwords" class="nav-tab <?php echo $active_tab === 'stopwords' ? 'nav-tab-active' : ''; ?>"><?php _e('Stopwords', 'relevanssi'); ?></a>
1068
- <?php if (function_exists('relevanssi_form_importexport')) : ?>
1069
- <a href="<?php echo $this_page; ?>&amp;tab=importexport" class="nav-tab <?php echo $active_tab === 'importexport' ? 'nav-tab-active' : ''; ?>"><?php _e('Import / Export options', 'relevanssi'); ?></a>
1070
  <?php endif; ?>
1071
  </h2>
1072
 
1073
- <?php /*
1074
- <p><a href="#basic"><?php _e("Basic options", "relevanssi"); ?></a> |
1075
- <a href="#weights"><?php _e("Weights", "relevanssi"); ?></a> |
1076
- <a href="#logs"><?php _e("Logs", "relevanssi"); ?></a> |
1077
- <a href="#exclusions"><?php _e("Exclusions and restrictions", "relevanssi"); ?></a> |
1078
- <a href="#excerpts"><?php _e("Custom excerpts", "relevanssi"); ?></a> |
1079
- <a href="#highlighting"><?php _e("Highlighting search results", "relevanssi"); ?></a> |
1080
- <a href="#indexing"><?php _e("Indexing options", "relevanssi"); ?></a> |
1081
- <a href="#synonyms"><?php _e("Synonyms", "relevanssi"); ?></a> |
1082
- <a href="#stopwords"><?php _e("Stopwords", "relevanssi"); ?></a> |
1083
  <?php
1084
- if (RELEVANSSI_PREMIUM) {
1085
- echo '<a href="#options">' . __("Import/export options", "relevanssi") . '</a>';
1086
- }
1087
- else {
1088
- echo '<strong><a href="https://www.relevanssi.com/buy-premium/?utm_source=plugin&utm_medium=link&utm_campaign=buy">' . __('Buy Relevanssi Premium', 'relevanssi') . '</a></strong>';
1089
- }
1090
  ?>
1091
- </p>
1092
- */ ?>
1093
-
1094
- <?php
1095
- if ($active_tab === "overview") :
1096
- if (!RELEVANSSI_PREMIUM) $display_save_button = false;
1097
- ?>
1098
 
1099
- <h2><?php _e("Welcome to Relevanssi!", "relevanssi"); ?></h2>
1100
 
1101
  <table class="form-table">
1102
  <?php
1103
- if (!is_multisite() && function_exists('relevanssi_form_api_key')) relevanssi_form_api_key($api_key);
1104
- ?>
1105
- <?php
1106
- if (function_exists('relevanssi_form_hide_post_controls')) relevanssi_form_hide_post_controls($hide_post_controls, $show_post_controls);
 
 
1107
  ?>
1108
  <tr>
1109
- <th scope="row"><?php _e("Getting started", "relevanssi"); ?></th>
1110
  <td>
1111
- <p><?php _e("You've already installed Relevanssi. That's a great first step towards good search experience!", "relevanssi"); ?></p>
1112
  <ol>
1113
- <?php if (get_option('relevanssi_indexed') !== 'done') : ?>
1114
- <li><p><?php printf(__("Now, you need an index. Head over to the %s%s%s tab to set up the basic indexing options and to build the index.", "relevanssi"), "<a href='{$this_page}&amp;tab=indexing'>", __("Indexing", "relevanssi"), "</a>"); ?></p>
1115
- <p><?php _e("You need to check at least the following options:", "relevanssi"); ?><br />
1116
- – <?php _e("Make sure the post types you want to include in the index are indexed.", "relevanssi"); ?><br />
1117
- – <?php printf(__("Do you use custom fields to store content you want included? If so, add those too. WooCommerce user? You probably want to include %s.", "relevanssi"), "<code>_sku</code>"); ?></p>
1118
- <p><?php _e("Then just save the options and build the index. First time you have to do it manually, but after that, it's fully automatic: all changes are reflected in the index without reindexing. (That said, it's a good idea to rebuild the index once a year.)", "relevanssi"); ?></p>
 
 
1119
  </li>
1120
  <?php else : ?>
1121
- <li><p><?php _e("Great, you already have an index!", "relevanssi"); ?></p></li>
1122
  <?php endif; ?>
1123
  <li>
1124
- <p><?php printf(__("On the %s%s%s tab, choose whether you want the default operator to be AND (less results, but more precise) or OR (more results, less precise).", "relevanssi"), "<a href='{$this_page}&amp;tab=searching'>", __("Searching", "relevanssi"), "</a>"); ?></p>
 
1125
  </li>
1126
  <li>
1127
- <p><?php printf(__("The next step is the %s%s%s tab, where you can enable the custom excerpts that show the relevant part of post in the search results pages.", "relevanssi"), "<a href='{$this_page}&amp;tab=excerpts'>", __("Excerpts and highlights", "relevanssi"), "</a>"); ?></p>
1128
- <p><?php _e("There are couple of options related to that, so if you want highlighting in the results, you can adjust the styles for that to suit the look of your site.", "relevanssi"); ?></p>
 
1129
  </li>
1130
  <li>
1131
- <p><?php _e("That's about it! Now you should have Relevanssi up and running. The rest of the options is mostly fine-tuning.", "relevanssi"); ?></p>
1132
  </li>
1133
  </ol>
1134
- <p><?php _e("Relevanssi doesn't have a separate search widget. Instead, Relevanssi uses the default search widget. Any standard search form will do!", "relevanssi"); ?></p>
1135
  </td>
1136
  </tr>
1137
  <tr>
1138
- <th scope="row"><?php _e("For more information", "relevanssi"); ?></th>
1139
  <td>
1140
- <p><?php _e("Relevanssi uses the WordPress contextual help. Click 'Help' on the top right corner for more information on many Relevanssi topics.", "relevanssi"); ?></p>
1141
- <p><?php printf(__("%sRelevanssi knowledge base%s has lots of information about advanced Relevanssi use, including plenty of code samples.", "relevanssi"), "<a href='https://www.relevanssi.com/knowledge-base/'>", "</a>"); ?></p>
 
1142
  </td>
1143
  </tr>
1144
  <tr>
1145
  <th scope="row">
1146
- <?php _e('Relevanssi on Facebook', 'relevanssi');?>
1147
  </th>
1148
  <td>
1149
- <p><a href="http://www.facebook.com/relevanssi"><?php _e('Check out the Relevanssi page on Facebook for news and updates about Relevanssi.', 'relevanssi'); ?></a></p>
1150
  </td>
1151
  </tr>
1152
- <?php if (!RELEVANSSI_PREMIUM) : ?>
1153
  <tr>
1154
  <th scope="row">
1155
- <?php _e('Buy Relevanssi Premium', 'relevanssi');?>
1156
  </th>
1157
  <td>
1158
- <p><a href="https://www.relevanssi.com/buy-premium"><?php _e('Buy Relevanssi Premium now', 'relevanssi'); ?></a> – <?php printf(__("use coupon code %s for 20%% discount (valid at least until the end of %s)", "relevanssi"), "<strong>FREE2018</strong>", "2018"); ?></p>
1159
- <p><?php _e("Here are some improvements Relevanssi Premium offers:", "relevanssi"); ?></p>
 
 
1160
  <ul class="relevanssi_ul">
1161
- <li><?php _e("PDF content indexing", "relevanssi"); ?></li>
1162
- <li><?php _e("Index and search user profile pages", "relevanssi"); ?></li>
1163
- <li><?php _e("Index and search taxonomy term pages", "relevanssi"); ?></li>
1164
- <li><?php _e("Multisite searches across many subsites", "relevanssi"); ?></li>
1165
- <li><?php _e("WP CLI commands", "relevanssi"); ?></li>
1166
- <li><?php _e("Adjust weights separately for each post type and taxonomy", "relevanssi"); ?></li>
1167
- <li><?php _e("Internal link anchors can be search terms for the target posts", "relevanssi"); ?></li>
1168
- <li><?php _e("Index and search any columns in the wp_posts database", "relevanssi"); ?></li>
1169
- <li><?php _e("Hide Relevanssi branding from the User Searches page on a client installation", "relevanssi"); ?></li>
1170
  </ul>
1171
  </td>
1172
  </tr>
1173
- <?php endif; ?>
1174
  </table>
1175
 
1176
- <?php endif; // active tab: basic ?>
1177
 
1178
- <?php if ($active_tab === "logging") : ?>
1179
 
1180
  <table class="form-table">
1181
  <tr>
1182
  <th scope="row">
1183
- <label for='relevanssi_log_queries'><?php _e("Enable logs", "relevanssi"); ?></label>
1184
  </th>
1185
  <td>
1186
  <fieldset>
1187
- <legend class="screen-reader-text"><?php _e("Keep a log of user queries.", "relevanssi"); ?></legend>
1188
  <label for='relevanssi_log_queries'>
1189
- <input type='checkbox' name='relevanssi_log_queries' id='relevanssi_log_queries' <?php echo $log_queries ?> />
1190
- <?php _e("Keep a log of user queries.", "relevanssi"); ?>
1191
  </label>
1192
  </fieldset>
1193
- <p class="description"><?php global $wpdb; printf(__("If enabled, Relevanssi will log user queries. The logs can be examined under '%s' on the Dashboard admin menu and are stored in the %s database table.", "relevanssi"), __('User searches', 'relevanssi'), $wpdb->prefix . 'relevanssi_log'); ?></p>
 
 
 
 
 
 
 
1194
  </td>
1195
  </tr>
1196
  <tr>
1197
  <th scope="row">
1198
- <label for='relevanssi_log_queries_with_ip'><?php _e("Log user IP", "relevanssi"); ?></label>
1199
  </th>
1200
  <td>
1201
  <fieldset>
1202
- <legend class="screen-reader-text"><?php _e("Log the user's IP with the queries.", "relevanssi"); ?></legend>
1203
  <label for='relevanssi_log_queries_with_ip'>
1204
- <input type='checkbox' name='relevanssi_log_queries_with_ip' id='relevanssi_log_queries_with_ip' <?php echo $log_queries_with_ip ?> />
1205
- <?php _e("Log the user's IP with the queries.", "relevanssi"); ?>
1206
  </label>
1207
  </fieldset>
1208
- <p class="description"><?php _e("If enabled, Relevanssi will log user's IP adress with the queries.", "relevanssi"); ?></p>
1209
  </td>
1210
  </tr>
1211
  <tr>
1212
  <th scope="row">
1213
- <label for='relevanssi_omit_from_logs'><?php _e("Exclude users", "relevanssi"); ?></label>
1214
  </th>
1215
  <td>
1216
- <input type='text' name='relevanssi_omit_from_logs' id='relevanssi_omit_from_logs' size='60' value='<?php echo esc_attr($omit_from_logs); ?>' />
1217
- <p class="description"><?php _e("Comma-separated list of numeric user IDs or user login names that will not be logged.", "relevanssi"); ?></p>
1218
  </td>
1219
  </tr>
1220
- <?php if (function_exists('relevanssi_form_hide_branding')) relevanssi_form_hide_branding($hide_branding); ?>
 
 
 
 
1221
  <tr>
1222
  <th scope="row">
1223
- <label for='relevanssi_trim_logs'><?php _e("Trim logs", "relevanssi"); ?></label>
1224
  </th>
1225
  <td>
1226
- <input type='number' name='relevanssi_trim_logs' id='relevanssi_trim_logs' value='<?php echo $trim_logs; ?>' />
1227
- <?php _e("How many days of logs to keep in the database.", "relevanssi"); ?>
1228
- <p class="description"><?php printf(__(" Set to %d for no trimming.", "relevanssi"), 0); ?></p>
 
1229
  </td>
1230
  </tr>
1231
 
1232
  </table>
1233
 
1234
- <?php endif; // active tag: logging ?>
1235
 
1236
- <?php if ($active_tab === "searching") :
1237
- $docs_count = $wpdb->get_var("SELECT COUNT(DISTINCT doc) FROM " . $relevanssi_variables['relevanssi_table'] . " WHERE doc != -1");
 
1238
  ?>
1239
 
1240
  <table class="form-table">
1241
  <tr>
1242
  <th scope="row">
1243
- <label for='relevanssi_implicit_operator'><?php _e("Default operator", "relevanssi"); ?></label>
1244
  </th>
1245
  <td>
1246
  <select name='relevanssi_implicit_operator' id='relevanssi_implicit_operator'>
1247
- <option value='AND' <?php echo $implicit_and ?>><?php _e("AND - require all terms", "relevanssi"); ?></option>
1248
- <option value='OR' <?php echo $implicit_or ?>><?php _e("OR - any term present is enough", "relevanssi"); ?></option>
1249
  </select>
1250
- <p class="description"><?php _e("This setting determines the default operator for the search.", "relevanssi"); ?></p>
1251
- <?php if (RELEVANSSI_PREMIUM) echo "<p class='description'>" . sprintf(__("You can override this setting with the %s query parameter, like this: %s", "relevanssi"), "<code>operator</code>", "http://www.example.com/?s=term&amp;operator=or") . "</p>"; ?>
 
 
 
 
 
1252
  </td>
1253
  </tr>
1254
- <tr id="orfallback" <?php echo $orfallback_visibility; ?>>
1255
  <th scope="row">
1256
- <label for='relevanssi_disable_or_fallback'><?php _e("Fallback to OR", "relevanssi"); ?></label>
1257
  </th>
1258
  <td>
1259
  <fieldset>
1260
- <legend class="screen-reader-text"><?php _e("Disable the OR fallback.", "relevanssi"); ?></legend>
1261
  <label for='relevanssi_disable_or_fallback'>
1262
- <input type='checkbox' name='relevanssi_disable_or_fallback' id='relevanssi_disable_or_fallback' <?php echo $disablefallback ?> />
1263
- <?php _e("Disable the OR fallback.", "relevanssi"); ?>
1264
  </label>
1265
  </fieldset>
1266
- <p class="description"><?php _e("By default, if AND search fails to find any results, Relevanssi will switch the operator to OR and run the search again. You can prevent that by checking this option.", "relevanssi"); ?></p>
1267
  </td>
1268
  </tr>
1269
  <tr>
1270
  <th scope="row">
1271
- <label for='relevanssi_default_orderby'><?php _e("Default order", "relevanssi"); ?></label>
1272
  </th>
1273
  <td>
1274
  <select name='relevanssi_default_orderby' id='relevanssi_default_orderby'>
1275
- <option value='relevance' <?php echo $orderby_relevance ?>><?php _e("Relevance (highly recommended)", "relevanssi"); ?></option>
1276
- <option value='post_date' <?php echo $orderby_date ?>><?php _e("Post date", "relevanssi"); ?></option>
1277
  </select>
1278
- <p class="description"><?php printf(__("If you want to override this or use multi-layered ordering (eg. first order by relevance, but sort ties by post title), you can use the %s query variable. See Help for more information.", "relevanssi"), "<code>orderby</code>"); ?></p>
1279
- <?php if (RELEVANSSI_PREMIUM) : ?>
1280
- <p class="description"><?php _e(" If you want date-based results, see the recent post bonus in the Weights section.", "relevanssi"); ?></p>
1281
- <?php endif; ?>
 
1282
  </td>
1283
  </tr>
1284
  <tr>
1285
  <th scope="row">
1286
- <label for='relevanssi_fuzzy'><?php _e("Keyword matching", "relevanssi"); ?></label>
1287
  </th>
1288
  <td>
1289
  <select name='relevanssi_fuzzy' id='relevanssi_fuzzy'>
1290
- <option value='never' <?php echo $fuzzy_never ?>><?php _e("Whole words", "relevanssi"); ?></option>
1291
- <option value='always' <?php echo $fuzzy_always ?>><?php _e("Partial words", "relevanssi"); ?></option>
1292
- <option value='sometimes' <?php echo $fuzzy_sometimes ?>><?php _e("Partial words if no hits for whole words", "relevanssi"); ?></option>
1293
  </select>
1294
- <p class="description"><?php _e("Whole words means Relevanssi only finds posts that include the whole search term.", "relevanssi"); ?></p>
1295
- <p class="description"><?php _e("Partial words also includes cases where the word in the index begins or ends with the search term (searching for 'ana' will match 'anaconda' or 'banana', but not 'banal'). See Help, if you want to make Relevanssi match also inside words.", "relevanssi"); ?></p>
1296
  </td>
1297
  </tr>
1298
  <tr>
1299
  <th scope="row">
1300
- <?php _e("Weights", "relevanssi"); ?>
1301
  </th>
1302
  <td>
1303
- <p class="description"><?php _e("All the weights in the table are multipliers. To increase the weight of an element, use a higher number. To make an element less significant, use a number lower than 1.", "relevanssi"); ?></p>
1304
  <table class="relevanssi-weights-table">
1305
  <thead>
1306
  <tr>
1307
- <th><?php _e('Element', 'relevanssi'); ?></th>
1308
- <th class="col-2"><?php _e('Weight', 'relevanssi'); ?></th>
1309
  </tr>
1310
  </thead>
1311
  <tr>
1312
  <td>
1313
- <?php _e('Content', 'relevanssi'); ?>
1314
  </td>
1315
  <td class="col-2">
1316
- <input type='text' name='relevanssi_content_boost' id='relevanssi_content_boost' size='4' value='<?php echo $content_boost ?>' />
1317
  </td>
1318
  </tr>
1319
  <tr>
1320
  <td>
1321
- <?php _e('Titles', 'relevanssi'); ?>
1322
  </td>
1323
  <td class="col-2">
1324
- <input type='text' name='relevanssi_title_boost' id='relevanssi_title_boost' size='4' value='<?php echo $title_boost ?>' />
1325
  </td>
1326
  </tr>
1327
- <?php if (function_exists('relevanssi_form_link_weight')) relevanssi_form_link_weight($link_boost); ?>
 
 
 
 
1328
  <tr>
1329
  <td>
1330
- <?php _e('Comment text', 'relevanssi'); ?>
1331
  </td>
1332
  <td class="col-2">
1333
- <input type='text' name='relevanssi_comment_boost' id='relevanssi_comment_boost' size='4' value='<?php echo $comment_boost ?>' />
1334
  </td>
1335
  </tr>
1336
  <?php
1337
- if (function_exists('relevanssi_form_post_type_weights')) relevanssi_form_post_type_weights($post_type_weights);
1338
- if (function_exists('relevanssi_form_taxonomy_weights')) relevanssi_form_taxonomy_weights($post_type_weights);
1339
- if (function_exists('relevanssi_form_tag_weight')) relevanssi_form_tag_weight($post_type_weights);
1340
- if (function_exists('relevanssi_form_recency_weight')) relevanssi_form_recency_weight($recency_bonus);
 
 
 
 
 
 
 
 
1341
  ?>
1342
  </table>
1343
  </td>
1344
  </tr>
1345
- <?php if (function_exists('relevanssi_form_recency_cutoff')) relevanssi_form_recency_cutoff($recency_bonus_days); ?>
1346
-
1347
- <?php if (function_exists('icl_object_id') && !function_exists('pll_get_post')) : ?>
 
 
1348
  <tr>
1349
  <th scope="row">
1350
- <label for='relevanssi_wpml_only_current'><?php _e("WPML", "relevanssi"); ?></label>
1351
  </th>
1352
  <td>
1353
  <fieldset>
1354
- <legend class="screen-reader-text"><?php _e("Limit results to current language.", "relevanssi"); ?></legend>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1355
  <label for='relevanssi_wpml_only_current'>
1356
- <input type='checkbox' name='relevanssi_wpml_only_current' id='relevanssi_wpml_only_current' <?php echo $wpml_only_current ?> />
1357
- <?php _e("Limit results to current language.", "relevanssi"); ?>
1358
  </label>
1359
  </fieldset>
1360
- <p class="description"><?php _e("Enabling this option will restrict the results to the currently active language. If the option is disabled, results will include posts in all languages.", "relevanssi"); ?></p>
1361
  </td>
1362
  </tr>
1363
- <?php endif; ?>
1364
- <?php if (function_exists('pll_get_post')) : ?>
1365
  <tr>
1366
  <th scope="row">
1367
- <label for='relevanssi_polylang_all_languages'><?php _e("Polylang", "relevanssi"); ?></label>
1368
  </th>
1369
  <td>
1370
  <fieldset>
1371
- <legend class="screen-reader-text"><?php _e("Allow results from all languages.", "relevanssi"); ?></legend>
1372
  <label for='relevanssi_polylang_all_languages'>
1373
- <input type='checkbox' name='relevanssi_polylang_all_languages' id='relevanssi_polylang_all_languages' <?php echo $polylang_allow_all ?> />
1374
- <?php _e("Allow results from all languages.", "relevanssi"); ?>
1375
  </label>
1376
  </fieldset>
1377
- <p class="description"><?php _e("By default Polylang restricts the search to the current language. Enabling this option will lift this restriction.", "relevanssi"); ?></p>
1378
  </td>
1379
  </tr>
1380
- <?php endif; ?>
1381
  <tr>
1382
  <th scope="row">
1383
- <label for='relevanssi_admin_search'><?php _e("Admin search", "relevanssi"); ?></label>
1384
  </th>
1385
  <td>
1386
  <fieldset>
1387
- <legend class="screen-reader-text"><?php _e("Use Relevanssi for admin searches.", "relevanssi"); ?></legend>
1388
  <label for='relevanssi_admin_search'>
1389
- <input type='checkbox' name='relevanssi_admin_search' id='relevanssi_admin_search' <?php echo $admin_search ?> />
1390
- <?php _e("Use Relevanssi for admin searches.", "relevanssi"); ?>
1391
  </label>
1392
  </fieldset>
1393
- <p class="description"><?php _e("If checked, Relevanssi will be used for searches in the admin interface. The page search doesn't use Relevanssi, because WordPress works like that.", "relevanssi"); ?></p>
1394
  </td>
1395
  </tr>
1396
  <tr>
1397
  <th scope="row">
1398
- <label for='relevanssi_respect_exclude'><?php printf(__('Respect %s', 'relevanssi'), 'exclude_from_search' ); ?></label>
 
1399
  </th>
1400
  <td>
1401
  <fieldset>
1402
- <legend class="screen-reader-text"><?php _e("Respect exclude_from_search for custom post types", "relevanssi"); ?></legend>
1403
  <label for='relevanssi_respect_exclude'>
1404
- <input type='checkbox' name='relevanssi_respect_exclude' id='relevanssi_respect_exclude' <?php echo $respect_exclude ?> />
1405
- <?php printf(__("Respect %s for custom post types", "relevanssi"), '<code>exclude_from_search</code>' ); ?>
 
1406
  </label>
1407
- <p class="description"><?php _e("If checked, Relevanssi won't display posts of custom post types that have 'exclude_from_search' set to true.", 'relevanssi'); ?></p>
1408
  <?php
1409
- if (!empty($respect_exclude)) {
1410
- $pt_1 = get_post_types(array('exclude_from_search' => '1'));
1411
- $pt_2 = get_post_types(array('exclude_from_search' => true));
1412
- $private_types = array_merge($pt_1, $pt_2);
1413
- $problem_post_types = array_intersect($index_post_types, $private_types);
1414
- if (!empty($problem_post_types)) : ?>
1415
- <p class="description important"><?php _e("You probably should uncheck this option, because you've set Relevanssi to index the following non-public post types:", "relevanssi"); echo " " . implode(", ", $problem_post_types); ?></p>
1416
- <?php endif;
 
 
 
 
 
 
 
1417
  }
 
1418
  ?>
1419
  </fieldset>
1420
  </td>
1421
  </tr>
1422
  <tr>
1423
  <th scope="row">
1424
- <label for='relevanssi_throttle'><?php _e("Throttle searches", "relevanssi"); ?></label>
1425
  </th>
1426
  <td id="throttlesearches">
1427
- <div id="throttle_disabled" <?php if (!$orderby_date) echo "class='screen-reader-text'" ?>>
1428
- <p class="description"><?php _e("Throttling the search does not work when sorting the posts by date.", 'relevanssi'); ?></p>
 
 
 
 
 
 
1429
  </div>
1430
- <div id="throttle_enabled" <?php if (!$orderby_relevance) echo "class='screen-reader-text'" ?>>
 
 
 
 
 
 
1431
  <fieldset>
1432
- <legend class="screen-reader-text"><?php _e("Throttle searches.", "relevanssi"); ?></legend>
1433
  <label for='relevanssi_throttle'>
1434
- <input type='checkbox' name='relevanssi_throttle' id='relevanssi_throttle' <?php echo $throttle ?> />
1435
- <?php _e("Throttle searches.", "relevanssi"); ?>
1436
  </label>
1437
  </fieldset>
1438
- <?php if ($docs_count < 1000) : ?>
1439
- <p class="description important"><?php _e("Your database is so small that you don't need to enable this.", 'relevanssi'); ?></p>
1440
- <?php endif; ?>
1441
- <p class="description"><?php _e("If this option is checked, Relevanssi will limit search results to at most 500 results per term. This will improve performance, but may cause some relevant documents to go unfound. See Help for more details.", 'relevanssi'); ?></p>
1442
  </div>
1443
  </td>
1444
  </tr>
1445
  <tr>
1446
  <th scope="row">
1447
- <label for='relevanssi_cat'><?php _e('Category restriction', 'relevanssi'); ?></label>
1448
  </th>
1449
  <td>
1450
  <div class="categorydiv" style="max-width: 400px">
1451
  <div class="tabs-panel">
1452
  <ul id="categorychecklist">
1453
- <?php
1454
- $selected_cats = explode(',', $cat);
1455
- $walker = get_Relevanssi_Taxonomy_Walker();
1456
- $walker->name = "relevanssi_cat";
1457
- wp_terms_checklist(0, array('taxonomy' => 'category', 'selected_cats' => $selected_cats, 'walker' => $walker));
 
 
 
 
1458
  ?>
1459
  </ul>
1460
  <input type="hidden" name="relevanssi_cat_active" value="1" />
1461
  </div>
1462
  </div>
1463
- <p class="description"><?php _e("You can restrict search results to a category for all searches. For restricting on a per-search basis and more options (eg. tag restrictions), see Help.", 'relevanssi'); ?></p>
1464
  </td>
1465
  </tr>
1466
  <tr>
1467
  <th scope="row">
1468
- <label for='relevanssi_excat'><?php _e('Category exclusion', 'relevanssi'); ?></label>
1469
  </th>
1470
  <td>
1471
  <div class="categorydiv" style="max-width: 400px">
1472
  <div class="tabs-panel">
1473
  <ul id="categorychecklist">
1474
- <?php
1475
- $selected_cats = explode(',', $excat);
1476
- $walker = get_Relevanssi_Taxonomy_Walker();
1477
- $walker->name = "relevanssi_excat";
1478
- wp_terms_checklist(0, array('taxonomy' => 'category', 'selected_cats' => $selected_cats, 'walker' => $walker));
 
 
 
 
1479
  ?>
1480
  </ul>
1481
  <input type="hidden" name="relevanssi_excat_active" value="1" />
1482
  </div>
1483
  </div>
1484
- <p class="description"><?php _e("Posts in these categories are not included in search results. To exclude the posts completely from the index, see Help.", 'relevanssi'); ?></p>
1485
  </td>
1486
  </tr>
1487
  <tr>
1488
- <th scope="row">
1489
- <label for='relevanssi_expst'><?php _e('Post exclusion', 'relevanssi'); ?>
1490
  </th>
1491
  <td>
1492
- <input type='text' name='relevanssi_expst' id='relevanssi_expst' size='60' value='<?php echo esc_attr($expst); ?>' />
1493
- <p class="description"><?php _e("Enter a comma-separated list of post or page ID's to exclude those pages from the search results.", 'relevanssi'); ?></p>
1494
- <?php if (RELEVANSSI_PREMIUM) : ?>
1495
- <p class="description"><?php _e("With Relevanssi Premium, it's better to use the check box on post edit pages. That will remove the posts completely from the index, and will work with multisite searches unlike this setting.", "relevanssi"); ?></p>
1496
- <?php endif; ?>
1497
  </td>
1498
  </tr>
 
 
 
 
 
1499
  </table>
1500
 
1501
- <?php endif; // active tab: searching ?>
1502
 
1503
- <?php if ($active_tab === "excerpts") : ?>
1504
 
1505
- <h2 id="excerpts"><?php _e("Custom excerpts/snippets", "relevanssi"); ?></h2>
1506
 
1507
  <table class="form-table">
1508
  <tr>
1509
  <th scope="row">
1510
- <label for='relevanssi_excerpts'><?php _e("Custom search result snippets", "relevanssi"); ?></label>
1511
  </th>
1512
  <td>
1513
  <fieldset>
1514
- <legend class="screen-reader-text"><?php _e("Create custom search result snippets", "relevanssi"); ?></legend>
1515
  <label >
1516
- <input type='checkbox' name='relevanssi_excerpts' id='relevanssi_excerpts' <?php echo $excerpts ?> />
1517
- <?php _e("Create custom search result snippets", "relevanssi"); ?>
1518
  </label>
1519
  </fieldset>
1520
- <p class="description"><?php _e("Only enable this if you actually use the custom excerpts.", "relevanssi"); ?></p>
1521
  </td>
1522
  </tr>
1523
- <tr id="tr_excerpt_length" <?php if (empty($excerpts)) echo "class='relevanssi_disabled'" ?>>
 
 
 
 
 
 
1524
  <th scope="row">
1525
- <label for='relevanssi_excerpt_length'><?php _e("Length of the snippet", "relevanssi"); ?></label>
1526
  </th>
1527
  <td>
1528
- <input type='text' name='relevanssi_excerpt_length' id='relevanssi_excerpt_length' size='4' value='<?php echo esc_attr($excerpt_length); ?>' <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1529
- <select name='relevanssi_excerpt_type' id='relevanssi_excerpt_type' <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>>
1530
- <option value='chars' <?php echo $excerpt_chars ?>><?php _e("characters", "relevanssi"); ?></option>
1531
- <option value='words' <?php echo $excerpt_words ?>><?php _e("words", "relevanssi"); ?></option>
 
 
 
 
 
 
 
 
 
 
 
 
1532
  </select>
1533
- <p class="description"><?php _e("Using words is much faster than characters. Don't use characters, unless you have a really good reason and your posts are short.", "relevanssi"); ?></p>
1534
  </td>
1535
  </tr>
1536
- <tr id="tr_excerpt_allowable_tags" <?php if (empty($excerpts)) echo "class='relevanssi_disabled'" ?>>
 
 
 
 
 
 
1537
  <th scope="row">
1538
- <label for='relevanssi_excerpt_allowable_tags'><?php _e("Allowable tags in excerpts", "relevanssi"); ?></label>
1539
  </th>
1540
  <td>
1541
- <input type='text' name='relevanssi_excerpt_allowable_tags' id='relevanssi_excerpt_allowable_tags' size='60' value='<?php echo esc_attr($excerpt_allowable_tags); ?>' <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1542
- <p class="description"><?php _e("List all tags you want to allow in excerpts. For example: &lt;p&gt;&lt;a&gt;&lt;strong&gt;.", "relevanssi"); ?></p>
 
 
 
 
 
 
1543
  </td>
1544
  </tr>
1545
- <tr id="tr_excerpt_custom_fields" <?php if (empty($excerpts)) echo "class='relevanssi_disabled'"; ?>>
 
 
 
 
 
 
1546
  <th scope="row">
1547
- <label for='relevanssi_excerpt_custom_fields'><?php _e("Use custom fields for excerpts", "relevanssi"); ?></label>
1548
  </th>
1549
  <td>
1550
  <fieldset>
1551
- <legend class="screen-reader-text"><?php _e("Use custom field content for building excerpts", "relevanssi"); ?></legend>
1552
  <label>
1553
- <input type='checkbox' name='relevanssi_excerpt_custom_fields' id='relevanssi_excerpt_custom_fields' <?php echo $excerpt_custom_fields ?> <?php if (empty($excerpts) || empty($original_index_fields)) echo "disabled='disabled'"; ?>/>
1554
- <?php _e("Use custom field content for building excerpts", "relevanssi"); ?>
 
 
 
 
 
 
1555
  </label>
1556
  </fieldset>
1557
- <p class="description"><?php _e("Use the custom fields setting for indexing for excerpt-making as well. Enabling this option will show custom field content in Relevanssi-generated excerpts.", "relevanssi"); ?>
1558
- <?php if (RELEVANSSI_PREMIUM) { _e("Enable this option to use PDF content for excerpts.", "relevanssi"); } ?>
 
 
 
 
1559
  </p>
1560
 
1561
- <p class="description"><?php _e("Current custom field setting", 'relevanssi'); ?>:
1562
  <?php
1563
- if ($original_index_fields === "visible") _e("all visible custom fields", 'relevanssi');
1564
- else if ($original_index_fields === "all") _e("all custom fields", 'relevanssi');
1565
- else if (!empty($original_index_fields)) echo "<code>$original_index_fields</code>";
1566
- else if (RELEVANSSI_PREMIUM) { _e('Just PDF content', 'relevanssi'); } else { _e('None selected', 'relevanssi'); }
1567
- ?></p>
 
 
 
 
 
 
 
 
1568
  </td>
1569
  </tr>
1570
  </table>
1571
 
1572
- <h2><?php _e("Search hit highlighting", "relevanssi"); ?></h2>
1573
 
1574
- <table id="relevanssi_highlighting" class="form-table <?php if (empty($excerpts)) echo "relevanssi_disabled" ?>">
 
 
 
 
 
 
1575
  <tr>
1576
  <th scope="row">
1577
- <label for='relevanssi_highlight'><?php _e("Highlight type", 'relevanssi'); ?></label>
1578
  </th>
1579
  <td>
1580
- <select name='relevanssi_highlight' id='relevanssi_highlight' <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>>
1581
- <option value='no' <?php echo $highlight_none ?>><?php _e('No highlighting', 'relevanssi'); ?></option>
1582
- <option value='mark' <?php echo $highlight_mark ?>>&lt;mark&gt;</option>
1583
- <option value='em' <?php echo $highlight_em ?>>&lt;em&gt;</option>
1584
- <option value='strong' <?php echo $highlight_strong ?>>&lt;strong&gt;</option>
1585
- <option value='col' <?php echo $highlight_col ?>><?php _e('Text color', 'relevanssi'); ?></option>
1586
- <option value='bgcol' <?php echo $highlight_bgcol ?>><?php _e('Background color', 'relevanssi'); ?></option>
1587
- <option value='css' <?php echo $highlight_style ?>><?php _e("CSS Style", 'relevanssi'); ?></option>
1588
- <option value='class' <?php echo $highlight_class ?>><?php _e("CSS Class", 'relevanssi'); ?></option>
 
 
 
 
 
 
1589
  </select>
1590
- <p class="description"><?php _e("Requires custom snippets to work.", "relevanssi"); ?></p>
1591
  </td>
1592
  </tr>
1593
- <tr id="relevanssi_txt_col" <?php echo $txt_col_display; ?>>
1594
  <th scope="row">
1595
- <label for="relevanssi_txt_col"><?php _e("Text color", "relevanssi"); ?></label>
1596
  </th>
1597
  <td>
1598
- <input type='text' name='relevanssi_txt_col' id='relevanssi_txt_col' size='7' class="color-field" data-default-color="#ff0000" value='<?php echo esc_attr($txt_col); ?>' <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
 
 
 
 
 
 
1599
  </td>
1600
  </tr>
1601
- <tr id="relevanssi_bg_col" <?php echo $bg_col_display; ?>>
1602
  <th scope="row">
1603
- <label for="relevanssi_bg_col"><?php _e("Background color", "relevanssi"); ?></label>
1604
  </th>
1605
  <td>
1606
- <input type='text' name='relevanssi_bg_col' id='relevanssi_bg_col' size='7' class="color-field" data-default-color="#ffaf75" value='<?php echo esc_attr($bg_col); ?>' <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
 
 
 
 
 
 
1607
  </td>
1608
  </tr>
1609
- <tr id="relevanssi_css" <?php echo $css_display; ?>>
1610
  <th scope="row">
1611
- <label for='relevanssi_css'><?php _e("CSS style for highlights", "relevanssi"); ?></label>
1612
  </th>
1613
  <td>
1614
- <input type='text' name='relevanssi_css' id='relevanssi_css' size='60' value='<?php echo esc_attr($css); ?>' <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1615
- <p class="description"><?php printf(__("The highlights will be wrapped in a %s with this CSS in the style parameter.", "relevanssi"), "&lt;span&gt;"); ?></p>
 
 
 
 
 
 
 
1616
  </td>
1617
  </tr>
1618
- <tr id="relevanssi_class" <?php echo $class_display; ?>>
1619
  <th scope="row">
1620
- <label for='relevanssi_class'><?php _e("CSS class for highlights", "relevanssi"); ?></label>
1621
  </th>
1622
  <td>
1623
- <input type='text' name='relevanssi_class' id='relevanssi_class' size='60' value='<?php echo esc_attr($class); ?>' <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1624
- <p class="description"><?php printf(__("The highlights will be wrapped in a %s with this class.", "relevanssi"), "&lt;span&gt;"); ?></p>
 
 
 
 
 
 
 
1625
  </td>
1626
  </tr>
1627
  <tr>
1628
  <th scope="row">
1629
- <label for='relevanssi_hilite_title'><?php _e("Highlight in titles", 'relevanssi'); ?></label>
1630
  </th>
1631
  <td>
1632
  <fieldset>
1633
- <legend class="screen-reader-text"><?php _e("Highlight query terms in titles", "relevanssi"); ?></legend>
1634
  <label for='relevanssi_hilite_title'>
1635
- <input type='checkbox' name='relevanssi_hilite_title' id='relevanssi_hilite_title' <?php echo $hititle ?> <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1636
- <?php _e("Highlight query terms in titles", "relevanssi"); ?>
 
 
 
 
 
 
1637
  </label>
1638
  </fieldset>
1639
- <p class="description"><?php printf(__("Highlights in titles require changes to the search results template. You need to replace %s in the search results template with %s. For more information, see the contextual help.", "relevanssi"), "<code>the_title()</code>", "<code>relevanssi_the_title()</code>"); ?></p>
 
1640
  </td>
1641
  </tr>
1642
  <tr>
1643
  <th scope="row">
1644
- <label for='relevanssi_highlight_docs'><?php _e("Highlight in documents", 'relevanssi'); ?></label>
1645
  </th>
1646
  <td>
1647
  <fieldset>
1648
- <legend class="screen-reader-text"><?php _e("Highlight query terms in documents", "relevanssi"); ?></legend>
1649
  <label for='relevanssi_highlight_docs'>
1650
- <input type='checkbox' name='relevanssi_highlight_docs' id='relevanssi_highlight_docs' <?php echo $highlight_docs ?> <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1651
- <?php _e("Highlight query terms in documents", "relevanssi"); ?>
 
 
 
 
 
 
1652
  </label>
1653
  </fieldset>
1654
- <p class="description"><?php printf(__("Highlights hits when user opens the post from search results. This requires an extra parameter (%s) to the links from the search results pages so in order to get these highlights, you need to use %s or %s to print out the permalinks on the search results templates.", "relevanssi"), "<code>highlight</code>", "<code>relevanssi_get_permalink()</code>", "<code>relevanssi_the_permalink()</code>"); ?></p>
 
1655
  </td>
1656
  </tr>
1657
- <?php if (function_exists('relevanssi_form_highlight_external')) relevanssi_form_highlight_external($highlight_docs_ext, $excerpts); ?>
 
 
 
 
1658
  <tr>
1659
  <th scope="row">
1660
- <label for='relevanssi_highlight_comments'><?php _e("Highlight in comments", 'relevanssi'); ?></label>
1661
  </th>
1662
  <td>
1663
  <fieldset>
1664
- <legend class="screen-reader-text"><?php _e("Highlight query terms in comments", "relevanssi"); ?></legend>
1665
  <label for='relevanssi_highlight_comments'>
1666
- <input type='checkbox' name='relevanssi_highlight_comments' id='relevanssi_highlight_comments' <?php echo $highlight_coms ?> <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1667
- <?php _e("Highlight query terms in comments", "relevanssi"); ?>
 
 
 
 
 
 
1668
  </label>
1669
  </fieldset>
1670
- <p class="description"><?php _e("Highlights hits in comments when user opens the post from search results.", "relevanssi"); ?></p>
1671
  </td>
1672
  </tr>
1673
  <tr>
1674
  <th scope="row">
1675
- <label for='relevanssi_word_boundaries'><?php _e("Highlighting problems with non-ASCII alphabet?", 'relevanssi'); ?></label>
1676
  </th>
1677
  <td>
1678
  <fieldset>
1679
- <legend class="screen-reader-text"><?php _e("Uncheck this if you use non-ASCII characters", "relevanssi"); ?></legend>
1680
  <label for='relevanssi_word_boundaries'>
1681
- <input type='checkbox' name='relevanssi_word_boundaries' id='relevanssi_word_boundaries' <?php echo $word_boundaries ?> <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1682
- <?php _e("Uncheck this if you use non-ASCII characters", "relevanssi"); ?>
 
 
 
 
 
 
1683
  </label>
1684
  </fieldset>
1685
- <p class="description"><?php _e("If you use non-ASCII characters (like Cyrillic alphabet) and the highlights don't work, unchecking this option may make the highlights work.", "relevanssi"); ?></p>
1686
  </td>
1687
  </tr>
1688
  </table>
1689
 
1690
- <h2><?php _e("Breakdown of search results", "relevanssi"); ?></h2>
1691
 
1692
- <table id="relevanssi_breakdown" class="form-table <?php if (empty($excerpts)) echo "relevanssi_disabled" ?>">
 
 
 
 
 
 
1693
  <tr>
1694
  <th scope="row">
1695
- <label for='relevanssi_show_matches'><?php _e("Breakdown of search hits in excerpts", "relevanssi"); ?></label>
1696
  </th>
1697
  <td>
1698
  <fieldset>
1699
- <legend class="screen-reader-text"><?php _e("Show the breakdown of search hits in the excerpts", "relevanssi"); ?></legend>
1700
  <label for='relevanssi_show_matches'>
1701
- <input type='checkbox' name='relevanssi_show_matches' id='relevanssi_show_matches' <?php echo $show_matches ?> <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>/>
1702
- <?php _e("Show the breakdown of search hits in the excerpts.", "relevanssi"); ?>
 
 
 
 
 
 
1703
  </label>
1704
  </fieldset>
1705
- <p class="description"><?php _e("Requires custom snippets to work.", "relevanssi"); ?></p>
1706
  </td>
1707
  </tr>
1708
  <tr>
1709
  <th scope="row">
1710
- <label for='relevanssi_show_matches_text'><?php _e("The breakdown format", "relevanssi"); ?></label>
1711
  </th>
1712
  <td>
1713
- <textarea name='relevanssi_show_matches_text' id='relevanssi_show_matches_text' cols="80" rows="4" <?php if (empty($excerpts)) echo "disabled='disabled'"; ?>><?php echo esc_attr($show_matches_text) ?></textarea>
1714
- <p class="description"><?php _e("Use %body%, %title%, %tags% and %comments% to display the number of hits (in different parts of the post), %total% for total hits, %score% to display the document weight and %terms% to show how many hits each search term got.", "relevanssi"); ?></p>
 
 
 
 
 
 
1715
  </td>
1716
  </tr>
1717
  </table>
1718
 
1719
- <?php endif; // active tab: excerpts & highlights ?>
1720
 
1721
- <?php if ($active_tab === "indexing") : ?>
1722
 
1723
  <?php
1724
-
1725
- $docs_count = $wpdb->get_var("SELECT COUNT(DISTINCT doc) FROM " . $relevanssi_variables['relevanssi_table'] . " WHERE doc != -1");
1726
- $terms_count = $wpdb->get_var("SELECT COUNT(*) FROM " . $relevanssi_variables['relevanssi_table']);
1727
- $biggest_doc = $wpdb->get_var("SELECT doc FROM " . $relevanssi_variables['relevanssi_table'] . " ORDER BY doc DESC LIMIT 1");
1728
-
1729
- if (RELEVANSSI_PREMIUM) {
1730
- $user_count = $wpdb->get_var("SELECT COUNT(DISTINCT item) FROM " . $relevanssi_variables['relevanssi_table'] . " WHERE type = 'user'");
1731
- $taxterm_count = $wpdb->get_var("SELECT COUNT(DISTINCT item) FROM " . $relevanssi_variables['relevanssi_table'] . " WHERE (type != 'post' AND type != 'attachment' AND type != 'user')");
1732
  }
1733
 
1734
  ?>
1735
  <div id="indexing_tab">
1736
-
1737
  <table class="form-table">
1738
  <tr>
1739
  <th scope="row">
1740
- <input type='submit' name='submit' value='<?php esc_attr_e('Save the options', 'relevanssi'); ?>' class='button button-primary' /><br /><br />
1741
- <input type="button" id="build_index" name="index" value="<?php esc_attr_e('Build the index', 'relevanssi'); ?>" class='button-primary' /><br /><br />
1742
- <input type="button" id="continue_indexing" name="continue" value="<?php esc_attr_e('Index unindexed posts', 'relevanssi'); ?>" class='button-primary' />
1743
  </th>
1744
  <td>
1745
  <div id='indexing_button_instructions'>
1746
- <p class="description"><?php printf(__("%s empties the existing index and rebuilds it from scratch.", "relevanssi"), "<strong>" . __("Build the index", "relevanssi") . "</strong>");?></p>
1747
- <p class="description"><?php printf(__("%s doesn't empty the index and only indexes those posts that are not indexed. You can use it if you have to interrupt building the index.", "relevanssi"), "<strong>" . __("Index unindexed posts", "relevanssi") . "</strong>");?>
1748
- <?php if (RELEVANSSI_PREMIUM) _e("This doesn't index any taxonomy terms or users.", "relevanssi"); ?></p>
 
 
 
 
 
 
 
1749
  </div>
1750
  <div id='relevanssi-note' style='display: none'></div>
1751
  <div id='relevanssi-progress' class='rpi-progress'><div class="rpi-indicator"></div></div>
1752
- <div id='relevanssi-timer'><?php _e("Time elapsed", "relevanssi"); ?>: <span id="relevanssi_elapsed">0:00:00</span> | <?php _e("Time remaining", "relevanssi"); ?>: <span id="relevanssi_estimated"><?php _e("some time", "relevanssi"); ?></span></div>
1753
  <textarea id='results' rows='10' cols='80'></textarea>
1754
  </td>
1755
  </tr>
1756
  <tr>
1757
- <th scope="row"><?php _e("State of the index", "relevanssi"); ?></td>
1758
- <td id="stateoftheindex"><p><?php echo $docs_count ?> <?php echo _n("document in the index.", "documents in the index.", $docs_count, "relevanssi"); ?>
1759
- <?php if (RELEVANSSI_PREMIUM) : ?>
1760
- <br /><?php echo $user_count ?> <?php echo _n("user in the index.", "users in the index.", $user_count, "relevanssi"); ?><br />
1761
- <?php echo $taxterm_count ?> <?php echo _n("taxonomy term in the index.", "taxonomy terms in the index.", $taxterm_count, "relevanssi"); ?>
1762
  <?php endif; ?>
1763
  </p>
1764
- <p><?php echo $terms_count; ?> <?php echo _n("term in the index.", "terms in the index.", $terms_count, "relevanssi"); ?><br />
1765
- <?php echo $biggest_doc ?> <?php _e("is the highest post ID indexed.", "relevanssi"); ?></p>
1766
  </td>
1767
  </tr>
1768
  </table>
1769
 
1770
- <?php
1771
- if (count($index_post_types) < 2) {
1772
- echo "<p><strong>" . __("WARNING: You've chosen no post types to index. Nothing will be indexed. Choose some post types to index.", 'relevanssi') . "</strong></p>";
1773
  }
1774
- ?>
1775
 
1776
- <h2 id="indexing"><?php _e('Indexing options', 'relevanssi'); ?></h2>
1777
 
1778
- <p><?php _e("Any changes to the settings on this page require reindexing before they take effect.", "relevanssi"); ?></p>
1779
 
1780
  <table class="form-table">
1781
  <tr>
1782
- <th scope="row"><?php _e("Post types", "relevanssi"); ?></th>
1783
  <td>
1784
 
1785
  <table class="widefat" id="index_post_types_table">
1786
  <thead>
1787
  <tr>
1788
- <th><?php _e('Type', 'relevanssi'); ?></th>
1789
- <th><?php _e('Index', 'relevanssi'); ?></th>
1790
- <th><?php _e('Excluded from search?', 'relevanssi'); ?></th>
1791
  </tr>
1792
  </thead>
1793
  <?php
1794
- $pt_1 = get_post_types(array('exclude_from_search' => '0'));
1795
- $pt_2 = get_post_types(array('exclude_from_search' => false));
1796
- $public_types = array_merge($pt_1, $pt_2);
1797
- $post_types = get_post_types();
1798
- foreach ($post_types as $type) {
1799
- if ('nav_menu_item' === $type) continue;
1800
- if ('revision' === $type) continue;
1801
- if (in_array($type, $index_post_types)) {
1802
- $checked = 'checked="checked"';
1803
- }
1804
- else {
1805
- $checked = '';
1806
- }
1807
- $label = sprintf("%s", $type);
1808
- in_array($type, $public_types) ? $public = __('no', 'relevanssi') : $public = __('yes', 'relevanssi');
1809
-
1810
- echo <<<EOH
1811
- <tr>
1812
- <td>
1813
- $label
1814
- </td>
1815
- <td>
1816
- <input type='checkbox' name='relevanssi_index_type_$type' id='relevanssi_index_type_$type' $checked />
1817
- </td>
1818
- <td>
1819
- $public
1820
- </td>
1821
- </tr>
1822
- EOH;
1823
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1824
  ?>
1825
  <tr style="display:none">
1826
  <td>
@@ -1834,133 +2205,135 @@ EOH;
1834
  </td>
1835
  </tr>
1836
  </table>
1837
- <p class="description"><?php printf(__("%s includes all attachment types. If you want to index only some attachments, see %sControlling attachment types in the Knowledge base%s.", "relevanssi"), '<code>attachment</code>', '<a href="https://www.relevanssi.com/knowledge-base/controlling-attachment-types-index/">', '</a>'); ?></p>
 
1838
  </td>
1839
  </tr>
1840
 
1841
  <tr>
1842
  <th scope="row">
1843
- <?php _e('Taxonomies', 'relevanssi'); ?>
1844
  </th>
1845
  <td>
1846
 
1847
  <table class="widefat" id="custom_taxonomies_table">
1848
  <thead>
1849
  <tr>
1850
- <th><?php _e('Taxonomy', 'relevanssi'); ?></th>
1851
- <th><?php _e('Index', 'relevanssi'); ?></th>
1852
- <th><?php _e('Public?', 'relevanssi'); ?></th>
1853
  </tr>
1854
  </thead>
1855
 
1856
  <?php
1857
- $taxos = get_taxonomies('', 'objects');
1858
- foreach ($taxos as $taxonomy) {
1859
- if ($taxonomy->name === 'nav_menu') continue;
1860
- if ($taxonomy->name === 'link_category') continue;
1861
- if (in_array($taxonomy->name, $index_taxonomies_list)) {
1862
- $checked = 'checked="checked"';
1863
- }
1864
- else {
1865
- $checked = '';
1866
- }
1867
- $label = sprintf("%s", $taxonomy->name);
1868
- $taxonomy->public ? $public = __('yes', 'relevanssi') : $public = __('no', 'relevanssi');
1869
- $type = $taxonomy->name;
1870
-
1871
- echo <<<EOH
1872
- <tr>
1873
- <td>
1874
- $label
1875
- </td>
1876
- <td>
1877
- <input type='checkbox' name='relevanssi_index_taxonomy_$type' id='relevanssi_index_taxonomy_$type' $checked />
1878
- </td>
1879
- <td>
1880
- $public
1881
- </td>
1882
- </tr>
1883
- EOH;
1884
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1885
  ?>
1886
  </table>
1887
 
1888
- <p class="description"><?php _e('If you check a taxonomy here, the terms for that taxonomy are indexed with the posts. If you for example choose "post_tag", searching for a tag will find all posts that have the tag.', 'relevanssi'); ?>
1889
 
1890
  </td>
1891
  </tr>
1892
 
1893
  <tr>
1894
  <th scope="row">
1895
- <label for='relevanssi_index_comments'><?php _e("Comments", "relevanssi"); ?></label>
1896
  </th>
1897
  <td>
1898
  <select name='relevanssi_index_comments' id='relevanssi_index_comments'>
1899
- <option value='none' <?php echo $incom_type_none ?>><?php _e("none", "relevanssi"); ?></option>
1900
- <option value='normal' <?php echo $incom_type_normal ?>><?php _e("comments", "relevanssi"); ?></option>
1901
- <option value='all' <?php echo $incom_type_all ?>><?php _e("comments and pingbacks", "relevanssi"); ?></option>
1902
  </select>
1903
- <p class="description"><?php _e("If you choose to index comments, you can choose if you want to index just comments, or everything including comments and track- and pingbacks.", 'relevanssi'); ?></p>
1904
  </td>
1905
  </tr>
1906
 
1907
  <tr>
1908
  <th scope="row">
1909
- <label for='relevanssi_index_fields'><?php _e("Custom fields", "relevanssi"); ?></label>
1910
  </th>
1911
  <td>
1912
  <select name='relevanssi_index_fields_select' id='relevanssi_index_fields_select'>
1913
- <option value='none' <?php echo $fields_select_none; ?>><?php _e("none", "relevanssi"); ?></option>
1914
- <option value='all' <?php echo $fields_select_all ?>><?php _e("all", "relevanssi"); ?></option>
1915
- <option value='visible' <?php echo $fields_select_visible ?>><?php _e("visible", "relevanssi"); ?></option>
1916
- <option value='some' <?php echo $fields_select_some ?>><?php _e("some", "relevanssi"); ?></option>
1917
  </select>
1918
- <p class="description"><?php _e("'All' indexes all custom fields for posts.", "relevanssi"); echo "<br/>";
1919
- _e("'Visible' only includes the custom fields that are visible in the user interface (with names that don't start with an underscore).", "relevanssi"); echo "<br />";
1920
- _e("'Some' lets you choose individual custom fields to index.", "relevanssi"); ?></p>
1921
- <div id="index_field_input" <?php if (empty($fields_select_some)) echo 'style="display: none"'; ?>>
1922
- <input type='text' name='relevanssi_index_fields' id='relevanssi_index_fields' size='60' value='<?php echo esc_attr($index_fields) ?>' />
1923
- <p class="description"><?php _e("Enter a comma-separated list of custom fields to include in the index. With Relevanssi Premium, you can also use 'fieldname_%_subfieldname' notation for ACF repeater fields.", "relevanssi"); ?></p>
1924
- <p class="description"><?php _e("You can use 'relevanssi_index_custom_fields' filter hook to adjust which custom fields are indexed.", "relevanssi"); ?></p>
 
 
 
 
 
 
 
 
 
 
 
 
1925
  </div>
1926
  <?php if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) : ?>
1927
- <p class="description"><?php printf(__("If you want the SKU included, choose %s and enter %s. Also see the contextual help for more details.", "relevanssi"), "'" . __('some', "relevanssi") . "'", '<code>_sku</code>'); ?></p>
 
1928
  <?php endif; ?>
1929
  </td>
1930
  </tr>
1931
 
1932
-
1933
-
1934
  <tr>
1935
  <th scope="row">
1936
- <label for='relevanssi_index_author'><?php _e('Author display names', 'relevanssi'); ?></label>
1937
  </th>
1938
  <td>
1939
  <fieldset>
1940
- <legend class="screen-reader-text"><?php _e("Index the post author display name", "relevanssi"); ?></legend>
1941
  <label for='relevanssi_index_author'>
1942
- <input type='checkbox' name='relevanssi_index_author' id='relevanssi_index_author' <?php echo $index_author ?> />
1943
- <?php _e("Index the post author display name", "relevanssi"); ?>
1944
  </label>
1945
- <p class="description"><?php _e("Searching for the post author display name will return posts by that author.", 'relevanssi'); ?></p>
1946
  </fieldset>
1947
  </td>
1948
  </tr>
1949
 
1950
  <tr>
1951
  <th scope="row">
1952
- <label for='relevanssi_index_excerpt'><?php _e('Excerpts', 'relevanssi'); ?></label>
1953
  </th>
1954
  <td>
1955
  <fieldset>
1956
- <legend class="screen-reader-text"><?php _e("Index the post excerpt", "relevanssi"); ?></legend>
1957
  <label for='relevanssi_index_excerpt'>
1958
- <input type='checkbox' name='relevanssi_index_excerpt' id='relevanssi_index_excerpt' <?php echo $index_excerpt ?> />
1959
- <?php _e("Index the post excerpt", "relevanssi") ?>
1960
  </label>
1961
- <p class="description"><?php _e("Relevanssi will find posts by the content in the excerpt.", 'relevanssi'); ?></p>
1962
  <?php if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) : ?>
1963
- <p class="description"><?php _e("WooCommerce stores the product short description in the excerpt, so it's a good idea to index excerpts.", "relevanssi"); ?></p>
1964
  <?php endif; ?>
1965
  </fieldset>
1966
  </td>
@@ -1968,187 +2341,233 @@ EOH;
1968
 
1969
  </table>
1970
 
1971
- <h2><?php _e("Shortcodes", "relevanssi"); ?></h2>
1972
 
1973
  <table class="form-table">
1974
  <tr>
1975
  <th scope="row">
1976
- <label for='relevanssi_expand_shortcodes'><?php _e("Expand shortcodes", "relevanssi"); ?></label>
1977
  </th>
1978
  <td>
1979
  <fieldset>
1980
- <legend class="screen-reader-text"><?php _e("Index the post excerpt", "relevanssi"); ?></legend>
1981
  <label for='relevanssi_expand_shortcodes'>
1982
- <input type='checkbox' name='relevanssi_expand_shortcodes' id='relevanssi_expand_shortcodes' <?php echo $expand_shortcodes ?> />
1983
- <?php _e("Expand shortcodes when indexing", "relevanssi"); ?>
1984
  </label>
1985
  <?php if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) : ?>
1986
- <p class="description important"><?php _e("WooCommerce has shortcodes that don't work well with Relevanssi. With WooCommerce, make sure the option is disabled.", "relevanssi"); ?></p>
1987
  <?php endif; ?>
1988
- <p class="description"><?php _e("If checked, Relevanssi will expand shortcodes in post content before indexing. Otherwise shortcodes will be stripped.", "relevanssi"); ?></p>
1989
- <p class="description"><?php _e("If you use shortcodes to include dynamic content, Relevanssi will not keep the index updated, the index will reflect the status of the shortcode content at the moment of indexing.", "relevanssi"); ?></p>
1990
  </fieldset>
1991
  </td>
1992
  </tr>
1993
 
1994
- <?php if (function_exists('relevanssi_form_disable_shortcodes')) relevanssi_form_disable_shortcodes($disable_shortcodes); ?>
 
 
 
 
1995
 
1996
  </table>
1997
 
1998
- <?php if (function_exists('relevanssi_form_index_users')) relevanssi_form_index_users($index_users, $index_subscribers, $index_user_fields); ?>
1999
-
2000
- <?php if (function_exists('relevanssi_form_index_synonyms')) relevanssi_form_index_synonyms($index_synonyms); ?>
2001
-
2002
- <?php if (function_exists('relevanssi_form_index_taxonomies')) relevanssi_form_index_taxonomies($index_taxonomies, $index_terms); ?>
2003
-
2004
- <?php if (function_exists('relevanssi_form_index_pdf_parent')) relevanssi_form_index_pdf_parent($index_pdf_parent, $index_post_types); ?>
 
 
 
 
 
 
 
2005
 
2006
- <h2><?php _e("Advanced indexing settings", "relevanssi"); ?></h2>
2007
 
2008
- <p><button type="button" id="show_advanced_indexing"><?php _e("Show advanced settings", "relevanssi"); ?></button></p>
2009
 
2010
  <table class="form-table screen-reader-text" id="advanced_indexing">
2011
  <tr>
2012
  <th scope="row">
2013
- <label for='relevanssi_min_word_length'><?php _e("Minimum word length", "relevanssi"); ?></label>
2014
  </th>
2015
  <td>
2016
- <input type='number' name='relevanssi_min_word_length' id='relevanssi_min_word_length' value='<?php echo esc_attr($min_word_length); ?>' />
2017
- <p class="description"><?php _e("Words shorter than this many letters will not be indexed.", "relevanssi"); ?></p>
 
 
2018
  </td>
2019
  </tr>
2020
  <tr>
2021
- <th scope="row"><?php _e("Punctuation control"); ?></th>
2022
- <td><p class="description"><?php _e("Here you can adjust how the punctuation is controlled. For more information, see help. Remember that any changes here require reindexing, otherwise searches will fail to find posts they should.", "relevanssi"); ?></p></td>
2023
  </tr>
2024
  <tr>
2025
  <th scope="row">
2026
- <label for='relevanssi_punct_hyphens'><?php _e("Hyphens and dashes", "relevanssi"); ?></label>
2027
  </th>
2028
  <td>
2029
  <select name='relevanssi_punct_hyphens' id='relevanssi_punct_hyphens'>
2030
- <option value='keep' <?php echo $punct_hyphens_keep ?>><?php _e("Keep", "relevanssi"); ?></option>
2031
- <option value='replace' <?php echo $punct_hyphens_replace ?>><?php _e("Replace with spaces", "relevanssi"); ?></option>
2032
- <option value='remove' <?php echo $punct_hyphens_remove ?>><?php _e("Remove", "relevanssi"); ?></option>
2033
  </select>
2034
- <p class="description"><?php _e("How Relevanssi should handle hyphens and dashes (en and em dashes)? Replacing with spaces is generally the best option, but in some cases removing completely is the best option. Keeping them is rarely the best option.", "relevanssi"); ?></p>
2035
 
2036
  </td>
2037
  </tr>
2038
  <tr>
2039
  <th scope="row">
2040
- <label for='relevanssi_punct_quotes'><?php _e("Apostrophes and quotes", "relevanssi"); ?></label>
2041
  </th>
2042
  <td>
2043
  <select name='relevanssi_punct_quotes' id='relevanssi_punct_quotes'>
2044
- <option value='replace' <?php echo $punct_quotes_replace ?>><?php _e("Replace with spaces", "relevanssi"); ?></option>
2045
- <option value='remove' <?php echo $punct_quotes_remove ?>><?php _e("Remove", "relevanssi"); ?></option>
2046
  </select>
2047
- <p class="description"><?php _e("How Relevanssi should handle apostrophes and quotes? It's not possible to keep them; that would lead to problems. Default behaviour is to replace with spaces, but sometimes removing makes sense.", "relevanssi"); ?></p>
2048
 
2049
  </td>
2050
  </tr>
2051
  <tr>
2052
  <th scope="row">
2053
- <label for='relevanssi_punct_ampersands'><?php _e("Ampersands", "relevanssi"); ?></label>
2054
  </th>
2055
  <td>
2056
  <select name='relevanssi_punct_ampersands' id='relevanssi_punct_ampersands'>
2057
- <option value='keep' <?php echo $punct_ampersands_keep ?>><?php _e("Keep", "relevanssi"); ?></option>
2058
- <option value='replace' <?php echo $punct_ampersands_replace ?>><?php _e("Replace with spaces", "relevanssi"); ?></option>
2059
- <option value='remove' <?php echo $punct_ampersands_remove ?>><?php _e("Remove", "relevanssi"); ?></option>
2060
  </select>
2061
- <p class="description"><?php _e("How Relevanssi should handle ampersands? Replacing with spaces is generally the best option, but if you talk a lot about D&amp;D, for example, keeping the ampersands is useful.", "relevanssi"); ?></p>
2062
 
2063
  </td>
2064
  </tr>
 
 
 
 
 
 
 
 
 
 
 
2065
 
2066
- <?php if (function_exists('relevanssi_form_thousep')) relevanssi_form_thousep($thousand_separator); ?>
 
 
 
 
 
 
 
 
 
 
 
 
2067
 
2068
- <?php if (function_exists('relevanssi_form_mysql_columns')) relevanssi_form_mysql_columns($mysql_columns); ?>
2069
 
2070
- <?php if (function_exists('relevanssi_form_internal_links')) relevanssi_form_internal_links($intlinks_noindex, $intlinks_strip, $intlinks_nostrip); ?>
2071
 
2072
- </table>
2073
 
2074
- <p><button type="button" style="display: none" id="hide_advanced_indexing"><?php _e("Hide advanced settings", "relevanssi"); ?></button></p>
2075
 
2076
- </div> <?php // #indexing_tab ?>
2077
 
2078
- <?php endif; // active tab: indexing ?>
 
 
 
 
 
2079
 
2080
- <?php if ($active_tab === "attachments") : ?>
2081
-
2082
- <?php if (function_exists('relevanssi_form_attachments')) { relevanssi_form_attachments($index_post_types, $index_pdf_parent); }
2083
- else {
2084
- $display_save_button = false; ?>
2085
-
2086
- <h2><?php _e("Indexing attachment content", "relevanssi"); ?></h2>
2087
 
2088
- <p><?php _e("With Relevanssi Premium, you can index the text contents of PDF attachments. The contents of the attachments are processed on an external service, which makes the feature reliable and light on your own server performance.", "relevanssi"); ?></p>
 
 
2089
 
2090
- <p><?php printf(__("In order to access this and many other delightful Premium features, %s buy Relevanssi Premium here%s.", "relevanssi"), '<a href="https://www.relevanssi.com/buy-premium/">', '</a>'); ?></p>
2091
-
2092
  <?php } ?>
2093
 
2094
- <?php endif; // active tab: attachments ?>
2095
 
2096
- <?php if ($active_tab === "synonyms") : ?>
2097
 
2098
- <h3 id="synonyms"><?php _e("Synonyms", "relevanssi"); ?></h3>
2099
 
2100
  <table class="form-table">
2101
  <tr>
2102
  <th scope="row">
2103
- <?php _e("Synonyms", "relevanssi"); ?>
2104
  </th>
2105
  <td>
2106
- <p class="description"><?php _e("Add synonyms here to make the searches find better results. If you notice your users frequently misspelling a product name, or for other reasons use many names for one thing, adding synonyms will make the results better.", "relevanssi"); ?></p>
2107
-
2108
- <p class="description"><?php _e("Do not go overboard, though, as too many synonyms can make the search confusing: users understand if a search query doesn't match everything, but they get confused if the searches match to unexpected things.", "relevanssi"); ?></p>
2109
  <br />
2110
- <textarea name='relevanssi_synonyms' id='relevanssi_synonyms' rows='9' cols='60'><?php echo htmlspecialchars($synonyms); ?></textarea>
2111
-
2112
- <p class="description"><?php _e("The format here is <code>key = value</code>. If you add <code>dog = hound</code> to the list of synonyms, searches for <code>dog</code> automatically become a search for <code>dog hound</code> and will thus match to posts that include either <code>dog</code> or <code>hound</code>. This only works in OR searches: in AND searches the synonyms only restrict the search, as now the search only finds posts that contain <strong>both</strong> <code>dog</code> and <code>hound</code>.", "relevanssi"); ?></p>
2113
 
2114
- <p class="description"><?php _e("The synonyms are one direction only. If you want both directions, add the synonym again, reversed: <code>hound = dog</code>.", "relevanssi"); ?></p>
2115
 
2116
- <p class="description"><?php _e("It's possible to use phrases for the value, but not for the key. <code>dog = \"great dane\"</code> works, but <code>\"great dane\" = dog</code> doesn't.", "relevanssi"); ?></p>
2117
 
2118
- <?php if (RELEVANSSI_PREMIUM) : ?>
2119
- <p class="description"><?php _e("If you want to use synonyms in AND searches, enable synonym indexing on the Indexing tab.", "relevanssi"); ?></p>
2120
  <?php endif; ?>
2121
  </td>
2122
  </tr>
2123
  </table>
2124
 
2125
- <?php endif; // active tab: synonyms ?>
2126
 
2127
- <?php if ($active_tab === "stopwords") : ?>
2128
 
2129
- <h3 id="stopwords"><?php _e("Stopwords", "relevanssi"); ?></h3>
2130
 
2131
  <?php relevanssi_show_stopwords(); ?>
2132
 
2133
  <?php
2134
- if (apply_filters('relevanssi_display_common_words', true))
2135
- relevanssi_common_words(25);
 
 
 
 
 
 
 
 
 
 
2136
  ?>
2137
 
2138
- <?php endif; // active tab: stopwords ?>
2139
 
2140
- <?php if ($active_tab === "importexport") : ?>
2141
 
2142
- <?php if (function_exists('relevanssi_form_importexport')) :
2143
- relevanssi_form_importexport($serialized_options);
2144
- endif;
 
2145
  ?>
2146
 
2147
  <?php endif; ?>
2148
 
2149
- <?php if ($display_save_button) : ?>
2150
 
2151
- <input type='submit' name='submit' value='<?php esc_attr_e('Save the options', 'relevanssi'); ?>' class='button button-primary' />
2152
 
2153
  <?php endif; ?>
2154
 
@@ -2156,246 +2575,319 @@ EOH;
2156
  </div>
2157
 
2158
  <?php
2159
-
2160
- //relevanssi_sidebar();
2161
  }
2162
 
 
 
 
 
 
 
 
 
2163
  function relevanssi_show_stopwords() {
2164
- global $wpdb, $relevanssi_variables, $wp_version;
2165
-
2166
- RELEVANSSI_PREMIUM ? $plugin = 'relevanssi-premium' : $plugin = 'relevanssi';
2167
 
2168
- echo "<p>";
2169
- _e("Enter a word here to add it to the list of stopwords. The word will automatically be removed from the index, so re-indexing is not necessary. You can enter many words at the same time, separate words with commas.", 'relevanssi');
2170
- echo "</p>";
 
2171
 
 
2172
  ?>
2173
  <table class="form-table">
2174
  <tr>
2175
  <th scope="row">
2176
- <label for="addstopword"><p><?php _e("Stopword(s) to add", 'relevanssi'); ?>
2177
  </th>
2178
  <td>
2179
  <textarea name="addstopword" id="addstopword" rows="2" cols="80"></textarea>
2180
- <p><input type="submit" value="<?php esc_attr_e("Add", 'relevanssi'); ?>" class='button' /></p>
2181
  </td>
2182
  </tr>
2183
  </table>
2184
- <p><?php
2185
- _e("Here's a list of stopwords in the database. Click a word to remove it from stopwords. Removing stopwords won't automatically return them to index, so you need to re-index all posts after removing stopwords to get those words back to index.", 'relevanssi');
2186
- ?></p>
2187
 
2188
  <table class="form-table">
2189
  <tr>
2190
  <th scope="row">
2191
- <?php _e("Current stopwords", "relevanssi"); ?>
2192
  </th>
2193
  <td>
2194
- <?php
2195
-
2196
- echo "<ul>";
2197
- $results = $wpdb->get_results("SELECT * FROM " . $relevanssi_variables['stopword_table']);
2198
  $exportlist = array();
2199
- foreach ($results as $stopword) {
2200
- $sw = stripslashes($stopword->stopword);
2201
- printf('<li style="display: inline;"><input type="submit" name="removestopword" value="%s"/></li>', esc_attr($sw));
2202
- array_push($exportlist, $sw);
2203
  }
2204
- echo "</ul>";
2205
 
2206
- $exportlist = htmlspecialchars(implode(", ", $exportlist));
2207
  ?>
2208
- <p><input type="submit" id="removeallstopwords" name="removeallstopwords" value="<?php esc_attr_e('Remove all stopwords', 'relevanssi'); ?>" class='button' /></p>
2209
  </td>
2210
  </tr>
2211
  <tr>
2212
  <th scope="row">
2213
- <?php _e("Exportable list of stopwords", "relevanssi");?>
2214
  </th>
2215
  <td>
2216
- <textarea name="stopwords" id="stopwords" rows="2" cols="80"><?php echo $exportlist; ?></textarea>
2217
- <p class="description"><?php _e("You can copy the list of stopwords here if you want to back up the list, copy it to a different blog or otherwise need the list.", "relevanssi"); ?></p>
2218
  </td>
2219
  </tr>
2220
  </table>
2221
 
2222
  <?php
2223
-
2224
  }
2225
 
 
 
 
 
 
2226
  function relevanssi_admin_help() {
2227
  global $wpdb;
2228
 
2229
  $screen = get_current_screen();
2230
  $screen->add_help_tab( array(
2231
- 'id' => 'relevanssi-searching',
2232
- 'title' => __( 'Searching', 'relevanssi' ),
2233
- 'content' => "<ul>" .
2234
- "<li>" . sprintf(__("To adjust the post order, you can use the %s query parameter. With %s, you can use multiple layers of different sorting methods. See <a href='%s'>WordPress Codex</a> for more details on using arrays for orderby.", 'relevanssi'), "<code>orderby</code>", "<code>orderby</code>", "https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters") . "</li>" .
2235
- "<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') .
2236
- '<pre>add_filter("relevanssi_fuzzy_query", "rlv_partial_inside_words");
2237
- function rlv_partial_inside_words($query) {
 
2238
  return "(term LIKE \'%#term#%\')";
2239
  }</pre></li>' .
2240
- "<li>" . sprintf(__("In order to adjust the throttle limit, you can use the %s filter hook.", 'relevanssi'), "<code>relevanssi_throttle_limit</code>") .
 
2241
  '<pre>add_filter("relevanssi_throttle_limit", function( $limit ) { return 200; } );</pre>' .
2242
- "<li>" . __("It's not usually necessary to adjust the limit from 500, but in some cases performance gains can be achieved by setting a lower limit. We don't suggest going under 200, as low values will make the results worse.", "relevanssi") . "</li>" .
2243
- "</ul>",
2244
  ));
2245
  $screen->add_help_tab( array(
2246
- 'id' => 'relevanssi-search-restrictions',
2247
- 'title' => __( 'Restrictions', 'relevanssi' ),
2248
- 'content' => "<ul>" .
2249
- "<li>" . __("If you want the general search to target all posts, but have a single search form target only certain posts, you can add a hidden input variable to the search form. ", 'relevanssi') . "</li>" .
2250
- "<li>" . __("For example in order to restrict the search to categories 10, 14 and 17, you could add this to the search form:", 'relevanssi') .
2251
- '<pre>&lt;input type="hidden" name="cats" value="10,14,17" /&gt;</pre></li>' .
2252
- "<li>" . __("To restrict the search to posts tagged with alfa AND beta, you could add this to the search form:", 'relevanssi') .
2253
- '<pre>&lt;input type="hidden" name="tag" value="alfa+beta" /&gt;</pre></li>' .
2254
- "<li>" . sprintf(__("For all the possible options, see the Codex documentation for %s.", "relevanssi"), '<a href="https://codex.wordpress.org/Class_Reference/WP_Query">WP_Query</a>') . "</li>" .
2255
- "</ul>",
 
2256
  ));
2257
  $screen->add_help_tab( array(
2258
- 'id' => 'relevanssi-search-exclusions',
2259
- 'title' => __( 'Exclusions', 'relevanssi' ),
2260
- 'content' => "<ul>" .
2261
- "<li>" . sprintf(__("For more exclusion options, see the Codex documentation for %s. For example, to exclude tag ID 10, use", "relevanssi"), '<a href="https://codex.wordpress.org/Class_Reference/WP_Query">WP_Query</a>') .
 
2262
  '<pre>&lt;input type="hidden" name="tag__not_in" value="10" /&gt;</pre></li>' .
2263
- "<li>" . sprintf(__("To exclude posts from the index and not just from the search, you can use the %s filter hook. This would not index posts that have a certain taxonomy term:", 'relevanssi'), '<code>relevanssi_do_not_index</code>') .
2264
- '<pre>add_filter("relevanssi_do_not_index", "rlv_index_filter", 10, 2);
2265
- function rlv_index_filter($block, $post_id) {
2266
- if (has_term("jazz", "genre", $post_id)) $block = true;
 
 
 
2267
  return $block;
2268
  }
2269
- </pre></li>' .
2270
- "<li>" . sprintf(__("For more examples, see <a href='%s'>the related knowledge base posts</a>.", "relevanssi"), 'https://www.relevanssi.com/tag/relevanssi_do_not_index/') . "</li>" .
2271
- "</ul>",
 
 
 
 
 
 
 
 
 
 
 
 
2272
  ));
2273
  $screen->add_help_tab( array(
2274
- 'id' => 'relevanssi-logging',
2275
- 'title' => __( 'Logs', 'relevanssi' ),
2276
- 'content' => "<ul>" .
2277
- "<li>" . sprintf(__('By default, the User searches page shows 20 most common keywords. In order to see more, you can adjust the value with the %s filter hook, like this:', 'relevanssi'), '<code>relevanssi_user_searches_limit</code>') .
2278
- "<pre>add_filter('relevanssi_user_searches_limit', function() { return 50; });</pre></li>" .
2279
- "<li>" . sprintf(__("The complete logs are stored in the %s database table, where you can access them if you need more information than what the User searches page provides.", "relevanssi"), '<code>' . $wpdb->prefix . "relevanssi_log</code>") . "</li>" .
2280
- "</ul>",
 
 
 
 
 
 
 
 
2281
  ));
2282
  $screen->add_help_tab( array(
2283
- 'id' => 'relevanssi-excerpts',
2284
- 'title' => __( 'Excerpts', 'relevanssi' ),
2285
- 'content' => "<ul>" .
2286
- "<li>" . __('Building custom excerpts can be slow. If you are not actually using the excerpts, make sure you disable the option.', 'relevanssi') . "</li>" .
2287
- "<li>" . sprintf(__('Custom snippets require that the search results template uses %s to print out the excerpts.', 'relevanssi'), "<code>the_excerpt()</code>") . "</li>" .
2288
- "<li>" . __("Generally, Relevanssi generates the excerpts from post content. If you want to include custom field content in the excerpt-building, this can be done with a simple setting from the excerpt settings.", "relevanssi") . "</li>" .
2289
- "<li>" . sprintf(__("If you want more control over what content Relevanssi uses to create the excerpts, you can use the %s and %s filter hooks to adjust the content.", "relevanssi"), "<code>relevanssi_pre_excerpt_content</code>", "<code>relevanssi_excerpt_content</code>") . "</li>" .
2290
- "<li>" . sprintf(__("Some shortcode do not work well with Relevanssi excerpt-generation. Relevanssi disables some shortcodes automatically to prevent problems. This can be adjusted with the %s filter hook.", "relevanssi"), "<code>relevanssi_disable_shortcodes_excerpt</code>") . "</li>" .
2291
- "<li>" . sprintf(__("If you want Relevanssi to build excerpts faster and don't mind that they may be less than perfect in quality, add a filter that returns true on hook %s.", "relevanssi"), '<code>relevanssi_optimize_excerpts</code>') .
2292
- "<pre>add_filter('relevanssi_optimize_excerpts', '__return_true');</pre></li>" .
2293
- "</ul>",
2294
  ));
2295
  $screen->add_help_tab( array(
2296
- 'id' => 'relevanssi-highlights',
2297
- 'title' => __( 'Highlights', 'relevanssi' ),
2298
- 'content' => "<ul>" .
2299
- "<li>" . __("Title highlights don't appear automatically, because that led to problems with highlights appearing in wrong places and messing up navigation menus, for example.", 'relevanssi') . "</li>" .
2300
- "<li>" . sprintf(__("In order to see title highlights from Relevanssi, replace %s in the search results template with %s. It does the same thing, but supports Relevanssi title highlights.", 'relevanssi'), "<code>the_title()</code>", "<code>relevanssi_the_title()</code>") . "</li>" .
2301
- "</ul>",
 
 
 
2302
  ));
2303
  $screen->add_help_tab( array(
2304
- 'id' => 'relevanssi-punctuation',
2305
- 'title' => __( 'Punctuation', 'relevanssi' ),
2306
- 'content' => "<ul>" .
2307
- "<li>" . __("Relevanssi removes punctuation. Some punctuation is removed, some replaced with spaces. Advanced indexing settings include some of the more common settings people want to change.", "relevanssi") . "</li>" .
2308
- "<li>" . sprintf(__("For more fine-tuned changes, you can use %s filter hook to adjust what is replaced with what, and %s filter hook to completely override the default punctuation control.", 'relevanssi'), '<code>relevanssi_punctuation_filter</code>', '<code>relevanssi_remove_punctuation</code>') . "</li>" .
2309
- "<li>" . sprintf(__("For more examples, see <a href='%s'>the related knowledge base posts</a>.", "relevanssi"), 'https://www.relevanssi.com/tag/relevanssi_remove_punct/') . "</li>" .
2310
- "</ul>",
 
 
 
2311
  ));
2312
  $screen->add_help_tab( array(
2313
- 'id' => 'relevanssi-helpful-shortcodes',
2314
- 'title' => __( 'Helpful shortcodes', 'relevanssi' ),
2315
- 'content' => "<ul>" .
2316
- "<li>" . sprintf(__("If you have content that you don't want indexed, you can wrap that content in a %s shortcode.", 'relevanssi'), '<code>[noindex]</code>') . "</li>" .
2317
- "<li>" . sprintf(__("If you need a search form on some page on your site, you can use the %s shortcode to print out a basic search form.", "relevanssi"), '<code>[searchform]</code>') . "</li>" .
2318
- "</ul>",
 
2319
  ));
2320
  $screen->add_help_tab( array(
2321
- 'id' => 'relevanssi-title-woocommerce',
2322
- 'title' => __( 'WooCommerce', 'relevanssi' ),
2323
- 'content' => "<ul>" .
2324
- "<li>" . __("If your SKUs include hyphens or other punctuation, do note that Relevanssi replaces most punctuation with spaces. That's going to cause issues with SKU searches.", 'relevanssi') . "</li>" .
2325
- "<li>" . sprintf(__("For more details how to fix that issue, see <a href='%s'>WooCommerce tips in Relevanssi user manual</a>.", 'relevanssi'), "https://www.relevanssi.com/user-manual/woocommerce/") . "</li>" .
2326
- "</ul>",
 
 
 
 
 
2327
  ));
2328
  $screen->set_help_sidebar(
2329
  '<p><strong>' . __( 'For more information:', 'relevanssi' ) . '</strong></p>' .
2330
- '<p><a href="http://www.relevanssi.com/knowledge-base/" target="_blank">' . __( 'Plugin knowledge base', 'relevanssi' ) . '</a></p>' .
2331
- '<p><a href="http://wordpress.org/tags/relevanssi?forum_id=10" target="_blank">' . __( 'WordPress.org forum', 'relevanssi' ) . '</a></p>'
2332
  );
2333
  }
2334
 
2335
- add_action( 'admin_enqueue_scripts', 'relevanssi_add_admin_scripts' );
2336
- function relevanssi_add_admin_scripts($hook) {
 
 
 
 
 
 
 
 
2337
  global $relevanssi_variables;
2338
 
2339
- $plugin_dir_url = plugin_dir_url($relevanssi_variables['file']);
2340
 
2341
  // Only enqueue on Relevanssi pages.
2342
  $acceptable_hooks = array(
2343
- 'toplevel_page_relevanssi-premium/relevanssi', 'settings_page_relevanssi-premium/relevanssi',
2344
- 'toplevel_page_relevanssi/relevanssi', 'settings_page_relevanssi/relevanssi',
 
 
2345
  );
2346
- if (!in_array($hook, $acceptable_hooks)) return;
 
 
2347
 
2348
  wp_enqueue_style( 'wp-color-picker' );
2349
  wp_enqueue_script( 'relevanssi_admin_js', $plugin_dir_url . 'lib/admin_scripts.js', array( 'wp-color-picker' ) );
2350
- if (!RELEVANSSI_PREMIUM) wp_enqueue_script( 'relevanssi_admin_js_free', $plugin_dir_url . 'lib/admin_scripts_free.js', array( 'relevanssi_admin_js' ) );
2351
- if (RELEVANSSI_PREMIUM) wp_enqueue_script( 'relevanssi_admin_js_premium', $plugin_dir_url . 'premium/admin_scripts_premium.js', array( 'relevanssi_admin_js' ) );
 
 
 
 
2352
  wp_enqueue_style( 'relevanssi_admin_css', $plugin_dir_url . 'lib/admin_styles.css' );
2353
-
2354
- $translation = array(
2355
- 'confirm' => __('Click OK to copy Relevanssi options to all subsites', 'relevanssi'),
2356
- 'confirm_stopwords' => __('Are you sure you want to remove all stopwords?', 'relevanssi'),
2357
- 'truncating_index' => __("Wiping out the index...", "relevanssi"),
2358
- 'done' => __('Done.', 'relevanssi'),
2359
- 'indexing_users' => __('Indexing users...', 'relevanssi'),
2360
- 'indexing_taxonomies' => __('Indexing the following taxonomies:', 'relevanssi'),
2361
- 'counting_posts' => __('Counting posts...', 'relevanssi'),
2362
- 'counting_terms' => __('Counting taxonomy terms...', 'relevanssi'),
2363
- 'counting_users' => __('Counting users...', 'relevanssi'),
2364
- 'posts_found' => __('posts found.', 'relevanssi'),
2365
- 'terms_found' => __('taxonomy terms found.', 'relevanssi'),
2366
- 'users_found' => __('users found.', 'relevanssi'),
2367
- 'taxonomy_disabled' => __('Taxonomy term indexing is disabled.', "relevanssi"),
2368
- 'user_disabled' => __('User indexing is disabled.', "relevanssi"),
2369
- 'indexing_complete' => __('Indexing complete.', 'relevanssi'),
2370
- 'excluded_posts' => __('posts excluded.', 'relevanssi'),
2371
- 'options_changed' => __("Settings have changed, please save the options before indexing.", 'relevanssi'),
2372
- 'reload_state' => __("Reload the page to refresh the state of the index.", 'relevanssi'),
2373
- 'pdf_reset_confirm' => __("Are you sure you want to delete all PDF content from the index?", 'relevanssi'),
2374
- 'pdf_reset_done' => __("Relevanssi PDF data wiped clean. Removed entries: ", 'relevanssi'),
2375
- 'hour' => __("hour", "relevanssi"),
2376
- 'hours' => __("hours", "relevanssi"),
2377
- 'about' => __("about", "relevanssi"),
2378
- 'sixty_min' => __("about an hour", "relevanssi"),
2379
- 'ninety_min' => __("about an hour and a half", "relevanssi"),
2380
- 'minute' => __("minute", "relevanssi"),
2381
- 'minutes' => __("minutes", "relevanssi"),
2382
- 'underminute' => __("less than a minute", "relevanssi"),
2383
- 'notimeremaining' => __("we're done!", "relevanssi"),
2384
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2385
  );
2386
 
2387
- wp_localize_script( 'relevanssi_admin_js', 'relevanssi', $translation );
 
 
 
 
 
 
 
2388
  }
2389
 
2390
- /* Copy of sanitize_hex_color(), because that isn't always available
2391
- */
2392
- function relevanssi_sanitize_hex_color($color) {
 
 
 
 
 
 
 
2393
  if ( '' === $color ) {
2394
  return '';
2395
  }
2396
-
2397
  // 3 or 6 hex digits, or the empty string.
2398
- if ( preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
2399
  return $color;
2400
  }
2401
- }
 
 
1
  <?php
2
+ /**
3
+ * /lib/interface.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
+
11
+ /**
12
+ * Controls the Relevanssi settings page.
13
+ *
14
+ * @global array $relevanssi_variables The global Relevanssi variables array.
15
+ */
16
  function relevanssi_options() {
17
  global $relevanssi_variables;
18
+ $options_txt = __( 'Relevanssi Search Options', 'relevanssi' );
19
+ if ( RELEVANSSI_PREMIUM ) {
20
+ $options_txt = __( 'Relevanssi Premium Search Options', 'relevanssi' );
 
 
21
  }
22
 
23
+ printf( "<div class='wrap'><h2>%s</h2>", esc_html( $options_txt ) );
24
+ if ( ! empty( $_REQUEST ) ) {
25
+ if ( isset( $_REQUEST['submit'] ) ) {
26
+ check_admin_referer( plugin_basename( $relevanssi_variables['file'] ), 'relevanssi_options' );
 
 
 
 
 
 
 
 
 
 
 
27
  update_relevanssi_options();
 
28
  }
29
 
30
+ if ( isset( $_REQUEST['import_options'] ) ) {
31
+ if ( function_exists( 'relevanssi_import_options' ) ) {
32
+ check_admin_referer( plugin_basename( $relevanssi_variables['file'] ), 'relevanssi_options' );
33
  $options = $_REQUEST['relevanssi_settings'];
34
+ relevanssi_import_options( $options );
35
  }
36
  }
37
 
38
+ if ( isset( $_REQUEST['search'] ) ) {
39
+ relevanssi_search( $_REQUEST['q'] );
40
  }
41
 
42
+ if ( isset( $_REQUEST['dowhat'] ) ) {
43
+ if ( 'add_stopword' === $_REQUEST['dowhat'] ) {
44
+ if ( isset( $_REQUEST['term'] ) ) {
45
+ check_admin_referer( plugin_basename( $relevanssi_variables['file'] ), 'relevanssi_options' );
46
+ relevanssi_add_stopword( $_REQUEST['term'] );
47
  }
48
  }
49
  }
50
 
51
+ if ( isset( $_REQUEST['addstopword'] ) ) {
52
+ check_admin_referer( plugin_basename( $relevanssi_variables['file'] ), 'relevanssi_options' );
53
+ relevanssi_add_stopword( $_REQUEST['addstopword'] );
54
  }
55
 
56
+ if ( isset( $_REQUEST['removestopword'] ) ) {
57
+ check_admin_referer( plugin_basename( $relevanssi_variables['file'] ), 'relevanssi_options' );
58
+ relevanssi_remove_stopword( $_REQUEST['removestopword'] );
59
  }
60
 
61
+ if ( isset( $_REQUEST['removeallstopwords'] ) ) {
62
+ check_admin_referer( plugin_basename( $relevanssi_variables['file'] ), 'relevanssi_options' );
63
  relevanssi_remove_all_stopwords();
64
  }
65
  }
 
66
 
67
+ relevanssi_options_form();
68
 
69
+ echo "<div style='clear:both'></div></div>";
70
  }
71
 
72
+ /**
73
+ * Prints out the 'User searches' page.
74
+ */
75
  function relevanssi_search_stats() {
76
  $relevanssi_hide_branding = get_option( 'relevanssi_hide_branding' );
77
 
78
+ if ( 'on' === $relevanssi_hide_branding ) {
79
+ $options_txt = __( 'User searches', 'relevanssi' );
80
+ } else {
81
+ $options_txt = __( 'Relevanssi User Searches', 'relevanssi' );
82
+ }
83
 
84
+ if ( isset( $_REQUEST['relevanssi_reset'] ) && current_user_can( 'manage_options' ) ) {
85
+ check_admin_referer( 'relevanssi_reset_logs', '_relresnonce' );
86
+ if ( isset( $_REQUEST['relevanssi_reset_code'] ) ) {
87
+ if ( 'reset' === $_REQUEST['relevanssi_reset_code'] ) {
88
  $verbose = true;
89
+ relevanssi_truncate_logs( $verbose );
90
  }
91
  }
92
  }
93
 
94
+ wp_enqueue_style( 'dashboard' );
95
+ wp_print_styles( 'dashboard' );
96
+ wp_enqueue_script( 'dashboard' );
97
+ wp_print_scripts( 'dashboard' );
98
 
99
+ printf( "<div class='wrap'><h2>%s</h2>", esc_html( $options_txt ) );
100
 
101
+ if ( 'on' === get_option( 'relevanssi_log_queries' ) ) {
 
 
102
  relevanssi_query_log();
103
+ } else {
104
+ printf( '<p>%s</p>', esc_html__( 'Enable query logging to see stats here.', 'relevanssi' ) );
105
  }
 
 
 
 
 
106
  }
107
 
108
+ /**
109
+ * Truncates the Relevanssi logs.
110
+ *
111
+ * @global object $wpdb The WP database interface.
112
+ * @global array $relevanssi_variables The global Relevanssi variables array.
113
+ *
114
+ * @param boolean $verbose If true, prints out a notice. Default true.
115
+ *
116
+ * @return boolean True if success, false if failure.
117
+ */
118
+ function relevanssi_truncate_logs( $verbose = true ) {
119
  global $wpdb, $relevanssi_variables;
120
 
121
+ $result = $wpdb->query( 'TRUNCATE ' . $relevanssi_variables['log_table'] ); // WPCS: unprepared SQL ok.
 
122
 
123
+ if ( $verbose ) {
124
+ if ( false !== $result ) {
125
+ printf( "<div id='relevanssi-warning' class='updated fade'>%s</div>", esc_html__( 'Logs clear!', 'relevanssi' ) );
126
+ } else {
127
+ printf( "<div id='relevanssi-warning' class='updated fade'>%s</div>", esc_html__( 'Clearing the logs failed.', 'relevanssi' ) );
 
128
  }
129
  }
130
 
131
  return $result;
132
  }
133
 
134
+ /**
135
+ * Updates Relevanssi options.
136
+ *
137
+ * Checks the option values and updates the options. It's safe to use $_REQUEST here,
138
+ * check_admin_referer() is done immediately before this function is called.
139
+ */
140
  function update_relevanssi_options() {
141
+ // phpcs:disable WordPress.CSRF.NonceVerification
142
+ if ( isset( $_REQUEST['relevanssi_content_boost'] ) ) {
143
+ $boost = floatval( $_REQUEST['relevanssi_content_boost'] );
144
+ update_option( 'relevanssi_content_boost', $boost );
145
  }
146
 
147
+ if ( isset( $_REQUEST['relevanssi_title_boost'] ) ) {
148
+ $boost = floatval( $_REQUEST['relevanssi_title_boost'] );
149
+ update_option( 'relevanssi_title_boost', $boost );
150
  }
151
 
152
+ if ( isset( $_REQUEST['relevanssi_comment_boost'] ) ) {
153
+ $boost = floatval( $_REQUEST['relevanssi_comment_boost'] );
154
+ update_option( 'relevanssi_comment_boost', $boost );
155
  }
156
 
157
+ if ( isset( $_REQUEST['relevanssi_min_word_length'] ) ) {
158
+ $value = intval( $_REQUEST['relevanssi_min_word_length'] );
159
+ if ( 0 === $value ) {
160
+ $value = 3;
161
+ }
162
+ update_option( 'relevanssi_min_word_length', $value );
163
  }
164
 
165
+ if ( 'indexing' === $_REQUEST['tab'] ) {
166
+ if ( ! isset( $_REQUEST['relevanssi_index_author'] ) ) {
167
+ $_REQUEST['relevanssi_index_author'] = 'off';
168
  }
169
+
170
+ if ( ! isset( $_REQUEST['relevanssi_index_excerpt'] ) ) {
171
+ $_REQUEST['relevanssi_index_excerpt'] = 'off';
172
  }
173
 
174
+ if ( ! isset( $_REQUEST['relevanssi_expand_shortcodes'] ) ) {
175
+ $_REQUEST['relevanssi_expand_shortcodes'] = 'off';
176
  }
177
  }
178
 
179
+ if ( 'searching' === $_REQUEST['tab'] ) {
180
+ if ( ! isset( $_REQUEST['relevanssi_admin_search'] ) ) {
181
+ $_REQUEST['relevanssi_admin_search'] = 'off';
182
  }
183
 
184
+ if ( ! isset( $_REQUEST['relevanssi_throttle'] ) ) {
185
+ $_REQUEST['relevanssi_throttle'] = 'off';
186
  }
187
 
188
+ if ( ! isset( $_REQUEST['relevanssi_disable_or_fallback'] ) ) {
189
+ $_REQUEST['relevanssi_disable_or_fallback'] = 'off';
190
  }
191
 
192
+ if ( ! isset( $_REQUEST['relevanssi_respect_exclude'] ) ) {
193
+ $_REQUEST['relevanssi_respect_exclude'] = 'off';
194
  }
195
+
196
+ if ( ! isset( $_REQUEST['relevanssi_wpml_only_current'] ) ) {
197
+ $_REQUEST['relevanssi_wpml_only_current'] = 'off';
198
  }
199
+
200
+ if ( ! isset( $_REQUEST['relevanssi_polylang_all_languages'] ) ) {
201
+ $_REQUEST['relevanssi_polylang_all_languages'] = 'off';
202
  }
203
  }
204
 
205
+ if ( 'logging' === $_REQUEST['tab'] ) {
206
+ if ( ! isset( $_REQUEST['relevanssi_log_queries'] ) ) {
207
+ $_REQUEST['relevanssi_log_queries'] = 'off';
208
  }
209
+
210
+ if ( ! isset( $_REQUEST['relevanssi_log_queries_with_ip'] ) ) {
211
+ $_REQUEST['relevanssi_log_queries_with_ip'] = 'off';
212
  }
213
  }
214
 
215
+ if ( 'excerpts' === $_REQUEST['tab'] ) {
216
+ if ( ! isset( $_REQUEST['relevanssi_excerpts'] ) ) {
217
+ $_REQUEST['relevanssi_excerpts'] = 'off';
218
  }
219
 
220
+ if ( ! isset( $_REQUEST['relevanssi_show_matches'] ) ) {
221
+ $_REQUEST['relevanssi_show_matches'] = 'off';
222
  }
223
 
224
+ if ( ! isset( $_REQUEST['relevanssi_hilite_title'] ) ) {
225
+ $_REQUEST['relevanssi_hilite_title'] = 'off';
226
  }
227
+
228
+ if ( ! isset( $_REQUEST['relevanssi_highlight_docs'] ) ) {
229
+ $_REQUEST['relevanssi_highlight_docs'] = 'off';
230
  }
231
+
232
+ if ( ! isset( $_REQUEST['relevanssi_highlight_comments'] ) ) {
233
+ $_REQUEST['relevanssi_highlight_comments'] = 'off';
234
  }
235
 
236
+ if ( ! isset( $_REQUEST['relevanssi_excerpt_custom_fields'] ) ) {
237
+ $_REQUEST['relevanssi_excerpt_custom_fields'] = 'off';
238
  }
239
 
240
+ if ( ! isset( $_REQUEST['relevanssi_word_boundaries'] ) ) {
241
+ $_REQUEST['relevanssi_word_boundaries'] = 'off';
242
  }
243
  }
244
 
245
+ if ( isset( $_REQUEST['relevanssi_excerpt_length'] ) ) {
246
+ $value = intval( $_REQUEST['relevanssi_excerpt_length'] );
247
+ if ( 0 !== $value ) {
248
+ update_option( 'relevanssi_excerpt_length', $value );
249
  }
250
  }
251
 
252
+ if ( isset( $_REQUEST['relevanssi_synonyms'] ) ) {
253
+ $linefeeds = array( "\r\n", "\n", "\r" );
254
+ $value = str_replace( $linefeeds, ';', $_REQUEST['relevanssi_synonyms'] );
255
+ $value = stripslashes( $value );
256
+ update_option( 'relevanssi_synonyms', $value );
257
  }
258
 
259
+ if ( isset( $_REQUEST['relevanssi_show_matches'] ) ) {
260
+ update_option( 'relevanssi_show_matches', $_REQUEST['relevanssi_show_matches'] );
261
+ }
262
+ if ( isset( $_REQUEST['relevanssi_show_matches_text'] ) ) {
263
  $value = $_REQUEST['relevanssi_show_matches_text'];
264
+ $value = str_replace( '"', "'", $value );
265
+ update_option( 'relevanssi_show_matches_text', $value );
266
  }
267
 
268
  $relevanssi_punct = array();
269
+ if ( isset( $_REQUEST['relevanssi_punct_quotes'] ) ) {
270
+ $relevanssi_punct['quotes'] = $_REQUEST['relevanssi_punct_quotes'];
271
+ }
272
+ if ( isset( $_REQUEST['relevanssi_punct_hyphens'] ) ) {
273
+ $relevanssi_punct['hyphens'] = $_REQUEST['relevanssi_punct_hyphens'];
274
+ }
275
+ if ( isset( $_REQUEST['relevanssi_punct_ampersands'] ) ) {
276
+ $relevanssi_punct['ampersands'] = $_REQUEST['relevanssi_punct_ampersands'];
277
+ }
278
+ if ( isset( $_REQUEST['relevanssi_punct_decimals'] ) ) {
279
+ $relevanssi_punct['decimals'] = $_REQUEST['relevanssi_punct_decimals'];
280
+ }
281
+ if ( ! empty( $relevanssi_punct ) ) {
282
+ update_option( 'relevanssi_punctuation', $relevanssi_punct );
283
+ }
284
 
285
+ $post_type_weights = array();
286
+ $index_post_types = array();
287
  $index_taxonomies_list = array();
288
+ $index_terms_list = array();
289
+ foreach ( $_REQUEST as $key => $value ) {
290
+ if ( 'relevanssi_weight_' === substr( $key, 0, strlen( 'relevanssi_weight_' ) ) ) {
291
+ $type = substr( $key, strlen( 'relevanssi_weight_' ) );
292
+ $post_type_weights[ $type ] = $value;
293
  }
294
+ if ( 'relevanssi_index_type_' === substr( $key, 0, strlen( 'relevanssi_index_type_' ) ) ) {
295
+ $type = substr( $key, strlen( 'relevanssi_index_type_' ) );
296
+ if ( 'on' === $value ) {
297
+ $index_post_types[ $type ] = true;
298
+ }
299
  }
300
+ if ( 'relevanssi_index_taxonomy_' === substr( $key, 0, strlen( 'relevanssi_index_taxonomy_' ) ) ) {
301
+ $type = substr( $key, strlen( 'relevanssi_index_taxonomy_' ) );
302
+ if ( 'on' === $value ) {
303
+ $index_taxonomies_list[ $type ] = true;
304
+ }
305
  }
306
+ if ( 'relevanssi_index_terms_' === substr( $key, 0, strlen( 'relevanssi_index_terms_' ) ) ) {
307
+ $type = substr( $key, strlen( 'relevanssi_index_terms_' ) );
308
+ if ( 'on' === $value ) {
309
+ $index_terms_list[ $type ] = true;
310
+ }
311
  }
312
  }
313
 
314
+ if ( 'indexing' === $_REQUEST['tab'] ) {
315
+ update_option( 'relevanssi_index_taxonomies_list', array_keys( $index_taxonomies_list ) );
316
+ if ( RELEVANSSI_PREMIUM ) {
317
+ update_option( 'relevanssi_index_terms', array_keys( $index_terms_list ) );
318
+ }
319
  }
320
 
321
+ if ( count( $post_type_weights ) > 0 ) {
322
+ update_option( 'relevanssi_post_type_weights', $post_type_weights );
323
  }
324
 
325
+ if ( count( $index_post_types ) > 0 ) {
326
+ update_option( 'relevanssi_index_post_types', array_keys( $index_post_types ) );
327
+ }
328
 
329
+ if ( isset( $_REQUEST['relevanssi_index_fields_select'] ) ) {
330
+ $fields_option = '';
331
+ if ( 'all' === $_REQUEST['relevanssi_index_fields_select'] ) {
332
+ $fields_option = 'all';
333
  }
334
+ if ( 'visible' === $_REQUEST['relevanssi_index_fields_select'] ) {
335
+ $fields_option = 'visible';
336
  }
337
+ if ( 'some' === $_REQUEST['relevanssi_index_fields_select'] ) {
338
+ if ( isset( $_REQUEST['relevanssi_index_fields'] ) ) {
339
+ $fields_option = $_REQUEST['relevanssi_index_fields'];
340
+ }
341
  }
342
+ update_option( 'relevanssi_index_fields', $fields_option );
343
  }
344
+
345
+ if ( isset( $_REQUEST['relevanssi_trim_logs'] ) ) {
346
  $trim_logs = $_REQUEST['relevanssi_trim_logs'];
347
+ if ( ! is_numeric( $trim_logs ) || $trim_logs < 0 ) {
348
+ $trim_logs = 0;
349
+ }
350
+ update_option( 'relevanssi_trim_logs', $trim_logs );
351
  }
352
 
353
+ if ( isset( $_REQUEST['relevanssi_cat'] ) ) {
354
+ if ( is_array( $_REQUEST['relevanssi_cat'] ) ) {
355
+ $csv_cats = implode( ',', $_REQUEST['relevanssi_cat'] );
356
+ update_option( 'relevanssi_cat', $csv_cats );
357
  }
358
  } else {
359
+ if ( isset( $_REQUEST['relevanssi_cat_active'] ) ) {
360
+ update_option( 'relevanssi_cat', '' );
361
  }
362
  }
363
 
364
+ if ( isset( $_REQUEST['relevanssi_excat'] ) ) {
365
+ if ( is_array( $_REQUEST['relevanssi_excat'] ) ) {
366
+ $csv_cats = implode( ',', $_REQUEST['relevanssi_excat'] );
367
+ update_option( 'relevanssi_excat', $csv_cats );
368
  }
369
  } else {
370
+ if ( isset( $_REQUEST['relevanssi_excat_active'] ) ) {
371
+ update_option( 'relevanssi_excat', '' );
372
  }
373
  }
374
 
375
+ if ( isset( $_REQUEST['relevanssi_admin_search'] ) ) {
376
+ update_option( 'relevanssi_admin_search', $_REQUEST['relevanssi_admin_search'] );
377
+ }
378
+ if ( isset( $_REQUEST['relevanssi_excerpts'] ) ) {
379
+ update_option( 'relevanssi_excerpts', $_REQUEST['relevanssi_excerpts'] );
380
+ }
381
+ if ( isset( $_REQUEST['relevanssi_excerpt_type'] ) ) {
382
+ update_option( 'relevanssi_excerpt_type', $_REQUEST['relevanssi_excerpt_type'] );
383
+ }
384
+ if ( isset( $_REQUEST['relevanssi_excerpt_allowable_tags'] ) ) {
385
+ update_option( 'relevanssi_excerpt_allowable_tags', $_REQUEST['relevanssi_excerpt_allowable_tags'] );
386
+ }
387
+ if ( isset( $_REQUEST['relevanssi_log_queries'] ) ) {
388
+ update_option( 'relevanssi_log_queries', $_REQUEST['relevanssi_log_queries'] );
389
+ }
390
+ if ( isset( $_REQUEST['relevanssi_log_queries_with_ip'] ) ) {
391
+ update_option( 'relevanssi_log_queries_with_ip', $_REQUEST['relevanssi_log_queries_with_ip'] );
392
+ }
393
+ if ( isset( $_REQUEST['relevanssi_highlight'] ) ) {
394
+ update_option( 'relevanssi_highlight', $_REQUEST['relevanssi_highlight'] );
395
+ }
396
+ if ( isset( $_REQUEST['relevanssi_highlight_docs'] ) ) {
397
+ update_option( 'relevanssi_highlight_docs', $_REQUEST['relevanssi_highlight_docs'] );
398
+ }
399
+ if ( isset( $_REQUEST['relevanssi_highlight_comments'] ) ) {
400
+ update_option( 'relevanssi_highlight_comments', $_REQUEST['relevanssi_highlight_comments'] );
401
+ }
402
+ if ( isset( $_REQUEST['relevanssi_txt_col'] ) ) {
403
+ update_option( 'relevanssi_txt_col', $_REQUEST['relevanssi_txt_col'] );
404
+ }
405
+ if ( isset( $_REQUEST['relevanssi_bg_col'] ) ) {
406
+ update_option( 'relevanssi_bg_col', $_REQUEST['relevanssi_bg_col'] );
407
+ }
408
+ if ( isset( $_REQUEST['relevanssi_css'] ) ) {
409
+ update_option( 'relevanssi_css', $_REQUEST['relevanssi_css'] );
410
+ }
411
+ if ( isset( $_REQUEST['relevanssi_class'] ) ) {
412
+ update_option( 'relevanssi_class', $_REQUEST['relevanssi_class'] );
413
+ }
414
+ if ( isset( $_REQUEST['relevanssi_expst'] ) ) {
415
+ update_option( 'relevanssi_exclude_posts', $_REQUEST['relevanssi_expst'] );
416
+ }
417
+ if ( isset( $_REQUEST['relevanssi_hilite_title'] ) ) {
418
+ update_option( 'relevanssi_hilite_title', $_REQUEST['relevanssi_hilite_title'] );
419
+ }
420
+ if ( isset( $_REQUEST['relevanssi_index_comments'] ) ) {
421
+ update_option( 'relevanssi_index_comments', $_REQUEST['relevanssi_index_comments'] );
422
+ }
423
+ if ( isset( $_REQUEST['relevanssi_index_author'] ) ) {
424
+ update_option( 'relevanssi_index_author', $_REQUEST['relevanssi_index_author'] );
425
+ }
426
+ if ( isset( $_REQUEST['relevanssi_index_excerpt'] ) ) {
427
+ update_option( 'relevanssi_index_excerpt', $_REQUEST['relevanssi_index_excerpt'] );
428
+ }
429
+ if ( isset( $_REQUEST['relevanssi_fuzzy'] ) ) {
430
+ update_option( 'relevanssi_fuzzy', $_REQUEST['relevanssi_fuzzy'] );
431
+ }
432
+ if ( isset( $_REQUEST['relevanssi_expand_shortcodes'] ) ) {
433
+ update_option( 'relevanssi_expand_shortcodes', $_REQUEST['relevanssi_expand_shortcodes'] );
434
+ }
435
+ if ( isset( $_REQUEST['relevanssi_implicit_operator'] ) ) {
436
+ update_option( 'relevanssi_implicit_operator', $_REQUEST['relevanssi_implicit_operator'] );
437
+ }
438
+ if ( isset( $_REQUEST['relevanssi_omit_from_logs'] ) ) {
439
+ update_option( 'relevanssi_omit_from_logs', $_REQUEST['relevanssi_omit_from_logs'] );
440
+ }
441
+ if ( isset( $_REQUEST['relevanssi_index_limit'] ) ) {
442
+ update_option( 'relevanssi_index_limit', $_REQUEST['relevanssi_index_limit'] );
443
+ }
444
+ if ( isset( $_REQUEST['relevanssi_disable_or_fallback'] ) ) {
445
+ update_option( 'relevanssi_disable_or_fallback', $_REQUEST['relevanssi_disable_or_fallback'] );
446
+ }
447
+ if ( isset( $_REQUEST['relevanssi_respect_exclude'] ) ) {
448
+ update_option( 'relevanssi_respect_exclude', $_REQUEST['relevanssi_respect_exclude'] );
449
+ }
450
+ if ( isset( $_REQUEST['relevanssi_throttle'] ) ) {
451
+ update_option( 'relevanssi_throttle', $_REQUEST['relevanssi_throttle'] );
452
+ }
453
+ if ( isset( $_REQUEST['relevanssi_wpml_only_current'] ) ) {
454
+ update_option( 'relevanssi_wpml_only_current', $_REQUEST['relevanssi_wpml_only_current'] );
455
+ }
456
+ if ( isset( $_REQUEST['relevanssi_polylang_all_languages'] ) ) {
457
+ update_option( 'relevanssi_polylang_all_languages', $_REQUEST['relevanssi_polylang_all_languages'] );
458
+ }
459
+ if ( isset( $_REQUEST['relevanssi_word_boundaries'] ) ) {
460
+ update_option( 'relevanssi_word_boundaries', $_REQUEST['relevanssi_word_boundaries'] );
461
+ }
462
+ if ( isset( $_REQUEST['relevanssi_default_orderby'] ) ) {
463
+ update_option( 'relevanssi_default_orderby', $_REQUEST['relevanssi_default_orderby'] );
464
+ }
465
+ if ( isset( $_REQUEST['relevanssi_excerpt_custom_fields'] ) ) {
466
+ update_option( 'relevanssi_excerpt_custom_fields', $_REQUEST['relevanssi_excerpt_custom_fields'] );
467
+ }
468
+ if ( isset( $_REQUEST['relevanssi_exact_match_bonus'] ) ) {
469
+ update_option( 'relevanssi_exact_match_bonus', $_REQUEST['relevanssi_exact_match_bonus'] );
470
+ }
471
 
472
+ if ( function_exists( 'relevanssi_update_premium_options' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  relevanssi_update_premium_options();
474
  }
475
+ // phpcs:enable
476
  }
477
 
478
+ /**
479
+ * Adds a stopword to the list of stopwords.
480
+ *
481
+ * @global object $wpdb The WP database interface.
482
+ *
483
+ * @param string $term The stopword that is added.
484
+ * @param boolean $verbose If true, print out notice. If false, be silent. Default
485
+ * true.
486
+ *
487
+ * @return boolean True, if success; false otherwise.
488
+ */
489
+ function relevanssi_add_stopword( $term, $verbose = true ) {
490
  global $wpdb;
491
+ if ( empty( $term ) ) {
492
+ return;
493
+ }
494
 
495
  $n = 0;
496
  $s = 0;
497
 
498
+ $terms = explode( ',', $term );
499
+ if ( count( $terms ) > 1 ) {
500
+ foreach ( $terms as $term ) {
501
  $n++;
502
+ $term = trim( $term );
503
+ $success = relevanssi_add_single_stopword( $term );
504
+ if ( $success ) {
505
+ $s++;
506
+ }
507
  }
508
+ if ( $verbose ) {
509
+ // translators: %1$d is the successful entries, %2$d is the total entries.
510
+ printf( "<div id='message' class='updated fade'><p>%s</p></div>", sprintf( esc_html__( 'Successfully added %1$d/%2$d terms to stopwords!', 'relevanssi' ), intval( $s ), intval( $n ) ) );
 
 
 
 
 
511
  }
512
+ } else {
513
+ // Add to stopwords.
514
+ $success = relevanssi_add_single_stopword( $term );
515
+
516
+ $term = stripslashes( $term );
517
+ $term = esc_html( $term );
518
+ if ( $verbose ) {
519
+ if ( $success ) {
520
+ // Translators: %s is the stopword.
521
+ printf( "<div id='message' class='updated fade'><p>%s</p></div>", sprintf( esc_html__( "Term '%s' added to stopwords!", 'relevanssi' ), esc_html( stripslashes( $term ) ) ) );
522
+ } else {
523
+ // Translators: %s is the stopword.
524
+ printf( esc_html__( "<div id='message' class='updated fade'><p>Couldn't add term '%s' to stopwords!</p></div>", 'relevanssi' ), esc_html( stripslashes( $term ) ) );
525
+ }
526
  }
527
  }
528
+
529
+ return $success;
530
  }
531
 
532
+ /**
533
+ * Adds a single stopword to the stopword table.
534
+ *
535
+ * @global object $wpdb The WP database interface.
536
+ * @global array $relevanssi_variables The global Relevanssi variables.
537
+ *
538
+ * @param string $term The term to add.
539
+ *
540
+ * @return boolean True if success, false if not.
541
+ */
542
+ function relevanssi_add_single_stopword( $term ) {
543
  global $wpdb, $relevanssi_variables;
544
+ if ( empty( $term ) ) {
545
+ return false;
 
 
 
 
 
 
 
 
546
  }
547
 
548
+ $term = stripslashes( $term );
549
+ $term = esc_sql( $wpdb->esc_like( $term ) );
550
+
551
+ $success = $wpdb->query( $wpdb->prepare( 'INSERT INTO ' . $relevanssi_variables['stopword_table'] . ' (stopword) VALUES (%s)', $term ) ); // WPCS: unprepared SQL ok, Relevanssi table name.
552
 
553
+ if ( $success ) {
554
+ // Remove from index.
555
+ $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $relevanssi_variables['relevanssi_table'] . ' WHERE term=%s', $term ) ); // WPCS: unprepared SQL ok, Relevanssi table name.
 
556
  return true;
557
+ } else {
 
558
  return false;
559
  }
560
  }
561
 
562
+ /**
563
+ * Removes all stopwords.
564
+ *
565
+ * Truncates the wp_relevanssi_stopwords database table.
566
+ *
567
+ * @global object $wpdb The WP database interface.
568
+ * @global array $relevanssi_variables The global Relevanssi variables.
569
+ */
570
  function relevanssi_remove_all_stopwords() {
571
  global $wpdb, $relevanssi_variables;
572
 
573
+ $success = $wpdb->query( 'TRUNCATE ' . $relevanssi_variables['stopword_table'] ); // WPCS: unprepared SQL ok, Relevanssi table name.
574
 
575
+ if ( $success ) {
576
+ printf( "<div id='message' class='updated fade'><p>%s</p></div>", esc_html__( 'All stopwords removed! Remember to re-index.', 'relevanssi' ) );
577
+ } else {
578
+ printf( "<div id='message' class='updated fade'><p>%s</p></div>", esc_html__( "There was a problem, and stopwords couldn't be removed.", 'relevanssi' ) );
579
+ }
580
  }
581
 
582
+ /**
583
+ * Removes a single stopword.
584
+ *
585
+ * @global object $wpdb The WP database interface.
586
+ * @global array $relevanssi_variables The global Relevanssi variables.
587
+ *
588
+ * @param string $term The stopword to remove.
589
+ * @param boolean $verbose If true, print out a notice. Default true.
590
+ *
591
+ * @return boolean True if success, false if not.
592
+ */
593
+ function relevanssi_remove_stopword( $term, $verbose = true ) {
594
  global $wpdb, $relevanssi_variables;
595
 
596
+ $success = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $relevanssi_variables['stopword_table'] . ' WHERE stopword=%s', $term ) ); // WPCS: unprepared SQL ok, Relevanssi table name.
 
597
 
598
+ if ( $success ) {
599
+ if ( $verbose ) {
600
+ // Translators: %s is the stopword.
601
+ printf( "<div id='message' class='updated fade'><p>%s</p></div>", sprintf( esc_html__( "Term '%s' removed from stopwords! Re-index to get it back to index.", 'relevanssi' ), esc_html( stripslashes( $term ) ) ) );
 
602
  }
603
+ return true;
604
+ } else {
605
+ if ( $verbose ) {
606
+ // Translators: %s is the stopword.
607
+ printf( "<div id='message' class='updated fade'><p>%s</p></div>", sprintf( esc_html__( "Couldn't remove term '%s' from stopwords!", 'relevanssi' ), esc_html( stripslashes( $term ) ) ) );
 
 
 
 
 
 
 
608
  }
609
+ return false;
610
  }
611
  }
612
 
613
+ /**
614
+ * Displays the list of most common words in the index.
615
+ *
616
+ * @global object $wpdb The WP database interface.
617
+ * @global array $relevanssi_variables The global Relevanssi variables.
618
+ *
619
+ * @param int $limit How many words to display, default 25.
620
+ * @param boolean $wp_cli If true, return just a list of words. If false, print out
621
+ * HTML code.
622
+ *
623
+ * @return array A list of words, if $wp_cli is true.
624
+ */
625
+ function relevanssi_common_words( $limit = 25, $wp_cli = false ) {
626
+ global $wpdb, $relevanssi_variables;
627
 
628
+ $plugin = 'relevanssi';
629
+ if ( RELEVANSSI_PREMIUM ) {
630
+ $plugin = 'relevanssi-premium';
631
+ }
632
 
633
+ if ( ! is_numeric( $limit ) ) {
634
+ $limit = 25;
635
+ }
636
 
637
+ $words = $wpdb->get_results( 'SELECT COUNT(*) as cnt, term FROM ' . $relevanssi_variables['relevanssi_table'] . " GROUP BY term ORDER BY cnt DESC LIMIT $limit" ); // WPCS: unprepared sql ok, Relevanssi table name and $limit is numeric.
 
638
 
639
+ if ( ! $wp_cli ) {
640
+ printf( '<h2>%s</h2>', esc_html__( '25 most common words in the index', 'relevanssi' ) );
641
+ printf( '<p>%s</p>', esc_html__( "These words are excellent stopword material. A word that appears in most of the posts in the database is quite pointless when searching. This is also an easy way to create a completely new stopword list, if one isn't available in your language. Click the icon after the word to add the word to the stopword list. The word will also be removed from the index, so rebuilding the index is not necessary.", 'relevanssi' ) );
642
 
643
  ?>
644
  <input type="hidden" name="dowhat" value="add_stopword" />
645
  <table class="form-table">
646
  <tr>
647
+ <th scope="row"><?php esc_html_e( 'Stopword Candidates', 'relevanssi' ); ?></th>
648
  <td>
649
  <ul>
650
+ <?php
651
+ $src = plugins_url( 'delete.png', $relevanssi_variables['file'] );
 
652
 
653
+ foreach ( $words as $word ) {
654
+ $stop = __( 'Add to stopwords', 'relevanssi' );
655
+ printf( '<li>%1$s (%2$d) <input style="padding: 0; margin: 0" type="image" src="%3$s" alt="%4$s" name="term" value="%5$s"/></li>', esc_html( $word->term ), esc_html( $word->cnt ), esc_attr( $src ), esc_attr( $stop ), esc_attr( $word->term ) );
656
+ }
657
  ?>
658
  </ul>
659
  </td>
662
  <?php
663
 
664
  }
665
+
666
+ return $words;
 
 
667
  }
668
 
669
+ /**
670
+ * Shows the query log with the most common queries
671
+ *
672
+ * Uses relevanssi_total_queries() and relevanssi_date_queries() to fetch the data.
673
+ */
674
  function relevanssi_query_log() {
675
+ /**
676
+ * Adjusts the number of days to show the logs in User searches page.
677
+ *
678
+ * @param int Number of days, default 30.
679
+ */
680
+ $days30 = apply_filters( 'relevanssi_30days', 30 );
681
 
682
+ printf( '<h3>%s</h3>', esc_html__( 'Total Searches', 'relevanssi' ) );
683
 
684
+ printf( "<div style='width: 50%%; overflow: auto'>%s</div>", relevanssi_total_queries( __( 'Totals', 'relevanssi' ) ) ); // WPCS: XSS ok, already escaped by relevanssi_total_queries().
 
 
 
 
685
 
686
  echo '<div style="clear: both"></div>';
687
 
688
+ printf( '<h3>%s</h3>', esc_html__( 'Common Queries', 'relevanssi' ) );
689
+
690
+ /**
691
+ * Filters the number of rows to show.
692
+ *
693
+ * @param int Number of top results to show, default 20.
694
+ */
695
+ $limit = apply_filters( 'relevanssi_user_searches_limit', 20 );
696
 
697
+ // Translators: %d is the number of queries to show.
698
+ printf( '<p>%s</p>', esc_html( sprintf( __( 'Here you can see the %d most common user search queries, how many times those queries were made and how many results were found for those queries.', 'relevanssi' ), $limit ) ) );
 
699
 
700
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
701
+ relevanssi_date_queries( 1, __( 'Today and yesterday', 'relevanssi' ) );
702
  echo '</div>';
703
 
704
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
705
+ relevanssi_date_queries( 7, __( 'Last 7 days', 'relevanssi' ) );
706
  echo '</div>';
707
 
708
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
709
+ // Translators: number of days to show.
710
+ relevanssi_date_queries( $days30, sprintf( __( 'Last %d days', 'relevanssi' ), $days30 ) );
711
  echo '</div>';
712
 
713
  echo '<div style="clear: both"></div>';
714
 
715
+ printf( '<h3>%s</h3>', esc_html__( 'Unsuccessful Queries', 'relevanssi' ) );
716
 
717
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
718
+ relevanssi_date_queries( 1, __( 'Today and yesterday', 'relevanssi' ), 'bad' );
719
  echo '</div>';
720
 
721
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
722
+ relevanssi_date_queries( 7, __( 'Last 7 days', 'relevanssi' ), 'bad' );
723
  echo '</div>';
724
 
725
  echo "<div style='width: 30%; float: left; margin-right: 2%; overflow: auto'>";
726
+ // Translators: number of days to show.
727
+ relevanssi_date_queries( $days30, sprintf( __( 'Last %d days', 'relevanssi' ), $days30 ), 'bad' );
728
  echo '</div>';
729
 
730
+ if ( current_user_can( 'manage_options' ) ) {
731
 
732
  echo '<div style="clear: both"></div>';
733
+ printf( '<h3>%s</h3>', esc_html__( 'Reset Logs', 'relevanssi' ) );
734
+ print( "<form method='post'>" );
735
+ wp_nonce_field( 'relevanssi_reset_logs', '_relresnonce', true, true );
736
+ // Translators: %1$s is the input field, %2$s is the submit button.
737
+ printf( '<p>%s</p></form>', esc_html( sprintf( __( 'To reset the logs, type "reset" into the box here %1$s and click %2$s', 'relevanssi' ), ' <input type="text" name="relevanssi_reset_code" />', ' <input type="submit" name="relevanssi_reset" value="Reset" class="button" />' ) ) );
 
738
 
739
  }
740
 
741
+ echo '</div>';
742
  }
743
 
744
+ /**
745
+ * Shows the total number of searches on 'User searches' page.
746
+ *
747
+ * @global object $wpdb The WP database interface.
748
+ * @global array $relevanssi_variables The global Relevanssi variables array.
749
+ *
750
+ * @param string $title The title that is printed out on top of the results.
751
+ */
752
  function relevanssi_total_queries( $title ) {
753
  global $wpdb, $relevanssi_variables;
754
  $log_table = $relevanssi_variables['log_table'];
755
 
756
+ $count = array();
757
+ $titles = array();
758
 
759
+ $titles[0] = __( 'Today and yesterday', 'relevanssi' );
760
+ $titles[1] = __( 'Last 7 days', 'relevanssi' );
761
+ $titles[2] = __( 'Last 30 days', 'relevanssi' );
762
+ $titles[3] = __( 'Forever', 'relevanssi' );
763
 
764
+ $count[0] = $wpdb->get_var( "SELECT COUNT(id) FROM $log_table WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= 1;" ); // WPCS: unprepared SQL ok, Relevanssi table name.
765
+ $count[1] = $wpdb->get_var( "SELECT COUNT(id) FROM $log_table WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= 7;" ); // WPCS: unprepared SQL ok, Relevanssi table name.
766
+ $count[2] = $wpdb->get_var( "SELECT COUNT(id) FROM $log_table WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= 30;" ); // WPCS: unprepared SQL ok, Relevanssi table name.
767
+ $count[3] = $wpdb->get_var( "SELECT COUNT(id) FROM $log_table;" ); // WPCS: unprepared SQL ok, Relevanssi table name.
768
+
769
+ printf( '<table class="widefat"><thead><tr><th colspan="2">%1$s</th></tr></thead><tbody><tr><th>%2$s</th><th style="text-align: center">%3$s</th></tr>',
770
+ esc_html( $title ), esc_html__( 'When', 'relevanssi' ), esc_html__( 'Searches', 'relevanssi' ) );
771
 
772
+ foreach ( $count as $key => $searches ) {
773
+ $when = $titles[ $key ];
774
+ printf( "<tr><td>%s</td><td style='text-align: center'>%d</td></tr>", esc_html( $when ), intval( $searches ) );
775
+ }
776
+ echo '</tbody></table>';
777
  }
778
 
779
+ /**
780
+ * Shows the most common search queries on different time periods.
781
+ *
782
+ * @global object $wpdb The WP database interface.
783
+ * @global array $relevanssi_variables The global Relevanssi variables array.
784
+ *
785
+ * @param int $days The number of days to show.
786
+ * @param string $title The title that is printed out on top of the results.
787
+ * @param string $version If 'good', show the searches that found something; if
788
+ * 'bad', show the searches that didn't find anything. Default 'good'.
789
+ */
790
+ function relevanssi_date_queries( $days, $title, $version = 'good' ) {
791
  global $wpdb, $relevanssi_variables;
792
  $log_table = $relevanssi_variables['log_table'];
793
 
794
+ /** Documented in lib/interface.php. */
795
+ $limit = apply_filters( 'relevanssi_user_searches_limit', 20 );
796
+
797
+ if ( 'good' === $version ) {
798
+ $queries = $wpdb->get_results(
799
+ $wpdb->prepare(
800
+ "SELECT COUNT(DISTINCT(id)) as cnt, query, hits
801
+ FROM $log_table
802
+ WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= %d
803
+ GROUP BY query
804
+ ORDER BY cnt DESC
805
+ LIMIT %d",
806
+ $days, $limit
807
+ )
808
+ ); // WPCS: unprepared SQL ok, Relevanssi table name.
809
+ }
810
+
811
+ if ( 'bad' === $version ) {
812
+ $queries = $wpdb->get_results(
813
+ $wpdb->prepare(
814
+ "SELECT COUNT(DISTINCT(id)) as cnt, query, hits
815
+ FROM $log_table
816
+ WHERE TIMESTAMPDIFF(DAY, time, NOW()) <= %d AND hits = 0
817
+ GROUP BY query
818
+ ORDER BY cnt DESC
819
+ LIMIT %d",
820
+ $days, $limit
821
+ )
822
+ ); // WPCS: unprepared SQL ok, Relevanssi table name.
823
+ }
824
+
825
+ if ( count( $queries ) > 0 ) {
826
+ printf( "<table class='widefat'><thead><tr><th colspan='3'>%s</th></tr></thead><tbody><tr><th>%s</th><th style='text-align: center'>#</th><th style='text-align: center'>%s</th></tr>",
827
+ esc_html( $title ), esc_html__( 'Query', 'relevanssi' ), esc_html__( 'Hits', 'relevanssi' ) );
828
+ $url = get_bloginfo( 'url' );
829
+ foreach ( $queries as $query ) {
830
+ $search_parameter = rawurlencode( $query->query );
831
+ $query_url = $url . '/?s=' . $search_parameter;
832
+ printf( "<tr><td><a href='%s'>%s</a></td><td style='padding: 3px 5px; text-align: center'>%d</td><td style='padding: 3px 5px; text-align: center'>%d</td></tr>",
833
+ esc_attr( $query_url ), esc_attr( $query->query ), intval( $query->cnt ), intval( $query->hits ) );
834
  }
835
+ echo '</tbody></table>';
836
  }
837
  }
838
 
839
+ /**
840
+ * Returns 'checked' if the option is enabled.
841
+ *
842
+ * @param string $option Value to check.
843
+ *
844
+ * @return string If the option is 'on', returns 'checked', otherwise returns an
845
+ * empty string.
846
+ */
847
+ function relevanssi_check( $option ) {
848
+ $checked = '';
849
+ if ( 'on' === $option ) {
850
+ $checked = 'checked';
851
+ }
852
+ return $checked;
853
+ }
854
+
855
+ /**
856
+ * Returns 'selected' if the option matches a value.
857
+ *
858
+ * @param string $option Value to check.
859
+ * @param string $value The 'selected' value.
860
+ *
861
+ * @return string If the option matches the value, returns 'selected', otherwise
862
+ * returns an empty string.
863
+ */
864
+ function relevanssi_select( $option, $value ) {
865
+ $selected = '';
866
+ if ( $option === $value ) {
867
+ $selected = 'selected';
868
+ }
869
+ return $selected;
870
+ }
871
+
872
+ /**
873
+ * Prints out the Relevanssi options form.
874
+ *
875
+ * @global object $wpdb The WP database interface.
876
+ * @global array $relevanssi_variables The global Relevanssi variables array.
877
+ */
878
  function relevanssi_options_form() {
879
  global $relevanssi_variables, $wpdb;
880
 
881
+ wp_enqueue_style( 'dashboard' );
882
+ wp_print_styles( 'dashboard' );
883
+ wp_enqueue_script( 'dashboard' );
884
+ wp_print_scripts( 'dashboard' );
885
 
886
  $serialize_options = array();
887
 
888
+ $content_boost = get_option( 'relevanssi_content_boost' );
889
+ $title_boost = get_option( 'relevanssi_title_boost' );
890
+ $comment_boost = get_option( 'relevanssi_comment_boost' );
891
+ $admin_search = get_option( 'relevanssi_admin_search' );
892
+ $index_limit = get_option( 'relevanssi_index_limit' );
893
+ $excerpts = get_option( 'relevanssi_excerpts' );
894
+ $excerpt_length = get_option( 'relevanssi_excerpt_length' );
895
+ $excerpt_type = get_option( 'relevanssi_excerpt_type' );
896
+ $excerpt_allowable_tags = get_option( 'relevanssi_excerpt_allowable_tags' );
897
+ $excerpt_custom_fields = get_option( 'relevanssi_excerpt_custom_fields' );
898
+ $log_queries = get_option( 'relevanssi_log_queries' );
899
+ $log_queries_with_ip = get_option( 'relevanssi_log_queries_with_ip' );
900
+ $trim_logs = get_option( 'relevanssi_trim_logs' );
901
+ $hide_branding = get_option( 'relevanssi_hide_branding' );
902
+ $highlight = get_option( 'relevanssi_highlight' );
903
+ $index_fields = get_option( 'relevanssi_index_fields' );
904
+ $txt_col = get_option( 'relevanssi_txt_col' );
905
+ $bg_col = get_option( 'relevanssi_bg_col' );
906
+ $css = get_option( 'relevanssi_css' );
907
+ $class = get_option( 'relevanssi_class' );
908
+ $cat = get_option( 'relevanssi_cat' );
909
+ $excat = get_option( 'relevanssi_excat' );
910
+ $fuzzy = get_option( 'relevanssi_fuzzy' );
911
+ $implicit = get_option( 'relevanssi_implicit_operator' );
912
+ $expand_shortcodes = get_option( 'relevanssi_expand_shortcodes' );
913
+ $disable_or_fallback = get_option( 'relevanssi_disable_or_fallback' );
914
+ $throttle = get_option( 'relevanssi_throttle' );
915
+ $throttle_limit = get_option( 'relevanssi_throttle_limit' );
916
+ $omit_from_logs = get_option( 'relevanssi_omit_from_logs' );
917
+ $synonyms = get_option( 'relevanssi_synonyms' );
918
+ $exclude_posts = get_option( 'relevanssi_exclude_posts' );
919
+ $highlight_title = get_option( 'relevanssi_hilite_title' );
920
+ $index_comments = get_option( 'relevanssi_index_comments' );
921
+ $highlight_docs = get_option( 'relevanssi_highlight_docs' );
922
+ $highlight_coms = get_option( 'relevanssi_highlight_comments' );
923
+ $respect_exclude = get_option( 'relevanssi_respect_exclude' );
924
+ $min_word_length = get_option( 'relevanssi_min_word_length' );
925
+ $index_author = get_option( 'relevanssi_index_author' );
926
+ $index_excerpt = get_option( 'relevanssi_index_excerpt' );
927
+ $show_matches = get_option( 'relevanssi_show_matches' );
928
+ $show_matches_text = get_option( 'relevanssi_show_matches_text' );
929
+ $wpml_only_current = get_option( 'relevanssi_wpml_only_current' );
930
+ $polylang_allow_all = get_option( 'relevanssi_polylang_all_languages' );
931
+ $word_boundaries = get_option( 'relevanssi_word_boundaries' );
932
+ $post_type_weights = get_option( 'relevanssi_post_type_weights' );
933
+ $index_post_types = get_option( 'relevanssi_index_post_types' );
934
+ $index_taxonomies_list = get_option( 'relevanssi_index_taxonomies_list' );
935
+ $orderby = get_option( 'relevanssi_default_orderby' );
936
+ $punctuation = get_option( 'relevanssi_punctuation' );
937
+ $exact_match_bonus = get_option( 'relevanssi_exact_match_bonus' );
938
+
939
+ if ( '#' !== substr( $txt_col, 0, 1 ) ) {
940
+ $txt_col = '#' . $txt_col;
941
+ }
942
+ $txt_col = relevanssi_sanitize_hex_color( $txt_col );
943
+ if ( '#' !== substr( $bg_col, 0, 1 ) ) {
944
+ $bg_col = '#' . $bg_col;
945
+ }
946
+ $bg_col = relevanssi_sanitize_hex_color( $bg_col );
947
 
948
+ if ( empty( $index_post_types ) ) {
949
+ $index_post_types = array();
950
+ }
951
+ if ( empty( $index_taxonomies_list ) ) {
952
+ $index_taxonomies_list = array();
953
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
954
 
955
+ $serialize_options['relevanssi_content_boost'] = $content_boost;
956
+ $serialize_options['relevanssi_title_boost'] = $title_boost;
957
+ $serialize_options['relevanssi_comment_boost'] = $comment_boost;
958
+ $serialize_options['relevanssi_admin_search'] = $admin_search;
959
+ $serialize_options['relevanssi_index_limit'] = $index_limit;
960
+ $serialize_options['relevanssi_excerpts'] = $excerpts;
961
+ $serialize_options['relevanssi_excerpt_length'] = $excerpt_length;
962
+ $serialize_options['relevanssi_excerpt_type'] = $excerpt_type;
963
+ $serialize_options['relevanssi_excerpt_allowable_tags'] = $excerpt_allowable_tags;
964
+ $serialize_options['relevanssi_excerpt_custom_fields'] = $excerpt_custom_fields;
965
+ $serialize_options['relevanssi_log_queries'] = $log_queries;
966
+ $serialize_options['relevanssi_log_queries_with_ip'] = $log_queries_with_ip;
967
+ $serialize_options['relevanssi_trim_logs'] = $trim_logs;
968
+ $serialize_options['relevanssi_hide_branding'] = $hide_branding;
969
+ $serialize_options['relevanssi_highlight'] = $highlight;
970
+ $serialize_options['relevanssi_index_fields'] = $index_fields;
971
+ $serialize_options['relevanssi_txt_col'] = $txt_col;
972
+ $serialize_options['relevanssi_bg_col'] = $bg_col;
973
+ $serialize_options['relevanssi_css'] = $css;
974
+ $serialize_options['relevanssi_class'] = $class;
975
+ $serialize_options['relevanssi_cat'] = $cat;
976
+ $serialize_options['relevanssi_excat'] = $excat;
977
+ $serialize_options['relevanssi_fuzzy'] = $fuzzy;
978
+ $serialize_options['relevanssi_implicit_operator'] = $implicit;
979
+ $serialize_options['relevanssi_expand_shortcodes'] = $expand_shortcodes;
980
+ $serialize_options['relevanssi_disable_or_fallback'] = $disable_or_fallback;
981
+ $serialize_options['relevanssi_throttle'] = $throttle;
982
+ $serialize_options['relevanssi_throttle_limit'] = $throttle_limit;
983
+ $serialize_options['relevanssi_omit_from_logs'] = $omit_from_logs;
984
+ $serialize_options['relevanssi_synonyms'] = $synonyms;
985
+ $serialize_options['relevanssi_exclude_posts'] = $exclude_posts;
986
+ $serialize_options['relevanssi_hilite_title'] = $highlight_title;
987
+ $serialize_options['relevanssi_index_comments'] = $index_comments;
988
+ $serialize_options['relevanssi_highlight_docs'] = $highlight_docs;
989
+ $serialize_options['relevanssi_highlight_comments'] = $highlight_coms;
990
+ $serialize_options['relevanssi_respect_exclude'] = $respect_exclude;
991
+ $serialize_options['relevanssi_min_word_length'] = $min_word_length;
992
+ $serialize_options['relevanssi_index_author'] = $index_author;
993
+ $serialize_options['relevanssi_index_excerpt'] = $index_excerpt;
994
+ $serialize_options['relevanssi_show_matches'] = $show_matches;
995
+ $serialize_options['relevanssi_show_matches_text'] = $show_matches_text;
996
+ $serialize_options['relevanssi_wpml_only_current'] = $wpml_only_current;
997
+ $serialize_options['relevanssi_polylang_all_languages'] = $polylang_allow_all;
998
+ $serialize_options['relevanssi_word_boundaries'] = $word_boundaries;
999
+ $serialize_options['relevanssi_post_type_weights'] = $post_type_weights;
1000
+ $serialize_options['relevanssi_index_post_types'] = $index_post_types;
1001
+ $serialize_options['relevanssi_index_taxonomies_list'] = $index_taxonomies_list;
1002
+ $serialize_options['relevanssi_default_orderby'] = $orderby;
1003
+ $serialize_options['relevanssi_punctuation'] = $punctuation;
1004
+ $serialize_options['relevanssi_exact_match_bonus'] = $exact_match_bonus;
1005
+
1006
+ $admin_search = relevanssi_check( $admin_search );
1007
+ $excerpts = relevanssi_check( $excerpts );
1008
+ $excerpt_custom_fields = relevanssi_check( $excerpt_custom_fields );
1009
+ $log_queries = relevanssi_check( $log_queries );
1010
+ $log_queries_with_ip = relevanssi_check( $log_queries_with_ip );
1011
+ $hide_branding = relevanssi_check( $hide_branding );
1012
+ $expand_shortcodes = relevanssi_check( $expand_shortcodes );
1013
+ $disable_or_fallback = relevanssi_check( $disable_or_fallback );
1014
+ $throttle = relevanssi_check( $throttle );
1015
+ $highlight_title = relevanssi_check( $highlight_title );
1016
+ $highlight_docs = relevanssi_check( $highlight_docs );
1017
+ $highlight_coms = relevanssi_check( $highlight_coms );
1018
+ $respect_exclude = relevanssi_check( $respect_exclude );
1019
+ $index_author = relevanssi_check( $index_author );
1020
+ $index_excerpt = relevanssi_check( $index_excerpt );
1021
+ $show_matches = relevanssi_check( $show_matches );
1022
+ $wpml_only_current = relevanssi_check( $wpml_only_current );
1023
+ $polylang_allow_all = relevanssi_check( $polylang_allow_all );
1024
+ $word_boundaries = relevanssi_check( $word_boundaries );
1025
+ $exact_match_bonus = relevanssi_check( $exact_match_bonus );
1026
+
1027
+ $excerpt_chars = relevanssi_select( $excerpt_type, 'chars' );
1028
+ $excerpt_words = relevanssi_select( $excerpt_type, 'words' );
1029
+ $fuzzy_sometimes = relevanssi_select( $fuzzy, 'sometimes' );
1030
+ $fuzzy_always = relevanssi_select( $fuzzy, 'always' );
1031
+ $fuzzy_never = relevanssi_select( $fuzzy, 'never' );
1032
+ $highlight_none = relevanssi_select( $highlight, 'no' );
1033
+ $highlight_mark = relevanssi_select( $highlight, 'mark' );
1034
+ $highlight_em = relevanssi_select( $highlight, 'em' );
1035
+ $highlight_strong = relevanssi_select( $highlight, 'strong' );
1036
+ $highlight_col = relevanssi_select( $highlight, 'col' );
1037
+ $highlight_bgcol = relevanssi_select( $highlight, 'bgcol' );
1038
+ $highlight_style = relevanssi_select( $highlight, 'style' );
1039
+ $highlight_class = relevanssi_select( $highlight, 'class' );
1040
+ $implicit_and = relevanssi_select( $implicit, 'AND' );
1041
+ $implicit_or = relevanssi_select( $implicit, 'OR' );
1042
+ $index_comments_all = relevanssi_select( $index_comments, 'all' );
1043
+ $index_comments_normal = relevanssi_select( $index_comments, 'normal' );
1044
+ $index_comments_none = relevanssi_select( $index_comments, 'none' );
1045
+ $orderby_relevance = relevanssi_select( $orderby, 'relevance' );
1046
+ $orderby_date = relevanssi_select( $orderby, 'post_date' );
1047
+
1048
+ $show_matches_text = stripslashes( $show_matches_text );
1049
+
1050
+ $txt_col_display = 'screen-reader-text';
1051
+ $bg_col_display = 'screen-reader-text';
1052
+ $css_display = 'screen-reader-text';
1053
+ $class_display = 'screen-reader-text';
1054
+
1055
+ if ( 'col' === $highlight ) {
1056
+ $txt_col_display = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1057
  }
1058
+ if ( 'bgcol' === $highlight ) {
1059
+ $bg_col_display = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1060
  }
1061
+ if ( 'style' === $highlight ) {
1062
+ $css_display = '';
1063
+ }
1064
+ if ( 'class' === $highlight ) {
1065
+ $class_display = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1066
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1067
 
1068
+ $orfallback_visibility = 'screen-reader-text';
1069
+ if ( 'AND' === $implicit ) {
1070
+ $orfallback_visibility = '';
1071
+ }
1072
 
1073
+ $fields_select_all = '';
1074
+ $fields_select_none = '';
1075
+ $fields_select_some = 'selected';
1076
+ $fields_select_visible = '';
1077
+ $original_index_fields = $index_fields;
1078
 
1079
+ if ( empty( $index_fields ) ) {
1080
+ $fields_select_none = 'selected';
1081
+ $fields_select_some = '';
1082
+ }
1083
+ if ( 'all' === $index_fields ) {
1084
+ $fields_select_all = 'selected';
1085
+ $fields_select_some = '';
1086
+ $index_fields = '';
1087
+ }
1088
+ if ( 'visible' === $index_fields ) {
1089
+ $fields_select_visible = 'selected';
1090
+ $fields_select_some = '';
1091
+ $index_fields = '';
1092
+ }
1093
 
1094
+ if ( isset( $synonyms ) ) {
1095
+ $synonyms = str_replace( ';', "\n", $synonyms );
1096
+ } else {
1097
+ $synonyms = '';
1098
+ }
1099
 
1100
+ if ( ! isset( $punctuation['quotes'] ) ) {
1101
+ $punctuation['quotes'] = 'replace';
1102
+ }
1103
+ if ( ! isset( $punctuation['decimals'] ) ) {
1104
+ $punctuation['decimals'] = 'remove';
1105
+ }
1106
+ if ( ! isset( $punctuation['ampersands'] ) ) {
1107
+ $punctuation['ampersands'] = 'replace';
1108
+ }
1109
+ if ( ! isset( $punctuation['hyphens'] ) ) {
1110
+ $punctuation['hyphens'] = 'replace';
1111
+ }
1112
+ $punct_quotes_replace = relevanssi_select( $punctuation['quotes'], 'replace' );
1113
+ $punct_quotes_remove = relevanssi_select( $punctuation['quotes'], 'remove' );
1114
+ $punct_decimals_replace = relevanssi_select( $punctuation['decimals'], 'replace' );
1115
+ $punct_decimals_remove = relevanssi_select( $punctuation['decimals'], 'remove' );
1116
+ $punct_decimals_keep = relevanssi_select( $punctuation['decimals'], 'keep' );
1117
+ $punct_ampersands_replace = relevanssi_select( $punctuation['ampersands'], 'replace' );
1118
+ $punct_ampersands_remove = relevanssi_select( $punctuation['ampersands'], 'remove' );
1119
+ $punct_ampersands_keep = relevanssi_select( $punctuation['ampersands'], 'keep' );
1120
+ $punct_hyphens_replace = relevanssi_select( $punctuation['hyphens'], 'replace' );
1121
+ $punct_hyphens_remove = relevanssi_select( $punctuation['hyphens'], 'remove' );
1122
+ $punct_hyphens_keep = relevanssi_select( $punctuation['hyphens'], 'keep' );
1123
+
1124
+ if ( RELEVANSSI_PREMIUM ) {
1125
+ $api_key = get_option( 'relevanssi_api_key' );
1126
+ $link_boost = get_option( 'relevanssi_link_boost' );
1127
+ $internal_links = get_option( 'relevanssi_internal_links' );
1128
+ $highlight_docs_ext = get_option( 'relevanssi_highlight_docs_external' );
1129
+ $thousand_separator = get_option( 'relevanssi_thousand_separator' );
1130
+ $disable_shortcodes = get_option( 'relevanssi_disable_shortcodes' );
1131
+ $index_users = get_option( 'relevanssi_index_users' );
1132
+ $index_user_fields = get_option( 'relevanssi_index_user_fields' );
1133
+ $index_subscribers = get_option( 'relevanssi_index_subscribers' );
1134
+ $index_synonyms = get_option( 'relevanssi_index_synonyms' );
1135
+ $index_taxonomies = get_option( 'relevanssi_index_taxonomies' );
1136
+ $index_terms = get_option( 'relevanssi_index_terms' );
1137
+ $hide_post_controls = get_option( 'relevanssi_hide_post_controls' );
1138
+ $show_post_controls = get_option( 'relevanssi_show_post_controls' );
1139
+ $recency_bonus_array = get_option( 'relevanssi_recency_bonus' );
1140
+ $searchblogs = get_option( 'relevanssi_searchblogs' );
1141
+ $searchblogs_all = get_option( 'relevanssi_searchblogs_all' );
1142
+ $mysql_columns = get_option( 'relevanssi_mysql_columns' );
1143
+ $index_pdf_parent = get_option( 'relevanssi_index_pdf_parent' );
1144
+ $server_location = get_option( 'relevanssi_server_location' );
1145
+
1146
+ if ( empty( $index_terms ) ) {
1147
+ $index_terms = array();
1148
+ }
1149
 
1150
+ $serialize_options['relevanssi_link_boost'] = $link_boost;
1151
+ $serialize_options['relevanssi_api_key'] = $api_key;
1152
+ $serialize_options['relevanssi_internal_links'] = $internal_links;
1153
+ $serialize_options['relevanssi_highlight_docs_external'] = $highlight_docs_ext;
1154
+ $serialize_options['relevanssi_thousand_separator'] = $thousand_separator;
1155
+ $serialize_options['relevanssi_disable_shortcodes'] = $disable_shortcodes;
1156
+ $serialize_options['relevanssi_index_users'] = $index_users;
1157
+ $serialize_options['relevanssi_index_user_fields'] = $index_user_fields;
1158
+ $serialize_options['relevanssi_index_subscribers'] = $index_subscribers;
1159
+ $serialize_options['relevanssi_index_synonyms'] = $index_synonyms;
1160
+ $serialize_options['relevanssi_index_taxonomies'] = $index_taxonomies;
1161
+ $serialize_options['relevanssi_index_terms'] = $index_terms;
1162
+ $serialize_options['relevanssi_hide_post_controls'] = $hide_post_controls;
1163
+ $serialize_options['relevanssi_show_post_controls'] = $show_post_controls;
1164
+ $serialize_options['recency_bonus'] = $recency_bonus_array;
1165
+ $serialize_options['relevanssi_searchblogs'] = $searchblogs;
1166
+ $serialize_options['relevanssi_searchblogs_all'] = $searchblogs_all;
1167
+ $serialize_options['relevanssi_mysql_columns'] = $mysql_columns;
1168
+ $serialize_options['relevanssi_index_pdf_parent'] = $index_pdf_parent;
1169
+ $serialize_options['relevanssi_server_location'] = get_option( 'relevanssi_server_location' );
1170
+ $serialize_options['relevanssi_send_pdf_files'] = get_option( 'relevanssi_send_pdf_files' );
1171
+ $serialize_options['relevanssi_read_new_files'] = get_option( 'relevanssi_read_new_files' );
1172
+ $serialize_options['relevanssi_link_pdf_files'] = get_option( 'relevanssi_link_pdf_files' );
1173
+
1174
+ $highlight_docs_ext = relevanssi_check( $highlight_docs_ext );
1175
+ $index_users = relevanssi_check( $index_users );
1176
+ $index_subscribers = relevanssi_check( $index_subscribers );
1177
+ $index_synonyms = relevanssi_check( $index_synonyms );
1178
+ $index_taxonomies = relevanssi_check( $index_taxonomies );
1179
+ $hide_post_controls = relevanssi_check( $hide_post_controls );
1180
+ $show_post_controls = relevanssi_check( $show_post_controls );
1181
+ $searchblogs_all = relevanssi_check( $searchblogs_all );
1182
+ $index_pdf_parent = relevanssi_check( $index_pdf_parent );
1183
+
1184
+ $internal_links_strip = relevanssi_select( $internal_links, 'strip' );
1185
+ $internal_links_nostrip = relevanssi_select( $internal_links, 'nostrip' );
1186
+ $internal_links_noindex = relevanssi_select( $internal_links, 'noindex' );
1187
+
1188
+ $recency_bonus = $recency_bonus_array['bonus'];
1189
  $recency_bonus_days = $recency_bonus_array['days'];
1190
 
1191
+ $serialized_options = wp_json_encode( $serialize_options );
 
 
 
 
 
 
 
 
 
 
1192
  }
1193
 
1194
  echo "<div class='postbox-container'>";
 
 
1195
  echo "<form method='post'>";
1196
+
1197
+ wp_nonce_field( plugin_basename( $relevanssi_variables['file'] ), 'relevanssi_options' );
1198
 
1199
  $display_save_button = true;
1200
 
1201
+ $active_tab = 'overview';
1202
+ if ( isset( $_REQUEST['tab'] ) ) { // WPCS: CSRF ok.
1203
+ $active_tab = $_REQUEST['tab']; // WPCS: CSRF ok. The value is printed once, but there it is escaped.
1204
+ }
1205
 
1206
+ if ( 'stopwords' === $active_tab ) {
1207
+ $display_save_button = false;
1208
+ }
1209
 
1210
+ printf( "<input type='hidden' name='tab' value='%s' />", esc_attr( $active_tab ) );
1211
 
1212
+ $this_page = '?page=' . plugin_basename( $relevanssi_variables['file'] );
1213
  ?>
1214
 
1215
  <h2 class="nav-tab-wrapper">
1216
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=overview" class="nav-tab <?php echo 'overview' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Overview', 'relevanssi' ); ?></a>
1217
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=indexing" class="nav-tab <?php echo 'indexing' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Indexing', 'relevanssi' ); ?></a>
1218
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=attachments" class="nav-tab <?php echo 'attachments' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Attachments', 'relevanssi' ); ?></a>
1219
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=searching" class="nav-tab <?php echo 'searching' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Searching', 'relevanssi' ); ?></a>
1220
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=logging" class="nav-tab <?php echo 'logging' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Logging', 'relevanssi' ); ?></a>
1221
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=excerpts" class="nav-tab <?php echo 'excerpts' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Excerpts and highlights', 'relevanssi' ); ?></a>
1222
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=synonyms" class="nav-tab <?php echo 'synonyms' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Synonyms', 'relevanssi' ); ?></a>
1223
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=stopwords" class="nav-tab <?php echo 'stopwords' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Stopwords', 'relevanssi' ); ?></a>
1224
+ <?php if ( function_exists( 'relevanssi_form_importexport' ) ) : ?>
1225
+ <a href="<?php echo esc_attr( $this_page ); ?>&amp;tab=importexport" class="nav-tab <?php echo 'importexport' === $active_tab ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Import / Export options', 'relevanssi' ); ?></a>
1226
  <?php endif; ?>
1227
  </h2>
1228
 
 
 
 
 
 
 
 
 
 
 
1229
  <?php
1230
+ if ( 'overview' === $active_tab ) :
1231
+ if ( ! RELEVANSSI_PREMIUM ) {
1232
+ $display_save_button = false;
1233
+ }
 
 
1234
  ?>
 
 
 
 
 
 
 
1235
 
1236
+ <h2><?php esc_html_e( 'Welcome to Relevanssi!', 'relevanssi' ); ?></h2>
1237
 
1238
  <table class="form-table">
1239
  <?php
1240
+ if ( ! is_multisite() && function_exists( 'relevanssi_form_api_key' ) ) {
1241
+ relevanssi_form_api_key( $api_key );
1242
+ }
1243
+ if ( function_exists( 'relevanssi_form_hide_post_controls' ) ) {
1244
+ relevanssi_form_hide_post_controls( $hide_post_controls, $show_post_controls );
1245
+ }
1246
  ?>
1247
  <tr>
1248
+ <th scope="row"><?php esc_html_e( 'Getting started', 'relevanssi' ); ?></th>
1249
  <td>
1250
+ <p><?php esc_html_e( "You've already installed Relevanssi. That's a great first step towards good search experience!", 'relevanssi' ); ?></p>
1251
  <ol>
1252
+ <?php if ( 'done' !== get_option( 'relevanssi_indexed' ) ) : ?>
1253
+ <?php // Translators: %1$s opens the link, %2$s is the anchor text, %3$s closes the link. ?>
1254
+ <li><p><?php printf( esc_html__( 'Now, you need an index. Head over to the %1$s%2$s%3$s tab to set up the basic indexing options and to build the index.', 'relevanssi' ), "<a href='" . esc_attr( $this_page ) . "&amp;tab=indexing'>", esc_html__( 'Indexing', 'relevanssi' ), '</a>' ); ?></p>
1255
+ <p><?php esc_html_e( 'You need to check at least the following options:', 'relevanssi' ); ?><br />
1256
+ – <?php esc_html_e( 'Make sure the post types you want to include in the index are indexed.', 'relevanssi' ); ?><br />
1257
+ <?php // Translators: %s is '_sku'. ?>
1258
+ – <?php printf( esc_html__( 'Do you use custom fields to store content you want included? If so, add those too. WooCommerce user? You probably want to include %s.', 'relevanssi' ), '<code>_sku</code>' ); ?></p>
1259
+ <p><?php esc_html_e( "Then just save the options and build the index. First time you have to do it manually, but after that, it's fully automatic: all changes are reflected in the index without reindexing. (That said, it's a good idea to rebuild the index once a year.)", 'relevanssi' ); ?></p>
1260
  </li>
1261
  <?php else : ?>
1262
+ <li><p><?php esc_html_e( 'Great, you already have an index!', 'relevanssi' ); ?></p></li>
1263
  <?php endif; ?>
1264
  <li>
1265
+ <?php // Translators: %1$s opens the link, %2$s is the anchor text, %3$s closes the link. ?>
1266
+ <p><?php printf( esc_html__( 'On the %1$s%2$s%3$s tab, choose whether you want the default operator to be AND (less results, but more precise) or OR (more results, less precise).', 'relevanssi' ), "<a href='" . esc_attr( $this_page ) . "&amp;tab=searching'>", esc_html__( 'Searching', 'relevanssi' ), '</a>' ); ?></p>
1267
  </li>
1268
  <li>
1269
+ <?php // Translators: %1$s opens the link, %2$s is the anchor text, %3$s closes the link. ?>
1270
+ <p><?php printf( esc_html__( 'The next step is the %1$s%2$s%3$s tab, where you can enable the custom excerpts that show the relevant part of post in the search results pages.', 'relevanssi' ), "<a href='" . esc_attr( $this_page ) . "&amp;tab=excerpts'>", esc_html__( 'Excerpts and highlights', 'relevanssi' ), '</a>' ); ?></p>
1271
+ <p><?php esc_html_e( 'There are couple of options related to that, so if you want highlighting in the results, you can adjust the styles for that to suit the look of your site.', 'relevanssi' ); ?></p>
1272
  </li>
1273
  <li>
1274
+ <p><?php esc_html_e( "That's about it! Now you should have Relevanssi up and running. The rest of the options is mostly fine-tuning.", 'relevanssi' ); ?></p>
1275
  </li>
1276
  </ol>
1277
+ <p><?php esc_html_e( "Relevanssi doesn't have a separate search widget. Instead, Relevanssi uses the default search widget. Any standard search form will do!", 'relevanssi' ); ?></p>
1278
  </td>
1279
  </tr>
1280
  <tr>
1281
+ <th scope="row"><?php esc_html_e( 'For more information', 'relevanssi' ); ?></th>
1282
  <td>
1283
+ <p><?php esc_html_e( "Relevanssi uses the WordPress contextual help. Click 'Help' on the top right corner for more information on many Relevanssi topics.", 'relevanssi' ); ?></p>
1284
+ <?php // Translators: %1$s opens the link, %2$s closes the link. ?>
1285
+ <p><?php printf( esc_html__( '%1$sRelevanssi knowledge base%2$s has lots of information about advanced Relevanssi use, including plenty of code samples.', 'relevanssi' ), "<a href='https://www.relevanssi.com/knowledge-base/'>", '</a>' ); ?></p>
1286
  </td>
1287
  </tr>
1288
  <tr>
1289
  <th scope="row">
1290
+ <?php esc_html_e( 'Relevanssi on Facebook', 'relevanssi' ); ?>
1291
  </th>
1292
  <td>
1293
+ <p><a href="https://www.facebook.com/relevanssi"><?php esc_html_e( 'Check out the Relevanssi page on Facebook for news and updates about Relevanssi.', 'relevanssi' ); ?></a></p>
1294
  </td>
1295
  </tr>
1296
+ <?php if ( ! RELEVANSSI_PREMIUM ) { ?>
1297
  <tr>
1298
  <th scope="row">
1299
+ <?php esc_html_e( 'Buy Relevanssi Premium', 'relevanssi' ); ?>
1300
  </th>
1301
  <td>
1302
+ <p><a href="https://www.relevanssi.com/buy-premium"><?php esc_html_e( 'Buy Relevanssi Premium now', 'relevanssi' ); ?></a> –
1303
+ <?php // Translators: %1$s is the coupon code, %2$s is the year it expires. ?>
1304
+ <?php printf( esc_html__( 'use coupon code %1$s for 20%% discount (valid at least until the end of %2$s)', 'relevanssi' ), '<strong>FREE2018</strong>', '2018' ); ?></p>
1305
+ <p><?php esc_html_e( 'Here are some improvements Relevanssi Premium offers:', 'relevanssi' ); ?></p>
1306
  <ul class="relevanssi_ul">
1307
+ <li><?php esc_html_e( 'PDF content indexing', 'relevanssi' ); ?></li>
1308
+ <li><?php esc_html_e( 'Index and search user profile pages', 'relevanssi' ); ?></li>
1309
+ <li><?php esc_html_e( 'Index and search taxonomy term pages', 'relevanssi' ); ?></li>
1310
+ <li><?php esc_html_e( 'Multisite searches across many subsites', 'relevanssi' ); ?></li>
1311
+ <li><?php esc_html_e( 'WP CLI commands', 'relevanssi' ); ?></li>
1312
+ <li><?php esc_html_e( 'Adjust weights separately for each post type and taxonomy', 'relevanssi' ); ?></li>
1313
+ <li><?php esc_html_e( 'Internal link anchors can be search terms for the target posts', 'relevanssi' ); ?></li>
1314
+ <li><?php esc_html_e( 'Index and search any columns in the wp_posts database', 'relevanssi' ); ?></li>
1315
+ <li><?php esc_html_e( 'Hide Relevanssi branding from the User Searches page on a client installation', 'relevanssi' ); ?></li>
1316
  </ul>
1317
  </td>
1318
  </tr>
1319
+ <?php } // End if ( ! RELEVANSSI_PREMIUM ). ?>
1320
  </table>
1321
 
1322
+ <?php endif; // Active tab: basic. ?>
1323
 
1324
+ <?php if ( 'logging' === $active_tab ) : ?>
1325
 
1326
  <table class="form-table">
1327
  <tr>
1328
  <th scope="row">
1329
+ <label for='relevanssi_log_queries'><?php esc_html_e( 'Enable logs', 'relevanssi' ); ?></label>
1330
  </th>
1331
  <td>
1332
  <fieldset>
1333
+ <legend class="screen-reader-text"><?php esc_html_e( 'Keep a log of user queries.', 'relevanssi' ); ?></legend>
1334
  <label for='relevanssi_log_queries'>
1335
+ <input type='checkbox' name='relevanssi_log_queries' id='relevanssi_log_queries' <?php echo esc_html( $log_queries ); ?> />
1336
+ <?php esc_html_e( 'Keep a log of user queries.', 'relevanssi' ); ?>
1337
  </label>
1338
  </fieldset>
1339
+ <p class="description">
1340
+ <?php
1341
+ global $wpdb;
1342
+ // Translators: %1$s is the name of the "User searches" page, %2$s is the name of the database table.
1343
+ printf( esc_html__( "If enabled, Relevanssi will log user queries. The logs can be examined under '%1\$s' on the Dashboard admin menu and are stored in the %2\$s database table.", 'relevanssi' ),
1344
+ esc_html__( 'User searches', 'relevanssi' ), esc_html( $wpdb->prefix . 'relevanssi_log' ) );
1345
+ ?>
1346
+ </p>
1347
  </td>
1348
  </tr>
1349
  <tr>
1350
  <th scope="row">
1351
+ <label for='relevanssi_log_queries_with_ip'><?php esc_html_e( 'Log user IP', 'relevanssi' ); ?></label>
1352
  </th>
1353
  <td>
1354
  <fieldset>
1355
+ <legend class="screen-reader-text"><?php esc_html_e( "Log the user's IP with the queries.", 'relevanssi' ); ?></legend>
1356
  <label for='relevanssi_log_queries_with_ip'>
1357
+ <input type='checkbox' name='relevanssi_log_queries_with_ip' id='relevanssi_log_queries_with_ip' <?php echo esc_html( $log_queries_with_ip ); ?> />
1358
+ <?php esc_html_e( "Log the user's IP with the queries.", 'relevanssi' ); ?>
1359
  </label>
1360
  </fieldset>
1361
+ <p class="description"><?php esc_html_e( "If enabled, Relevanssi will log user's IP adress with the queries. Note that this may be illegal where you live, and in EU will create a person registry that falls under the GDPR.", 'relevanssi' ); ?></p>
1362
  </td>
1363
  </tr>
1364
  <tr>
1365
  <th scope="row">
1366
+ <label for='relevanssi_omit_from_logs'><?php esc_html_e( 'Exclude users', 'relevanssi' ); ?></label>
1367
  </th>
1368
  <td>
1369
+ <input type='text' name='relevanssi_omit_from_logs' id='relevanssi_omit_from_logs' size='60' value='<?php echo esc_attr( $omit_from_logs ); ?>' />
1370
+ <p class="description"><?php esc_html_e( 'Comma-separated list of numeric user IDs or user login names that will not be logged.', 'relevanssi' ); ?></p>
1371
  </td>
1372
  </tr>
1373
+ <?php
1374
+ if ( function_exists( 'relevanssi_form_hide_branding' ) ) {
1375
+ relevanssi_form_hide_branding( $hide_branding );
1376
+ }
1377
+ ?>
1378
  <tr>
1379
  <th scope="row">
1380
+ <label for='relevanssi_trim_logs'><?php esc_html_e( 'Trim logs', 'relevanssi' ); ?></label>
1381
  </th>
1382
  <td>
1383
+ <input type='number' name='relevanssi_trim_logs' id='relevanssi_trim_logs' value='<?php echo esc_attr( $trim_logs ); ?>' />
1384
+ <?php esc_html_e( 'How many days of logs to keep in the database.', 'relevanssi' ); ?>
1385
+ <?php // Translators: %d is the setting for no trim (probably 0). ?>
1386
+ <p class="description"><?php printf( esc_html__( ' Set to %d for no trimming.', 'relevanssi' ), 0 ); ?></p>
1387
  </td>
1388
  </tr>
1389
 
1390
  </table>
1391
 
1392
+ <?php endif; // Active tag: logging. ?>
1393
 
1394
+ <?php
1395
+ if ( 'searching' === $active_tab ) :
1396
+ $docs_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT doc) FROM ' . $relevanssi_variables['relevanssi_table'] . ' WHERE doc != -1' ); // WPCS: unprepared SQL ok, Relevanssi table name.
1397
  ?>
1398
 
1399
  <table class="form-table">
1400
  <tr>
1401
  <th scope="row">
1402
+ <label for='relevanssi_implicit_operator'><?php esc_html_e( 'Default operator', 'relevanssi' ); ?></label>
1403
  </th>
1404
  <td>
1405
  <select name='relevanssi_implicit_operator' id='relevanssi_implicit_operator'>
1406
+ <option value='AND' <?php echo esc_html( $implicit_and ); ?>><?php esc_html_e( 'AND - require all terms', 'relevanssi' ); ?></option>
1407
+ <option value='OR' <?php echo esc_html( $implicit_or ); ?>><?php esc_html_e( 'OR - any term present is enough', 'relevanssi' ); ?></option>
1408
  </select>
1409
+ <p class="description"><?php esc_html_e( 'This setting determines the default operator for the search.', 'relevanssi' ); ?></p>
1410
+ <?php
1411
+ if ( RELEVANSSI_PREMIUM ) {
1412
+ // Translators: %1$s is the name of the 'operator' query variable, %2$s is an example url.
1413
+ echo "<p class='description'>" . sprintf( esc_html__( 'You can override this setting with the %1$s query parameter, like this: %2$s', 'relevanssi' ), '<code>operator</code>', 'http://www.example.com/?s=term&amp;operator=or' ) . '</p>';
1414
+ }
1415
+ ?>
1416
  </td>
1417
  </tr>
1418
+ <tr id="orfallback" class='<?php echo esc_attr( $orfallback_visibility ); ?>'>
1419
  <th scope="row">
1420
+ <label for='relevanssi_disable_or_fallback'><?php esc_html_e( 'Fallback to OR', 'relevanssi' ); ?></label>
1421
  </th>
1422
  <td>
1423
  <fieldset>
1424
+ <legend class="screen-reader-text"><?php esc_html_e( 'Disable the OR fallback.', 'relevanssi' ); ?></legend>
1425
  <label for='relevanssi_disable_or_fallback'>
1426
+ <input type='checkbox' name='relevanssi_disable_or_fallback' id='relevanssi_disable_or_fallback' <?php echo esc_html( $disable_or_fallback ); ?> />
1427
+ <?php esc_html_e( 'Disable the OR fallback.', 'relevanssi' ); ?>
1428
  </label>
1429
  </fieldset>
1430
+ <p class="description"><?php esc_html_e( 'By default, if AND search fails to find any results, Relevanssi will switch the operator to OR and run the search again. You can prevent that by checking this option.', 'relevanssi' ); ?></p>
1431
  </td>
1432
  </tr>
1433
  <tr>
1434
  <th scope="row">
1435
+ <label for='relevanssi_default_orderby'><?php esc_html_e( 'Default order', 'relevanssi' ); ?></label>
1436
  </th>
1437
  <td>
1438
  <select name='relevanssi_default_orderby' id='relevanssi_default_orderby'>
1439
+ <option value='relevance' <?php echo esc_html( $orderby_relevance ); ?>><?php esc_html_e( 'Relevance (highly recommended)', 'relevanssi' ); ?></option>
1440
+ <option value='post_date' <?php echo esc_html( $orderby_date ); ?>><?php esc_html_e( 'Post date', 'relevanssi' ); ?></option>
1441
  </select>
1442
+ <?php // Translators: name of the query variable. ?>
1443
+ <p class="description"><?php printf( esc_html__( 'If you want to override this or use multi-layered ordering (eg. first order by relevance, but sort ties by post title), you can use the %s query variable. See Help for more information.', 'relevanssi' ), '<code>orderby</code>' ); ?></p>
1444
+ <?php if ( RELEVANSSI_PREMIUM ) { ?>
1445
+ <p class="description"><?php esc_html_e( ' If you want date-based results, see the recent post bonus in the Weights section.', 'relevanssi' ); ?></p>
1446
+ <?php } // End if ( RELEVANSSI_PREMIUM ). ?>
1447
  </td>
1448
  </tr>
1449
  <tr>
1450
  <th scope="row">
1451
+ <label for='relevanssi_fuzzy'><?php esc_html_e( 'Keyword matching', 'relevanssi' ); ?></label>
1452
  </th>
1453
  <td>
1454
  <select name='relevanssi_fuzzy' id='relevanssi_fuzzy'>
1455
+ <option value='never' <?php echo esc_html( $fuzzy_never ); ?>><?php esc_html_e( 'Whole words', 'relevanssi' ); ?></option>
1456
+ <option value='always' <?php echo esc_html( $fuzzy_always ); ?>><?php esc_html_e( 'Partial words', 'relevanssi' ); ?></option>
1457
+ <option value='sometimes' <?php echo esc_html( $fuzzy_sometimes ); ?>><?php esc_html_e( 'Partial words if no hits for whole words', 'relevanssi' ); ?></option>
1458
  </select>
1459
+ <p class="description"><?php esc_html_e( 'Whole words means Relevanssi only finds posts that include the whole search term.', 'relevanssi' ); ?></p>
1460
+ <p class="description"><?php esc_html_e( "Partial words also includes cases where the word in the index begins or ends with the search term (searching for 'ana' will match 'anaconda' or 'banana', but not 'banal'). See Help, if you want to make Relevanssi match also inside words.", 'relevanssi' ); ?></p>
1461
  </td>
1462
  </tr>
1463
  <tr>
1464
  <th scope="row">
1465
+ <?php esc_html_e( 'Weights', 'relevanssi' ); ?>
1466
  </th>
1467
  <td>
1468
+ <p class="description"><?php esc_html_e( 'All the weights in the table are multipliers. To increase the weight of an element, use a higher number. To make an element less significant, use a number lower than 1.', 'relevanssi' ); ?></p>
1469
  <table class="relevanssi-weights-table">
1470
  <thead>
1471
  <tr>
1472
+ <th><?php esc_html_e( 'Element', 'relevanssi' ); ?></th>
1473
+ <th class="col-2"><?php esc_html_e( 'Weight', 'relevanssi' ); ?></th>
1474
  </tr>
1475
  </thead>
1476
  <tr>
1477
  <td>
1478
+ <?php esc_html_e( 'Content', 'relevanssi' ); ?>
1479
  </td>
1480
  <td class="col-2">
1481
+ <input type='text' name='relevanssi_content_boost' id='relevanssi_content_boost' size='4' value='<?php echo esc_attr( $content_boost ); ?>' />
1482
  </td>
1483
  </tr>
1484
  <tr>
1485
  <td>
1486
+ <?php esc_html_e( 'Titles', 'relevanssi' ); ?>
1487
  </td>
1488
  <td class="col-2">
1489
+ <input type='text' name='relevanssi_title_boost' id='relevanssi_title_boost' size='4' value='<?php echo esc_attr( $title_boost ); ?>' />
1490
  </td>
1491
  </tr>
1492
+ <?php
1493
+ if ( function_exists( 'relevanssi_form_link_weight' ) ) {
1494
+ relevanssi_form_link_weight( $link_boost );
1495
+ }
1496
+ ?>
1497
  <tr>
1498
  <td>
1499
+ <?php esc_html_e( 'Comment text', 'relevanssi' ); ?>
1500
  </td>
1501
  <td class="col-2">
1502
+ <input type='text' name='relevanssi_comment_boost' id='relevanssi_comment_boost' size='4' value='<?php echo esc_attr( $comment_boost ); ?>' />
1503
  </td>
1504
  </tr>
1505
  <?php
1506
+ if ( function_exists( 'relevanssi_form_post_type_weights' ) ) {
1507
+ relevanssi_form_post_type_weights( $post_type_weights );
1508
+ }
1509
+ if ( function_exists( 'relevanssi_form_taxonomy_weights' ) ) {
1510
+ relevanssi_form_taxonomy_weights( $post_type_weights );
1511
+ }
1512
+ if ( function_exists( 'relevanssi_form_tag_weight' ) ) {
1513
+ relevanssi_form_tag_weight( $post_type_weights );
1514
+ }
1515
+ if ( function_exists( 'relevanssi_form_recency_weight' ) ) {
1516
+ relevanssi_form_recency_weight( $recency_bonus );
1517
+ }
1518
  ?>
1519
  </table>
1520
  </td>
1521
  </tr>
1522
+ <?php
1523
+ if ( function_exists( 'relevanssi_form_recency_cutoff' ) ) {
1524
+ relevanssi_form_recency_cutoff( $recency_bonus_days );
1525
+ }
1526
+ ?>
1527
  <tr>
1528
  <th scope="row">
1529
+ <label for='relevanssi_exact_match_bonus'><?php esc_html_e( 'Boost exact matches', 'relevanssi' ); ?></label>
1530
  </th>
1531
  <td>
1532
  <fieldset>
1533
+ <legend class="screen-reader-text"><?php esc_html_e( 'Give boost to exact matches.', 'relevanssi' ); ?></legend>
1534
+ <label for='relevanssi_exact_match_bonus'>
1535
+ <input type='checkbox' name='relevanssi_exact_match_bonus' id='relevanssi_exact_match_bonus' <?php echo esc_html( $exact_match_bonus ); ?> />
1536
+ <?php esc_html_e( 'Give boost to exact matches.', 'relevanssi' ); ?>
1537
+ </label>
1538
+ </fieldset>
1539
+ <?php // Translators: %s is the name of the filter hook. ?>
1540
+ <p class="description"><?php printf( esc_html__( 'If you enable this option, matches where the search query appears in title or content as a phrase will get a weight boost. To adjust the boost, you can use the %s filter hook. See Help for more details.', 'relevanssi' ), '<code>relevanssi_exact_match_bonus</code>' ); ?></p>
1541
+ </td>
1542
+ </tr>
1543
+ <?php
1544
+ if ( function_exists( 'icl_object_id' ) && ! function_exists( 'pll_get_post' ) ) {
1545
+ ?>
1546
+ <tr>
1547
+ <th scope="row">
1548
+ <label for='relevanssi_wpml_only_current'><?php esc_html_e( 'WPML', 'relevanssi' ); ?></label>
1549
+ </th>
1550
+ <td>
1551
+ <fieldset>
1552
+ <legend class="screen-reader-text"><?php esc_html_e( 'Limit results to current language.', 'relevanssi' ); ?></legend>
1553
  <label for='relevanssi_wpml_only_current'>
1554
+ <input type='checkbox' name='relevanssi_wpml_only_current' id='relevanssi_wpml_only_current' <?php echo esc_html( $wpml_only_current ); ?> />
1555
+ <?php esc_html_e( 'Limit results to current language.', 'relevanssi' ); ?>
1556
  </label>
1557
  </fieldset>
1558
+ <p class="description"><?php esc_html_e( 'Enabling this option will restrict the results to the currently active language. If the option is disabled, results will include posts in all languages.', 'relevanssi' ); ?></p>
1559
  </td>
1560
  </tr>
1561
+ <?php } // WPML. ?>
1562
+ <?php if ( function_exists( 'pll_get_post' ) ) { ?>
1563
  <tr>
1564
  <th scope="row">
1565
+ <label for='relevanssi_polylang_all_languages'><?php esc_html_e( 'Polylang', 'relevanssi' ); ?></label>
1566
  </th>
1567
  <td>
1568
  <fieldset>
1569
+ <legend class="screen-reader-text"><?php esc_html_e( 'Allow results from all languages.', 'relevanssi' ); ?></legend>
1570
  <label for='relevanssi_polylang_all_languages'>
1571
+ <input type='checkbox' name='relevanssi_polylang_all_languages' id='relevanssi_polylang_all_languages' <?php echo esc_html( $polylang_allow_all ); ?> />
1572
+ <?php esc_html_e( 'Allow results from all languages.', 'relevanssi' ); ?>
1573
  </label>
1574
  </fieldset>
1575
+ <p class="description"><?php esc_html_e( 'By default Polylang restricts the search to the current language. Enabling this option will lift this restriction.', 'relevanssi' ); ?></p>
1576
  </td>
1577
  </tr>
1578
+ <?php } // Polylang. ?>
1579
  <tr>
1580
  <th scope="row">
1581
+ <label for='relevanssi_admin_search'><?php esc_html_e( 'Admin search', 'relevanssi' ); ?></label>
1582
  </th>
1583
  <td>
1584
  <fieldset>
1585
+ <legend class="screen-reader-text"><?php esc_html_e( 'Use Relevanssi for admin searches.', 'relevanssi' ); ?></legend>
1586
  <label for='relevanssi_admin_search'>
1587
+ <input type='checkbox' name='relevanssi_admin_search' id='relevanssi_admin_search' <?php echo esc_html( $admin_search ); ?> />
1588
+ <?php esc_html_e( 'Use Relevanssi for admin searches.', 'relevanssi' ); ?>
1589
  </label>
1590
  </fieldset>
1591
+ <p class="description"><?php esc_html_e( "If checked, Relevanssi will be used for searches in the admin interface. The page search doesn't use Relevanssi, because WordPress works like that.", 'relevanssi' ); ?></p>
1592
  </td>
1593
  </tr>
1594
  <tr>
1595
  <th scope="row">
1596
+ <?php // Translators: %s is 'exclude_from_search'. ?>
1597
+ <label for='relevanssi_respect_exclude'><?php printf( esc_html__( 'Respect %s', 'relevanssi' ), 'exclude_from_search' ); ?></label>
1598
  </th>
1599
  <td>
1600
  <fieldset>
1601
+ <legend class="screen-reader-text"><?php esc_html_e( 'Respect exclude_from_search for custom post types', 'relevanssi' ); ?></legend>
1602
  <label for='relevanssi_respect_exclude'>
1603
+ <input type='checkbox' name='relevanssi_respect_exclude' id='relevanssi_respect_exclude' <?php echo esc_html( $respect_exclude ); ?> />
1604
+ <?php // Translators: %s is 'exclude_from_search'. ?>
1605
+ <?php printf( esc_html__( 'Respect %s for custom post types', 'relevanssi' ), '<code>exclude_from_search</code>' ); ?>
1606
  </label>
1607
+ <p class="description"><?php esc_html_e( "If checked, Relevanssi won't display posts of custom post types that have 'exclude_from_search' set to true.", 'relevanssi' ); ?></p>
1608
  <?php
1609
+ if ( ! empty( $respect_exclude ) ) {
1610
+ $pt_1 = get_post_types( array( 'exclude_from_search' => '1' ) );
1611
+ $pt_2 = get_post_types( array( 'exclude_from_search' => true ) );
1612
+
1613
+ $private_types = array_merge( $pt_1, $pt_2 );
1614
+ $problem_post_types = array_intersect( $index_post_types, $private_types );
1615
+ if ( ! empty( $problem_post_types ) ) {
1616
+ ?>
1617
+ <p class="description important">
1618
+ <?php
1619
+ esc_html_e( "You probably should uncheck this option, because you've set Relevanssi to index the following non-public post types:", 'relevanssi' );
1620
+ echo ' ' . esc_html( implode( ', ', $problem_post_types ) );
1621
+ ?>
1622
+ </p>
1623
+ <?php
1624
  }
1625
+ }
1626
  ?>
1627
  </fieldset>
1628
  </td>
1629
  </tr>
1630
  <tr>
1631
  <th scope="row">
1632
+ <label for='relevanssi_throttle'><?php esc_html_e( 'Throttle searches', 'relevanssi' ); ?></label>
1633
  </th>
1634
  <td id="throttlesearches">
1635
+ <div id="throttle_disabled"
1636
+ <?php
1637
+ if ( ! $orderby_date ) {
1638
+ echo "class='screen-reader-text'";
1639
+ }
1640
+ ?>
1641
+ >
1642
+ <p class="description"><?php esc_html_e( 'Throttling the search does not work when sorting the posts by date.', 'relevanssi' ); ?></p>
1643
  </div>
1644
+ <div id="throttle_enabled"
1645
+ <?php
1646
+ if ( ! $orderby_relevance ) {
1647
+ echo "class='screen-reader-text'";
1648
+ }
1649
+ ?>
1650
+ >
1651
  <fieldset>
1652
+ <legend class="screen-reader-text"><?php esc_html_e( 'Throttle searches.', 'relevanssi' ); ?></legend>
1653
  <label for='relevanssi_throttle'>
1654
+ <input type='checkbox' name='relevanssi_throttle' id='relevanssi_throttle' <?php echo esc_html( $throttle ); ?> />
1655
+ <?php esc_html_e( 'Throttle searches.', 'relevanssi' ); ?>
1656
  </label>
1657
  </fieldset>
1658
+ <?php if ( $docs_count < 1000 ) { ?>
1659
+ <p class="description important"><?php esc_html_e( "Your database is so small that you don't need to enable this.", 'relevanssi' ); ?></p>
1660
+ <?php } ?>
1661
+ <p class="description"><?php esc_html_e( 'If this option is checked, Relevanssi will limit search results to at most 500 results per term. This will improve performance, but may cause some relevant documents to go unfound. See Help for more details.', 'relevanssi' ); ?></p>
1662
  </div>
1663
  </td>
1664
  </tr>
1665
  <tr>
1666
  <th scope="row">
1667
+ <label for='relevanssi_cat'><?php esc_html_e( 'Category restriction', 'relevanssi' ); ?></label>
1668
  </th>
1669
  <td>
1670
  <div class="categorydiv" style="max-width: 400px">
1671
  <div class="tabs-panel">
1672
  <ul id="categorychecklist">
1673
+ <?php
1674
+ $selected_cats = explode( ',', $cat );
1675
+ $walker = get_relevanssi_taxonomy_walker();
1676
+ $walker->name = 'relevanssi_cat';
1677
+ wp_terms_checklist( 0, array(
1678
+ 'taxonomy' => 'category',
1679
+ 'selected_cats' => $selected_cats,
1680
+ 'walker' => $walker,
1681
+ ));
1682
  ?>
1683
  </ul>
1684
  <input type="hidden" name="relevanssi_cat_active" value="1" />
1685
  </div>
1686
  </div>
1687
+ <p class="description"><?php esc_html_e( 'You can restrict search results to a category for all searches. For restricting on a per-search basis and more options (eg. tag restrictions), see Help.', 'relevanssi' ); ?></p>
1688
  </td>
1689
  </tr>
1690
  <tr>
1691
  <th scope="row">
1692
+ <label for='relevanssi_excat'><?php esc_html_e( 'Category exclusion', 'relevanssi' ); ?></label>
1693
  </th>
1694
  <td>
1695
  <div class="categorydiv" style="max-width: 400px">
1696
  <div class="tabs-panel">
1697
  <ul id="categorychecklist">
1698
+ <?php
1699
+ $selected_cats = explode( ',', $excat );
1700
+ $walker = get_relevanssi_taxonomy_walker();
1701
+ $walker->name = 'relevanssi_excat';
1702
+ wp_terms_checklist( 0, array(
1703
+ 'taxonomy' => 'category',
1704
+ 'selected_cats' => $selected_cats,
1705
+ 'walker' => $walker,
1706
+ ));
1707
  ?>
1708
  </ul>
1709
  <input type="hidden" name="relevanssi_excat_active" value="1" />
1710
  </div>
1711
  </div>
1712
+ <p class="description"><?php esc_html_e( 'Posts in these categories are not included in search results. To exclude the posts completely from the index, see Help.', 'relevanssi' ); ?></p>
1713
  </td>
1714
  </tr>
1715
  <tr>
1716
+ <th scope="row">
1717
+ <label for='relevanssi_expst'><?php esc_html_e( 'Post exclusion', 'relevanssi' ); ?>
1718
  </th>
1719
  <td>
1720
+ <input type='text' name='relevanssi_expst' id='relevanssi_expst' size='60' value='<?php echo esc_attr( $exclude_posts ); ?>' />
1721
+ <p class="description"><?php esc_html_e( "Enter a comma-separated list of post or page ID's to exclude those pages from the search results.", 'relevanssi' ); ?></p>
1722
+ <?php if ( RELEVANSSI_PREMIUM ) { ?>
1723
+ <p class="description"><?php esc_html_e( "With Relevanssi Premium, it's better to use the check box on post edit pages. That will remove the posts completely from the index, and will work with multisite searches unlike this setting.", 'relevanssi' ); ?></p>
1724
+ <?php } ?>
1725
  </td>
1726
  </tr>
1727
+ <?php
1728
+ if ( function_exists( 'relevanssi_form_searchblogs_setting' ) ) {
1729
+ relevanssi_form_searchblogs_setting( $searchblogs_all, $searchblogs );
1730
+ }
1731
+ ?>
1732
  </table>
1733
 
1734
+ <?php endif; // Active tab: searching. ?>
1735
 
1736
+ <?php if ( 'excerpts' === $active_tab ) : ?>
1737
 
1738
+ <h2 id="excerpts"><?php esc_html_e( 'Custom excerpts/snippets', 'relevanssi' ); ?></h2>
1739
 
1740
  <table class="form-table">
1741
  <tr>
1742
  <th scope="row">
1743
+ <label for='relevanssi_excerpts'><?php esc_html_e( 'Custom search result snippets', 'relevanssi' ); ?></label>
1744
  </th>
1745
  <td>
1746
  <fieldset>
1747
+ <legend class="screen-reader-text"><?php esc_html_e( 'Create custom search result snippets', 'relevanssi' ); ?></legend>
1748
  <label >
1749
+ <input type='checkbox' name='relevanssi_excerpts' id='relevanssi_excerpts' <?php echo esc_html( $excerpts ); ?> />
1750
+ <?php esc_html_e( 'Create custom search result snippets', 'relevanssi' ); ?>
1751
  </label>
1752
  </fieldset>
1753
+ <p class="description"><?php esc_html_e( 'Only enable this if you actually use the custom excerpts.', 'relevanssi' ); ?></p>
1754
  </td>
1755
  </tr>
1756
+ <tr id="tr_excerpt_length"
1757
+ <?php
1758
+ if ( empty( $excerpts ) ) {
1759
+ echo "class='relevanssi_disabled'";
1760
+ }
1761
+ ?>
1762
+ >
1763
  <th scope="row">
1764
+ <label for='relevanssi_excerpt_length'><?php esc_html_e( 'Length of the snippet', 'relevanssi' ); ?></label>
1765
  </th>
1766
  <td>
1767
+ <input type='text' name='relevanssi_excerpt_length' id='relevanssi_excerpt_length' size='4' value='<?php echo esc_attr( $excerpt_length ); ?>'
1768
+ <?php
1769
+ if ( empty( $excerpts ) ) {
1770
+ echo "disabled='disabled'";
1771
+ }
1772
+ ?>
1773
+ />
1774
+ <select name='relevanssi_excerpt_type' id='relevanssi_excerpt_type'
1775
+ <?php
1776
+ if ( empty( $excerpts ) ) {
1777
+ echo "disabled='disabled'";
1778
+ }
1779
+ ?>
1780
+ >
1781
+ <option value='chars' <?php echo esc_html( $excerpt_chars ); ?>><?php esc_html_e( 'characters', 'relevanssi' ); ?></option>
1782
+ <option value='words' <?php echo esc_html( $excerpt_words ); ?>><?php esc_html_e( 'words', 'relevanssi' ); ?></option>
1783
  </select>
1784
+ <p class="description"><?php esc_html_e( "Using words is much faster than characters. Don't use characters, unless you have a really good reason and your posts are short.", 'relevanssi' ); ?></p>
1785
  </td>
1786
  </tr>
1787
+ <tr id="tr_excerpt_allowable_tags"
1788
+ <?php
1789
+ if ( empty( $excerpts ) ) {
1790
+ echo "class='relevanssi_disabled'";
1791
+ }
1792
+ ?>
1793
+ >
1794
  <th scope="row">
1795
+ <label for='relevanssi_excerpt_allowable_tags'><?php esc_html_e( 'Allowable tags in excerpts', 'relevanssi' ); ?></label>
1796
  </th>
1797
  <td>
1798
+ <input type='text' name='relevanssi_excerpt_allowable_tags' id='relevanssi_excerpt_allowable_tags' size='60' value='<?php echo esc_attr( $excerpt_allowable_tags ); ?>'
1799
+ <?php
1800
+ if ( empty( $excerpts ) ) {
1801
+ echo "disabled='disabled'";
1802
+ }
1803
+ ?>
1804
+ />
1805
+ <p class="description"><?php esc_html_e( 'List all tags you want to allow in excerpts. For example: &lt;p&gt;&lt;a&gt;&lt;strong&gt;.', 'relevanssi' ); ?></p>
1806
  </td>
1807
  </tr>
1808
+ <tr id="tr_excerpt_custom_fields"
1809
+ <?php
1810
+ if ( empty( $excerpts ) ) {
1811
+ echo "class='relevanssi_disabled'";
1812
+ }
1813
+ ?>
1814
+ >
1815
  <th scope="row">
1816
+ <label for='relevanssi_excerpt_custom_fields'><?php esc_html_e( 'Use custom fields for excerpts', 'relevanssi' ); ?></label>
1817
  </th>
1818
  <td>
1819
  <fieldset>
1820
+ <legend class="screen-reader-text"><?php esc_html_e( 'Use custom field content for building excerpts', 'relevanssi' ); ?></legend>
1821
  <label>
1822
+ <input type='checkbox' name='relevanssi_excerpt_custom_fields' id='relevanssi_excerpt_custom_fields' <?php echo esc_html( $excerpt_custom_fields ); ?>
1823
+ <?php
1824
+ if ( empty( $excerpts ) || empty( $original_index_fields ) ) {
1825
+ echo "disabled='disabled'";
1826
+ }
1827
+ ?>
1828
+ />
1829
+ <?php esc_html_e( 'Use custom field content for building excerpts', 'relevanssi' ); ?>
1830
  </label>
1831
  </fieldset>
1832
+ <p class="description"><?php esc_html_e( 'Use the custom fields setting for indexing for excerpt-making as well. Enabling this option will show custom field content in Relevanssi-generated excerpts.', 'relevanssi' ); ?>
1833
+ <?php
1834
+ if ( RELEVANSSI_PREMIUM ) {
1835
+ esc_html_e( 'Enable this option to use PDF content for excerpts.', 'relevanssi' );
1836
+ }
1837
+ ?>
1838
  </p>
1839
 
1840
+ <p class="description"><?php esc_html_e( 'Current custom field setting', 'relevanssi' ); ?>:
1841
  <?php
1842
+ if ( 'visible' === $original_index_fields ) {
1843
+ esc_html_e( 'all visible custom fields', 'relevanssi' );
1844
+ } elseif ( 'all' === $original_index_fields ) {
1845
+ esc_html_e( 'all custom fields', 'relevanssi' );
1846
+ } elseif ( ! empty( $original_index_fields ) ) {
1847
+ printf( '<code>%s</code>', esc_html( $original_index_fields ) );
1848
+ } elseif ( RELEVANSSI_PREMIUM ) {
1849
+ esc_html_e( 'Just PDF content', 'relevanssi' );
1850
+ } else {
1851
+ esc_html_e( 'None selected', 'relevanssi' );
1852
+ }
1853
+ ?>
1854
+ </p>
1855
  </td>
1856
  </tr>
1857
  </table>
1858
 
1859
+ <h2><?php esc_html_e( 'Search hit highlighting', 'relevanssi' ); ?></h2>
1860
 
1861
+ <table id="relevanssi_highlighting" class="form-table
1862
+ <?php
1863
+ if ( empty( $excerpts ) ) {
1864
+ echo 'relevanssi_disabled';
1865
+ }
1866
+ ?>
1867
+ ">
1868
  <tr>
1869
  <th scope="row">
1870
+ <label for='relevanssi_highlight'><?php esc_html_e( 'Highlight type', 'relevanssi' ); ?></label>
1871
  </th>
1872
  <td>
1873
+ <select name='relevanssi_highlight' id='relevanssi_highlight'
1874
+ <?php
1875
+ if ( empty( $excerpts ) ) {
1876
+ echo "disabled='disabled'";
1877
+ }
1878
+ ?>
1879
+ >
1880
+ <option value='no' <?php echo esc_html( $highlight_none ); ?>><?php esc_html_e( 'No highlighting', 'relevanssi' ); ?></option>
1881
+ <option value='mark' <?php echo esc_html( $highlight_mark ); ?>>&lt;mark&gt;</option>
1882
+ <option value='em' <?php echo esc_html( $highlight_em ); ?>>&lt;em&gt;</option>
1883
+ <option value='strong' <?php echo esc_html( $highlight_strong ); ?>>&lt;strong&gt;</option>
1884
+ <option value='col' <?php echo esc_html( $highlight_col ); ?>><?php esc_html_e( 'Text color', 'relevanssi' ); ?></option>
1885
+ <option value='bgcol' <?php echo esc_html( $highlight_bgcol ); ?>><?php esc_html_e( 'Background color', 'relevanssi' ); ?></option>
1886
+ <option value='css' <?php echo esc_html( $highlight_style ); ?>><?php esc_html_e( 'CSS Style', 'relevanssi' ); ?></option>
1887
+ <option value='class' <?php echo esc_html( $highlight_class ); ?>><?php esc_html_e( 'CSS Class', 'relevanssi' ); ?></option>
1888
  </select>
1889
+ <p class="description"><?php esc_html_e( 'Requires custom snippets to work.', 'relevanssi' ); ?></p>
1890
  </td>
1891
  </tr>
1892
+ <tr id="relevanssi_txt_col" class='<?php echo esc_attr( $txt_col_display ); ?>'>
1893
  <th scope="row">
1894
+ <label for="relevanssi_txt_col"><?php esc_html_e( 'Text color', 'relevanssi' ); ?></label>
1895
  </th>
1896
  <td>
1897
+ <input type='text' name='relevanssi_txt_col' id='relevanssi_txt_col' size='7' class="color-field" data-default-color="#ff0000" value='<?php echo esc_attr( $txt_col ); ?>'
1898
+ <?php
1899
+ if ( empty( $excerpts ) ) {
1900
+ echo "disabled='disabled'";
1901
+ }
1902
+ ?>
1903
+ />
1904
  </td>
1905
  </tr>
1906
+ <tr id="relevanssi_bg_col" class=' <?php echo esc_attr( $bg_col_display ); ?>'>
1907
  <th scope="row">
1908
+ <label for="relevanssi_bg_col"><?php esc_html_e( 'Background color', 'relevanssi' ); ?></label>
1909
  </th>
1910
  <td>
1911
+ <input type='text' name='relevanssi_bg_col' id='relevanssi_bg_col' size='7' class="color-field" data-default-color="#ffaf75" value='<?php echo esc_attr( $bg_col ); ?>'
1912
+ <?php
1913
+ if ( empty( $excerpts ) ) {
1914
+ echo "disabled='disabled'";
1915
+ }
1916
+ ?>
1917
+ />
1918
  </td>
1919
  </tr>
1920
+ <tr id="relevanssi_css" class=' <?php echo esc_attr( $css_display ); ?>'>
1921
  <th scope="row">
1922
+ <label for='relevanssi_css'><?php esc_html_e( 'CSS style for highlights', 'relevanssi' ); ?></label>
1923
  </th>
1924
  <td>
1925
+ <input type='text' name='relevanssi_css' id='relevanssi_css' size='60' value='<?php echo esc_attr( $css ); ?>'
1926
+ <?php
1927
+ if ( empty( $excerpts ) ) {
1928
+ echo "disabled='disabled'";
1929
+ }
1930
+ ?>
1931
+ />
1932
+ <?php // Translators: %s is a <span> tag. ?>
1933
+ <p class="description"><?php printf( esc_html__( 'The highlights will be wrapped in a %s with this CSS in the style parameter.', 'relevanssi' ), '&lt;span&gt;' ); ?></p>
1934
  </td>
1935
  </tr>
1936
+ <tr id="relevanssi_class" class=' <?php echo esc_attr( $class_display ); ?>'>
1937
  <th scope="row">
1938
+ <label for='relevanssi_class'><?php esc_html_e( 'CSS class for highlights', 'relevanssi' ); ?></label>
1939
  </th>
1940
  <td>
1941
+ <input type='text' name='relevanssi_class' id='relevanssi_class' size='60' value='<?php echo esc_attr( $class ); ?>'
1942
+ <?php
1943
+ if ( empty( $excerpts ) ) {
1944
+ echo "disabled='disabled'";
1945
+ }
1946
+ ?>
1947
+ />
1948
+ <?php // Translators: %s is a <span> tag. ?>
1949
+ <p class="description"><?php printf( esc_html__( 'The highlights will be wrapped in a %s with this class.', 'relevanssi' ), '&lt;span&gt;' ); ?></p>
1950
  </td>
1951
  </tr>
1952
  <tr>
1953
  <th scope="row">
1954
+ <label for='relevanssi_hilite_title'><?php esc_html_e( 'Highlight in titles', 'relevanssi' ); ?></label>
1955
  </th>
1956
  <td>
1957
  <fieldset>
1958
+ <legend class="screen-reader-text"><?php esc_html_e( 'Highlight query terms in titles', 'relevanssi' ); ?></legend>
1959
  <label for='relevanssi_hilite_title'>
1960
+ <input type='checkbox' name='relevanssi_hilite_title' id='relevanssi_hilite_title' <?php echo esc_html( $highlight_title ); ?>
1961
+ <?php
1962
+ if ( empty( $excerpts ) ) {
1963
+ echo "disabled='disabled'";
1964
+ }
1965
+ ?>
1966
+ />
1967
+ <?php esc_html_e( 'Highlight query terms in titles', 'relevanssi' ); ?>
1968
  </label>
1969
  </fieldset>
1970
+ <?php // Translators: %1$s is 'the_title()', %2$s is 'relevanssi_the_title()'. ?>
1971
+ <p class="description"><?php printf( esc_html__( 'Highlights in titles require changes to the search results template. You need to replace %1$s in the search results template with %2$s. For more information, see the contextual help.', 'relevanssi' ), '<code>the_title()</code>', '<code>relevanssi_the_title()</code>' ); ?></p>
1972
  </td>
1973
  </tr>
1974
  <tr>
1975
  <th scope="row">
1976
+ <label for='relevanssi_highlight_docs'><?php esc_html_e( 'Highlight in documents', 'relevanssi' ); ?></label>
1977
  </th>
1978
  <td>
1979
  <fieldset>
1980
+ <legend class="screen-reader-text"><?php esc_html_e( 'Highlight query terms in documents', 'relevanssi' ); ?></legend>
1981
  <label for='relevanssi_highlight_docs'>
1982
+ <input type='checkbox' name='relevanssi_highlight_docs' id='relevanssi_highlight_docs' <?php echo esc_html( $highlight_docs ); ?>
1983
+ <?php
1984
+ if ( empty( $excerpts ) ) {
1985
+ echo "disabled='disabled'";
1986
+ }
1987
+ ?>
1988
+ />
1989
+ <?php esc_html_e( 'Highlight query terms in documents', 'relevanssi' ); ?>
1990
  </label>
1991
  </fieldset>
1992
+ <?php // Translators: %s is 'highlight'. ?>
1993
+ <p class="description"><?php printf( esc_html__( 'Highlights hits when user opens the post from search results. This requires an extra parameter (%s) to the links from the search results pages, which Relevanssi should add automatically.', 'relevanssi' ), '<code>highlight</code>' ); ?></p>
1994
  </td>
1995
  </tr>
1996
+ <?php
1997
+ if ( function_exists( 'relevanssi_form_highlight_external' ) ) {
1998
+ relevanssi_form_highlight_external( $highlight_docs_ext, $excerpts );
1999
+ }
2000
+ ?>
2001
  <tr>
2002
  <th scope="row">
2003
+ <label for='relevanssi_highlight_comments'><?php esc_html_e( 'Highlight in comments', 'relevanssi' ); ?></label>
2004
  </th>
2005
  <td>
2006
  <fieldset>
2007
+ <legend class="screen-reader-text"><?php esc_html_e( 'Highlight query terms in comments', 'relevanssi' ); ?></legend>
2008
  <label for='relevanssi_highlight_comments'>
2009
+ <input type='checkbox' name='relevanssi_highlight_comments' id='relevanssi_highlight_comments' <?php echo esc_html( $highlight_coms ); ?>
2010
+ <?php
2011
+ if ( empty( $excerpts ) ) {
2012
+ echo "disabled='disabled'";
2013
+ }
2014
+ ?>
2015
+ />
2016
+ <?php esc_html_e( 'Highlight query terms in comments', 'relevanssi' ); ?>
2017
  </label>
2018
  </fieldset>
2019
+ <p class="description"><?php esc_html_e( 'Highlights hits in comments when user opens the post from search results.', 'relevanssi' ); ?></p>
2020
  </td>
2021
  </tr>
2022
  <tr>
2023
  <th scope="row">
2024
+ <label for='relevanssi_word_boundaries'><?php esc_html_e( 'Highlighting problems with non-ASCII alphabet?', 'relevanssi' ); ?></label>
2025
  </th>
2026
  <td>
2027
  <fieldset>
2028
+ <legend class="screen-reader-text"><?php esc_html_e( 'Uncheck this if you use non-ASCII characters', 'relevanssi' ); ?></legend>
2029
  <label for='relevanssi_word_boundaries'>
2030
+ <input type='checkbox' name='relevanssi_word_boundaries' id='relevanssi_word_boundaries' <?php echo esc_html( $word_boundaries ); ?>
2031
+ <?php
2032
+ if ( empty( $excerpts ) ) {
2033
+ echo "disabled='disabled'";
2034
+ }
2035
+ ?>
2036
+ />
2037
+ <?php esc_html_e( 'Uncheck this if you use non-ASCII characters', 'relevanssi' ); ?>
2038
  </label>
2039
  </fieldset>
2040
+ <p class="description"><?php esc_html_e( "If you use non-ASCII characters (like Cyrillic alphabet) and the highlights don't work, unchecking this option may make the highlights work.", 'relevanssi' ); ?></p>
2041
  </td>
2042
  </tr>
2043
  </table>
2044
 
2045
+ <h2><?php esc_html_e( 'Breakdown of search results', 'relevanssi' ); ?></h2>
2046
 
2047
+ <table id="relevanssi_breakdown" class="form-table
2048
+ <?php
2049
+ if ( empty( $excerpts ) ) {
2050
+ echo 'relevanssi_disabled';
2051
+ }
2052
+ ?>
2053
+ ">
2054
  <tr>
2055
  <th scope="row">
2056
+ <label for='relevanssi_show_matches'><?php esc_html_e( 'Breakdown of search hits in excerpts', 'relevanssi' ); ?></label>
2057
  </th>
2058
  <td>
2059
  <fieldset>
2060
+ <legend class="screen-reader-text"><?php esc_html_e( 'Show the breakdown of search hits in the excerpts', 'relevanssi' ); ?></legend>
2061
  <label for='relevanssi_show_matches'>
2062
+ <input type='checkbox' name='relevanssi_show_matches' id='relevanssi_show_matches' <?php echo esc_html( $show_matches ); ?>
2063
+ <?php
2064
+ if ( empty( $excerpts ) ) {
2065
+ echo "disabled='disabled'";
2066
+ }
2067
+ ?>
2068
+ />
2069
+ <?php esc_html_e( 'Show the breakdown of search hits in the excerpts.', 'relevanssi' ); ?>
2070
  </label>
2071
  </fieldset>
2072
+ <p class="description"><?php esc_html_e( 'Requires custom snippets to work.', 'relevanssi' ); ?></p>
2073
  </td>
2074
  </tr>
2075
  <tr>
2076
  <th scope="row">
2077
+ <label for='relevanssi_show_matches_text'><?php esc_html_e( 'The breakdown format', 'relevanssi' ); ?></label>
2078
  </th>
2079
  <td>
2080
+ <textarea name='relevanssi_show_matches_text' id='relevanssi_show_matches_text' cols="80" rows="4"
2081
+ <?php
2082
+ if ( empty( $excerpts ) ) {
2083
+ echo "disabled='disabled'";
2084
+ }
2085
+ ?>
2086
+ ><?php echo esc_attr( $show_matches_text ); ?></textarea>
2087
+ <p class="description"><?php esc_html_e( 'Use %body%, %title%, %tags% and %comments% to display the number of hits (in different parts of the post), %total% for total hits, %score% to display the document weight and %terms% to show how many hits each search term got.', 'relevanssi' ); /* phpcs:ignore WordPress.WP.I18n */ ?></p>
2088
  </td>
2089
  </tr>
2090
  </table>
2091
 
2092
+ <?php endif; // Active tab: excerpts & highlights. ?>
2093
 
2094
+ <?php if ( 'indexing' === $active_tab ) : ?>
2095
 
2096
  <?php
2097
+
2098
+ $docs_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT doc) FROM ' . $relevanssi_variables['relevanssi_table'] . ' WHERE doc != -1' ); // WPCS: unprepared SQL ok, Relevanssi table name.
2099
+ $terms_count = $wpdb->get_var( 'SELECT COUNT(*) FROM ' . $relevanssi_variables['relevanssi_table'] ); // WPCS: unprepared SQL ok, Relevanssi table name.
2100
+ $biggest_doc = $wpdb->get_var( 'SELECT doc FROM ' . $relevanssi_variables['relevanssi_table'] . ' ORDER BY doc DESC LIMIT 1' ); // WPCS: unprepared SQL ok, Relevanssi table name.
2101
+
2102
+ if ( RELEVANSSI_PREMIUM ) {
2103
+ $user_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT item) FROM ' . $relevanssi_variables['relevanssi_table'] . " WHERE type = 'user'" ); // WPCS: unprepared SQL ok, Relevanssi table name.
2104
+ $taxterm_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT item) FROM ' . $relevanssi_variables['relevanssi_table'] . " WHERE (type != 'post' AND type != 'attachment' AND type != 'user')" ); // WPCS: unprepared SQL ok, Relevanssi table name.
2105
  }
2106
 
2107
  ?>
2108
  <div id="indexing_tab">
2109
+
2110
  <table class="form-table">
2111
  <tr>
2112
  <th scope="row">
2113
+ <input type='submit' name='submit' value='<?php esc_attr_e( 'Save the options', 'relevanssi' ); ?>' class='button button-primary' /><br /><br />
2114
+ <input type="button" id="build_index" name="index" value="<?php esc_attr_e( 'Build the index', 'relevanssi' ); ?>" class='button-primary' /><br /><br />
2115
+ <input type="button" id="continue_indexing" name="continue" value="<?php esc_attr_e( 'Index unindexed posts', 'relevanssi' ); ?>" class='button-primary' />
2116
  </th>
2117
  <td>
2118
  <div id='indexing_button_instructions'>
2119
+ <?php // Translators: %s is "Build the index". ?>
2120
+ <p class="description"><?php printf( esc_html__( '%s empties the existing index and rebuilds it from scratch.', 'relevanssi' ), '<strong>' . esc_html__( 'Build the index', 'relevanssi' ) . '</strong>' ); ?></p>
2121
+ <?php // Translators: %s is "Build the index". ?>
2122
+ <p class="description"><?php printf( esc_html__( "%s doesn't empty the index and only indexes those posts that are not indexed. You can use it if you have to interrupt building the index.", 'relevanssi' ), '<strong>' . esc_html__( 'Index unindexed posts', 'relevanssi' ) . '</strong>' ); ?>
2123
+ <?php
2124
+ if ( RELEVANSSI_PREMIUM ) {
2125
+ esc_html_e( "This doesn't index any taxonomy terms or users.", 'relevanssi' );
2126
+ }
2127
+ ?>
2128
+ </p>
2129
  </div>
2130
  <div id='relevanssi-note' style='display: none'></div>
2131
  <div id='relevanssi-progress' class='rpi-progress'><div class="rpi-indicator"></div></div>
2132
+ <div id='relevanssi-timer'><?php esc_html_e( 'Time elapsed', 'relevanssi' ); ?>: <span id="relevanssi_elapsed">0:00:00</span> | <?php esc_html_e( 'Time remaining', 'relevanssi' ); ?>: <span id="relevanssi_estimated"><?php esc_html_e( 'some time', 'relevanssi' ); ?></span></div>
2133
  <textarea id='results' rows='10' cols='80'></textarea>
2134
  </td>
2135
  </tr>
2136
  <tr>
2137
+ <th scope="row"><?php esc_html_e( 'State of the index', 'relevanssi' ); ?></td>
2138
+ <td id="stateoftheindex"><p><?php echo esc_html( $docs_count ); ?> <?php echo esc_html( _n( 'document in the index.', 'documents in the index.', $docs_count, 'relevanssi' ) ); ?>
2139
+ <?php if ( RELEVANSSI_PREMIUM ) : ?>
2140
+ <br /><?php echo esc_html( $user_count ); ?> <?php echo esc_html( _n( 'user in the index.', 'users in the index.', $user_count, 'relevanssi' ) ); ?><br />
2141
+ <?php echo esc_html( $taxterm_count ); ?> <?php echo esc_html( _n( 'taxonomy term in the index.', 'taxonomy terms in the index.', $taxterm_count, 'relevanssi' ) ); ?>
2142
  <?php endif; ?>
2143
  </p>
2144
+ <p><?php echo esc_html( $terms_count ); ?> <?php echo esc_html( _n( 'term in the index.', 'terms in the index.', $terms_count, 'relevanssi' ) ); ?><br />
2145
+ <?php echo esc_html( $biggest_doc ); ?> <?php esc_html_e( 'is the highest post ID indexed.', 'relevanssi' ); ?></p>
2146
  </td>
2147
  </tr>
2148
  </table>
2149
 
2150
+ <?php
2151
+ if ( count( $index_post_types ) < 2 ) {
2152
+ printf( '<p><strong>%s</strong></p>', esc_html__( "WARNING: You've chosen no post types to index. Nothing will be indexed. Choose some post types to index.", 'relevanssi' ) );
2153
  }
2154
+ ?>
2155
 
2156
+ <h2 id="indexing"><?php esc_html_e( 'Indexing options', 'relevanssi' ); ?></h2>
2157
 
2158
+ <p><?php esc_html_e( 'Any changes to the settings on this page require reindexing before they take effect.', 'relevanssi' ); ?></p>
2159
 
2160
  <table class="form-table">
2161
  <tr>
2162
+ <th scope="row"><?php esc_html_e( 'Post types', 'relevanssi' ); ?></th>
2163
  <td>
2164
 
2165
  <table class="widefat" id="index_post_types_table">
2166
  <thead>
2167
  <tr>
2168
+ <th><?php esc_html_e( 'Type', 'relevanssi' ); ?></th>
2169
+ <th><?php esc_html_e( 'Index', 'relevanssi' ); ?></th>
2170
+ <th><?php esc_html_e( 'Excluded from search?', 'relevanssi' ); ?></th>
2171
  </tr>
2172
  </thead>
2173
  <?php
2174
+ $pt_1 = get_post_types( array( 'exclude_from_search' => '0' ) );
2175
+ $pt_2 = get_post_types( array( 'exclude_from_search' => false ) );
2176
+ $public_types = array_merge( $pt_1, $pt_2 );
2177
+ $post_types = get_post_types();
2178
+ foreach ( $post_types as $type ) {
2179
+ if ( in_array( $type, array( 'nav_menu_item', 'revision' ), true ) ) {
2180
+ continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2181
  }
2182
+ $checked = '';
2183
+ if ( in_array( $type, $index_post_types, true ) ) {
2184
+ $checked = 'checked="checked"';
2185
+ }
2186
+ $label = sprintf( '%s', $type );
2187
+ $excluded_from_search = __( 'yes', 'relevanssi' );
2188
+ if ( in_array( $type, $public_types, true ) ) {
2189
+ $excluded_from_search = __( 'no', 'relevanssi' );
2190
+ }
2191
+ $name_id = 'relevanssi_index_type_' . $type;
2192
+ printf( '<tr><td>%1$s</td><td><input type="checkbox" name="%2$s" id="%2$s" %3$s /></td><td>%4$s</td></tr>',
2193
+ esc_html( $label ), esc_attr( $name_id ), esc_html( $checked ), esc_html( $excluded_from_search ) );
2194
+ }
2195
  ?>
2196
  <tr style="display:none">
2197
  <td>
2205
  </td>
2206
  </tr>
2207
  </table>
2208
+ <?php // Translators: %1$s is 'attachment', %2$s opens the link, %3$s closes it. ?>
2209
+ <p class="description"><?php printf( esc_html__( '%1$s includes all attachment types. If you want to index only some attachments, see %2$sControlling attachment types in the Knowledge base%3$s.', 'relevanssi' ), '<code>attachment</code>', '<a href="https://www.relevanssi.com/knowledge-base/controlling-attachment-types-index/">', '</a>' ); ?></p>
2210
  </td>
2211
  </tr>
2212
 
2213
  <tr>
2214
  <th scope="row">
2215
+ <?php esc_html_e( 'Taxonomies', 'relevanssi' ); ?>
2216
  </th>
2217
  <td>
2218
 
2219
  <table class="widefat" id="custom_taxonomies_table">
2220
  <thead>
2221
  <tr>
2222
+ <th><?php esc_html_e( 'Taxonomy', 'relevanssi' ); ?></th>
2223
+ <th><?php esc_html_e( 'Index', 'relevanssi' ); ?></th>
2224
+ <th><?php esc_html_e( 'Public?', 'relevanssi' ); ?></th>
2225
  </tr>
2226
  </thead>
2227
 
2228
  <?php
2229
+ $taxos = get_taxonomies( '', 'objects' );
2230
+ foreach ( $taxos as $taxonomy ) {
2231
+ if ( in_array( $taxonomy->name, array( 'nav_menu', 'link_category' ), true ) ) {
2232
+ continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2233
  }
2234
+ $checked = '';
2235
+ if ( in_array( $taxonomy->name, $index_taxonomies_list, true ) ) {
2236
+ $checked = 'checked="checked"';
2237
+ }
2238
+ $label = sprintf( '%s', $taxonomy->name );
2239
+ $public = __( 'no', 'relevanssi' );
2240
+ if ( $taxonomy->public ) {
2241
+ $public = __( 'yes', 'relevanssi' );
2242
+ }
2243
+ $name_id = 'relevanssi_index_taxonomy_' . $taxonomy->name;
2244
+ printf( '<tr><td>%1$s</td><td><input type="checkbox" name="%2$s" id="%2$s" %3$s /></td><td>%4$s</td></tr>',
2245
+ esc_html( $label ), esc_attr( $name_id ), esc_html( $checked ), esc_html( $public ) );
2246
+ }
2247
  ?>
2248
  </table>
2249
 
2250
+ <p class="description"><?php esc_html_e( 'If you check a taxonomy here, the terms for that taxonomy are indexed with the posts. If you for example choose "post_tag", searching for a tag will find all posts that have the tag.', 'relevanssi' ); ?>
2251
 
2252
  </td>
2253
  </tr>
2254
 
2255
  <tr>
2256
  <th scope="row">
2257
+ <label for='relevanssi_index_comments'><?php esc_html_e( 'Comments', 'relevanssi' ); ?></label>
2258
  </th>
2259
  <td>
2260
  <select name='relevanssi_index_comments' id='relevanssi_index_comments'>
2261
+ <option value='none' <?php echo esc_html( $index_comments_none ); ?>><?php esc_html_e( 'none', 'relevanssi' ); ?></option>
2262
+ <option value='normal' <?php echo esc_html( $index_comments_normal ); ?>><?php esc_html_e( 'comments', 'relevanssi' ); ?></option>
2263
+ <option value='all' <?php echo esc_html( $index_comments_all ); ?>><?php esc_html_e( 'comments and pingbacks', 'relevanssi' ); ?></option>
2264
  </select>
2265
+ <p class="description"><?php esc_html_e( 'If you choose to index comments, you can choose if you want to index just comments, or everything including comments and track- and pingbacks.', 'relevanssi' ); ?></p>
2266
  </td>
2267
  </tr>
2268
 
2269
  <tr>
2270
  <th scope="row">
2271
+ <label for='relevanssi_index_fields'><?php esc_html_e( 'Custom fields', 'relevanssi' ); ?></label>
2272
  </th>
2273
  <td>
2274
  <select name='relevanssi_index_fields_select' id='relevanssi_index_fields_select'>
2275
+ <option value='none' <?php echo esc_html( $fields_select_none ); ?>><?php esc_html_e( 'none', 'relevanssi' ); ?></option>
2276
+ <option value='all' <?php echo esc_html( $fields_select_all ); ?>><?php esc_html_e( 'all', 'relevanssi' ); ?></option>
2277
+ <option value='visible' <?php echo esc_html( $fields_select_visible ); ?>><?php esc_html_e( 'visible', 'relevanssi' ); ?></option>
2278
+ <option value='some' <?php echo esc_html( $fields_select_some ); ?>><?php esc_html_e( 'some', 'relevanssi' ); ?></option>
2279
  </select>
2280
+ <p class="description">
2281
+ <?php
2282
+ esc_html_e( "'All' indexes all custom fields for posts.", 'relevanssi' );
2283
+ echo '<br/>';
2284
+ esc_html_e( "'Visible' only includes the custom fields that are visible in the user interface (with names that don't start with an underscore).", 'relevanssi' );
2285
+ echo '<br/>';
2286
+ esc_html_e( "'Some' lets you choose individual custom fields to index.", 'relevanssi' );
2287
+ ?>
2288
+ </p>
2289
+ <div id="index_field_input"
2290
+ <?php
2291
+ if ( empty( $fields_select_some ) ) {
2292
+ echo 'style="display: none"';
2293
+ }
2294
+ ?>
2295
+ >
2296
+ <input type='text' name='relevanssi_index_fields' id='relevanssi_index_fields' size='60' value='<?php echo esc_attr( $index_fields ); ?>' />
2297
+ <p class="description"><?php esc_html_e( "Enter a comma-separated list of custom fields to include in the index. With Relevanssi Premium, you can also use 'fieldname_%_subfieldname' notation for ACF repeater fields.", 'relevanssi' ); ?></p>
2298
+ <p class="description"><?php esc_html_e( "You can use 'relevanssi_index_custom_fields' filter hook to adjust which custom fields are indexed.", 'relevanssi' ); ?></p>
2299
  </div>
2300
  <?php if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) : ?>
2301
+ <?php // Translators: %1$s is the 'some' option and %2$s is '_sku'. ?>
2302
+ <p class="description"><?php printf( esc_html__( 'If you want the SKU included, choose %1$s and enter %2$s. Also see the contextual help for more details.', 'relevanssi' ), esc_html( "'" . __( 'some', 'relevanssi' ) . "'" ), '<code>_sku</code>' ); ?></p>
2303
  <?php endif; ?>
2304
  </td>
2305
  </tr>
2306
 
 
 
2307
  <tr>
2308
  <th scope="row">
2309
+ <label for='relevanssi_index_author'><?php esc_html_e( 'Author display names', 'relevanssi' ); ?></label>
2310
  </th>
2311
  <td>
2312
  <fieldset>
2313
+ <legend class="screen-reader-text"><?php esc_html_e( 'Index the post author display name', 'relevanssi' ); ?></legend>
2314
  <label for='relevanssi_index_author'>
2315
+ <input type='checkbox' name='relevanssi_index_author' id='relevanssi_index_author' <?php echo esc_html( $index_author ); ?> />
2316
+ <?php esc_html_e( 'Index the post author display name', 'relevanssi' ); ?>
2317
  </label>
2318
+ <p class="description"><?php esc_html_e( 'Searching for the post author display name will return posts by that author.', 'relevanssi' ); ?></p>
2319
  </fieldset>
2320
  </td>
2321
  </tr>
2322
 
2323
  <tr>
2324
  <th scope="row">
2325
+ <label for='relevanssi_index_excerpt'><?php esc_html_e( 'Excerpts', 'relevanssi' ); ?></label>
2326
  </th>
2327
  <td>
2328
  <fieldset>
2329
+ <legend class="screen-reader-text"><?php esc_html_e( 'Index the post excerpt', 'relevanssi' ); ?></legend>
2330
  <label for='relevanssi_index_excerpt'>
2331
+ <input type='checkbox' name='relevanssi_index_excerpt' id='relevanssi_index_excerpt' <?php echo esc_html( $index_excerpt ); ?> />
2332
+ <?php esc_html_e( 'Index the post excerpt', 'relevanssi' ); ?>
2333
  </label>
2334
+ <p class="description"><?php esc_html_e( 'Relevanssi will find posts by the content in the excerpt.', 'relevanssi' ); ?></p>
2335
  <?php if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) : ?>
2336
+ <p class="description"><?php esc_html_e( "WooCommerce stores the product short description in the excerpt, so it's a good idea to index excerpts.", 'relevanssi' ); ?></p>
2337
  <?php endif; ?>
2338
  </fieldset>
2339
  </td>
2341
 
2342
  </table>
2343
 
2344
+ <h2><?php esc_html_e( 'Shortcodes', 'relevanssi' ); ?></h2>
2345
 
2346
  <table class="form-table">
2347
  <tr>
2348
  <th scope="row">
2349
+ <label for='relevanssi_expand_shortcodes'><?php esc_html_e( 'Expand shortcodes', 'relevanssi' ); ?></label>
2350
  </th>
2351
  <td>
2352
  <fieldset>
2353
+ <legend class="screen-reader-text"><?php esc_html_e( 'Index the post excerpt', 'relevanssi' ); ?></legend>
2354
  <label for='relevanssi_expand_shortcodes'>
2355
+ <input type='checkbox' name='relevanssi_expand_shortcodes' id='relevanssi_expand_shortcodes' <?php echo esc_html( $expand_shortcodes ); ?> />
2356
+ <?php esc_html_e( 'Expand shortcodes when indexing', 'relevanssi' ); ?>
2357
  </label>
2358
  <?php if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) : ?>
2359
+ <p class="description important"><?php esc_html_e( "WooCommerce has shortcodes that don't work well with Relevanssi. With WooCommerce, make sure the option is disabled.", 'relevanssi' ); ?></p>
2360
  <?php endif; ?>
2361
+ <p class="description"><?php esc_html_e( 'If checked, Relevanssi will expand shortcodes in post content before indexing. Otherwise shortcodes will be stripped.', 'relevanssi' ); ?></p>
2362
+ <p class="description"><?php esc_html_e( 'If you use shortcodes to include dynamic content, Relevanssi will not keep the index updated, the index will reflect the status of the shortcode content at the moment of indexing.', 'relevanssi' ); ?></p>
2363
  </fieldset>
2364
  </td>
2365
  </tr>
2366
 
2367
+ <?php
2368
+ if ( function_exists( 'relevanssi_form_disable_shortcodes' ) ) {
2369
+ relevanssi_form_disable_shortcodes( $disable_shortcodes );
2370
+ }
2371
+ ?>
2372
 
2373
  </table>
2374
 
2375
+ <?php
2376
+ if ( function_exists( 'relevanssi_form_index_users' ) ) {
2377
+ relevanssi_form_index_users( $index_users, $index_subscribers, $index_user_fields );
2378
+ }
2379
+ if ( function_exists( 'relevanssi_form_index_synonyms' ) ) {
2380
+ relevanssi_form_index_synonyms( $index_synonyms );
2381
+ }
2382
+ if ( function_exists( 'relevanssi_form_index_taxonomies' ) ) {
2383
+ relevanssi_form_index_taxonomies( $index_taxonomies, $index_terms );
2384
+ }
2385
+ if ( function_exists( 'relevanssi_form_index_pdf_parent' ) ) {
2386
+ relevanssi_form_index_pdf_parent( $index_pdf_parent, $index_post_types );
2387
+ }
2388
+ ?>
2389
 
2390
+ <h2><?php esc_html_e( 'Advanced indexing settings', 'relevanssi' ); ?></h2>
2391
 
2392
+ <p><button type="button" id="show_advanced_indexing"><?php esc_html_e( 'Show advanced settings', 'relevanssi' ); ?></button></p>
2393
 
2394
  <table class="form-table screen-reader-text" id="advanced_indexing">
2395
  <tr>
2396
  <th scope="row">
2397
+ <label for='relevanssi_min_word_length'><?php esc_html_e( 'Minimum word length', 'relevanssi' ); ?></label>
2398
  </th>
2399
  <td>
2400
+ <input type='number' name='relevanssi_min_word_length' id='relevanssi_min_word_length' value='<?php echo esc_attr( $min_word_length ); ?>' />
2401
+ <p class="description"><?php esc_html_e( 'Words shorter than this many letters will not be indexed.', 'relevanssi' ); ?></p>
2402
+ <?php // Translators: %1$s is 'relevanssi_block_one_letter_searches' and %2$s is 'false'. ?>
2403
+ <p class="description"><?php printf( esc_html__( 'To enable one-letter searches, you need to add a filter function on the filter hook %1$s that returns %2$s.', 'relevanssi' ), '<code>relevanssi_block_one_letter_searches</code>', '<code>false</code>' ); ?></p>
2404
  </td>
2405
  </tr>
2406
  <tr>
2407
+ <th scope="row"><?php esc_html_e( 'Punctuation control' ); ?></th>
2408
+ <td><p class="description"><?php esc_html_e( 'Here you can adjust how the punctuation is controlled. For more information, see help. Remember that any changes here require reindexing, otherwise searches will fail to find posts they should.', 'relevanssi' ); ?></p></td>
2409
  </tr>
2410
  <tr>
2411
  <th scope="row">
2412
+ <label for='relevanssi_punct_hyphens'><?php esc_html_e( 'Hyphens and dashes', 'relevanssi' ); ?></label>
2413
  </th>
2414
  <td>
2415
  <select name='relevanssi_punct_hyphens' id='relevanssi_punct_hyphens'>
2416
+ <option value='keep' <?php echo esc_html( $punct_hyphens_keep ); ?>><?php esc_html_e( 'Keep', 'relevanssi' ); ?></option>
2417
+ <option value='replace' <?php echo esc_html( $punct_hyphens_replace ); ?>><?php esc_html_e( 'Replace with spaces', 'relevanssi' ); ?></option>
2418
+ <option value='remove' <?php echo esc_html( $punct_hyphens_remove ); ?>><?php esc_html_e( 'Remove', 'relevanssi' ); ?></option>
2419
  </select>
2420
+ <p class="description"><?php esc_html_e( 'How Relevanssi should handle hyphens and dashes (en and em dashes)? Replacing with spaces is generally the best option, but in some cases removing completely is the best option. Keeping them is rarely the best option.', 'relevanssi' ); ?></p>
2421
 
2422
  </td>
2423
  </tr>
2424
  <tr>
2425
  <th scope="row">
2426
+ <label for='relevanssi_punct_quotes'><?php esc_html_e( 'Apostrophes and quotes', 'relevanssi' ); ?></label>
2427
  </th>
2428
  <td>
2429
  <select name='relevanssi_punct_quotes' id='relevanssi_punct_quotes'>
2430
+ <option value='replace' <?php echo esc_html( $punct_quotes_replace ); ?>><?php esc_html_e( 'Replace with spaces', 'relevanssi' ); ?></option>
2431
+ <option value='remove' <?php echo esc_html( $punct_quotes_remove ); ?>><?php esc_html_e( 'Remove', 'relevanssi' ); ?></option>
2432
  </select>
2433
+ <p class="description"><?php esc_html_e( "How Relevanssi should handle apostrophes and quotes? It's not possible to keep them; that would lead to problems. Default behaviour is to replace with spaces, but sometimes removing makes sense.", 'relevanssi' ); ?></p>
2434
 
2435
  </td>
2436
  </tr>
2437
  <tr>
2438
  <th scope="row">
2439
+ <label for='relevanssi_punct_ampersands'><?php esc_html_e( 'Ampersands', 'relevanssi' ); ?></label>
2440
  </th>
2441
  <td>
2442
  <select name='relevanssi_punct_ampersands' id='relevanssi_punct_ampersands'>
2443
+ <option value='keep' <?php echo esc_html( $punct_ampersands_keep ); ?>><?php esc_html_e( 'Keep', 'relevanssi' ); ?></option>
2444
+ <option value='replace' <?php echo esc_html( $punct_ampersands_replace ); ?>><?php esc_html_e( 'Replace with spaces', 'relevanssi' ); ?></option>
2445
+ <option value='remove' <?php echo esc_html( $punct_ampersands_remove ); ?>><?php esc_html_e( 'Remove', 'relevanssi' ); ?></option>
2446
  </select>
2447
+ <p class="description"><?php esc_html_e( 'How Relevanssi should handle ampersands? Replacing with spaces is generally the best option, but if you talk a lot about D&amp;D, for example, keeping the ampersands is useful.', 'relevanssi' ); ?></p>
2448
 
2449
  </td>
2450
  </tr>
2451
+ <tr>
2452
+ <th scope="row">
2453
+ <label for='relevanssi_punct_decimals'><?php esc_html_e( 'Decimal separators', 'relevanssi' ); ?></label>
2454
+ </th>
2455
+ <td>
2456
+ <select name='relevanssi_punct_decimals' id='relevanssi_punct_decimals'>
2457
+ <option value='keep' <?php echo esc_html( $punct_decimals_keep ); ?>><?php esc_html_e( 'Keep', 'relevanssi' ); ?></option>
2458
+ <option value='replace' <?php echo esc_html( $punct_decimals_replace ); ?>><?php esc_html_e( 'Replace with spaces', 'relevanssi' ); ?></option>
2459
+ <option value='remove' <?php echo esc_html( $punct_decimals_remove ); ?>><?php esc_html_e( 'Remove', 'relevanssi' ); ?></option>
2460
+ </select>
2461
+ <p class="description"><?php esc_html_e( 'How Relevanssi should handle periods between decimals? Replacing with spaces is the default option, but that often leads to the numbers being removed completely. If you need to search decimal numbers a lot, keep the periods.', 'relevanssi' ); ?></p>
2462
 
2463
+ </td>
2464
+ </tr>
2465
+ <?php
2466
+ if ( function_exists( 'relevanssi_form_thousands_separator' ) ) {
2467
+ relevanssi_form_thousands_separator( $thousand_separator );
2468
+ }
2469
+ if ( function_exists( 'relevanssi_form_mysql_columns' ) ) {
2470
+ relevanssi_form_mysql_columns( $mysql_columns );
2471
+ }
2472
+ if ( function_exists( 'relevanssi_form_internal_links' ) ) {
2473
+ relevanssi_form_internal_links( $internal_links_noindex, $internal_links_strip, $internal_links_nostrip );
2474
+ }
2475
+ ?>
2476
 
2477
+ </table>
2478
 
2479
+ <p><button type="button" style="display: none" id="hide_advanced_indexing"><?php esc_html_e( 'Hide advanced settings', 'relevanssi' ); ?></button></p>
2480
 
2481
+ </div> <?php // End #indexing_tab. ?>
2482
 
2483
+ <?php endif; // Active tab: indexing. ?>
2484
 
2485
+ <?php if ( 'attachments' === $active_tab ) : ?>
2486
 
2487
+ <?php
2488
+ if ( function_exists( 'relevanssi_form_attachments' ) ) {
2489
+ relevanssi_form_attachments( $index_post_types, $index_pdf_parent );
2490
+ } else {
2491
+ $display_save_button = false;
2492
+ ?>
2493
 
2494
+ <h2><?php esc_html_e( 'Indexing attachment content', 'relevanssi' ); ?></h2>
 
 
 
 
 
 
2495
 
2496
+ <p><?php esc_html_e( 'With Relevanssi Premium, you can index the text contents of PDF attachments. The contents of the attachments are processed on an external service, which makes the feature reliable and light on your own server performance.', 'relevanssi' ); ?></p>
2497
+ <?php // Translators: %1$s starts the link, %2$s closes it. ?>
2498
+ <p><?php printf( esc_html__( 'In order to access this and many other delightful Premium features, %1$sbuy Relevanssi Premium here%2$s.', 'relevanssi' ), '<a href="https://www.relevanssi.com/buy-premium/">', '</a>' ); ?></p>
2499
 
 
 
2500
  <?php } ?>
2501
 
2502
+ <?php endif; // Active tab: attachments. ?>
2503
 
2504
+ <?php if ( 'synonyms' === $active_tab ) : ?>
2505
 
2506
+ <h3 id="synonyms"><?php esc_html_e( 'Synonyms', 'relevanssi' ); ?></h3>
2507
 
2508
  <table class="form-table">
2509
  <tr>
2510
  <th scope="row">
2511
+ <?php esc_html_e( 'Synonyms', 'relevanssi' ); ?>
2512
  </th>
2513
  <td>
2514
+ <p class="description"><?php esc_html_e( 'Add synonyms here to make the searches find better results. If you notice your users frequently misspelling a product name, or for other reasons use many names for one thing, adding synonyms will make the results better.', 'relevanssi' ); ?></p>
2515
+
2516
+ <p class="description"><?php esc_html_e( "Do not go overboard, though, as too many synonyms can make the search confusing: users understand if a search query doesn't match everything, but they get confused if the searches match to unexpected things.", 'relevanssi' ); ?></p>
2517
  <br />
2518
+ <textarea name='relevanssi_synonyms' id='relevanssi_synonyms' rows='9' cols='60'><?php echo esc_textarea( htmlspecialchars( $synonyms ) ); ?></textarea>
2519
+
2520
+ <p class="description"><?php esc_html_e( 'The format here is <code>key = value</code>. If you add <code>dog = hound</code> to the list of synonyms, searches for <code>dog</code> automatically become a search for <code>dog hound</code> and will thus match to posts that include either <code>dog</code> or <code>hound</code>. This only works in OR searches: in AND searches the synonyms only restrict the search, as now the search only finds posts that contain <strong>both</strong> <code>dog</code> and <code>hound</code>.', 'relevanssi' ); ?></p>
2521
 
2522
+ <p class="description"><?php esc_html_e( 'The synonyms are one direction only. If you want both directions, add the synonym again, reversed: <code>hound = dog</code>.', 'relevanssi' ); ?></p>
2523
 
2524
+ <p class="description"><?php esc_html_e( "It's possible to use phrases for the value, but not for the key. <code>dog = \"great dane\"</code> works, but <code>\"great dane\" = dog</code> doesn't.", 'relevanssi' ); ?></p>
2525
 
2526
+ <?php if ( RELEVANSSI_PREMIUM ) : ?>
2527
+ <p class="description"><?php esc_html_e( 'If you want to use synonyms in AND searches, enable synonym indexing on the Indexing tab.', 'relevanssi' ); ?></p>
2528
  <?php endif; ?>
2529
  </td>
2530
  </tr>
2531
  </table>
2532
 
2533
+ <?php endif; // Active tab: synonyms. ?>
2534
 
2535
+ <?php if ( 'stopwords' === $active_tab ) : ?>
2536
 
2537
+ <h3 id="stopwords"><?php esc_html_e( 'Stopwords', 'relevanssi' ); ?></h3>
2538
 
2539
  <?php relevanssi_show_stopwords(); ?>
2540
 
2541
  <?php
2542
+ /**
2543
+ * Filters whether the common words list is displayed or not.
2544
+ *
2545
+ * The list of 25 most common words is displayed by default, but if the index is
2546
+ * big, displaying the list can take a long time. This filter can be used to
2547
+ * turn the list off.
2548
+ *
2549
+ * @param boolean If true, show the list; if false, don't show it.
2550
+ */
2551
+ if ( apply_filters( 'relevanssi_display_common_words', true ) ) {
2552
+ relevanssi_common_words( 25 );
2553
+ }
2554
  ?>
2555
 
2556
+ <?php endif; // Active tab: stopwords. ?>
2557
 
2558
+ <?php if ( 'importexport' === $active_tab ) : ?>
2559
 
2560
+ <?php
2561
+ if ( function_exists( 'relevanssi_form_importexport' ) ) {
2562
+ relevanssi_form_importexport( $serialized_options );
2563
+ }
2564
  ?>
2565
 
2566
  <?php endif; ?>
2567
 
2568
+ <?php if ( $display_save_button ) : ?>
2569
 
2570
+ <input type='submit' name='submit' value='<?php esc_attr_e( 'Save the options', 'relevanssi' ); ?>' class='button button-primary' />
2571
 
2572
  <?php endif; ?>
2573
 
2575
  </div>
2576
 
2577
  <?php
 
 
2578
  }
2579
 
2580
+ /**
2581
+ * Displays a list of stopwords.
2582
+ *
2583
+ * Displays the list of stopwords and gives the controls for adding new stopwords.
2584
+ *
2585
+ * @global object $wpdb The WP database interface.
2586
+ * @global array $relevanssi_variables The global Relevanssi variables array.
2587
+ */
2588
  function relevanssi_show_stopwords() {
2589
+ global $wpdb, $relevanssi_variables;
 
 
2590
 
2591
+ $plugin = 'relevanssi';
2592
+ if ( RELEVANSSI_PREMIUM ) {
2593
+ $plugin = 'relevanssi-premium';
2594
+ }
2595
 
2596
+ printf( '<p>%s</p>', esc_html__( 'Enter a word here to add it to the list of stopwords. The word will automatically be removed from the index, so re-indexing is not necessary. You can enter many words at the same time, separate words with commas.', 'relevanssi' ) );
2597
  ?>
2598
  <table class="form-table">
2599
  <tr>
2600
  <th scope="row">
2601
+ <label for="addstopword"><p><?php esc_html_e( 'Stopword(s) to add', 'relevanssi' ); ?>
2602
  </th>
2603
  <td>
2604
  <textarea name="addstopword" id="addstopword" rows="2" cols="80"></textarea>
2605
+ <p><input type="submit" value="<?php esc_attr_e( 'Add', 'relevanssi' ); ?>" class='button' /></p>
2606
  </td>
2607
  </tr>
2608
  </table>
2609
+ <p><?php esc_html_e( "Here's a list of stopwords in the database. Click a word to remove it from stopwords. Removing stopwords won't automatically return them to index, so you need to re-index all posts after removing stopwords to get those words back to index.", 'relevanssi' ); ?></p>
 
 
2610
 
2611
  <table class="form-table">
2612
  <tr>
2613
  <th scope="row">
2614
+ <?php esc_html_e( 'Current stopwords', 'relevanssi' ); ?>
2615
  </th>
2616
  <td>
2617
+ <?php
2618
+ echo '<ul>';
2619
+ $results = $wpdb->get_results( 'SELECT * FROM ' . $relevanssi_variables['stopword_table'] ); // WPCS: unprepared SQL ok, Relevanssi table name.
 
2620
  $exportlist = array();
2621
+ foreach ( $results as $stopword ) {
2622
+ $sw = stripslashes( $stopword->stopword );
2623
+ printf( '<li style="display: inline;"><input type="submit" name="removestopword" value="%s"/></li>', esc_attr( $sw ) );
2624
+ array_push( $exportlist, $sw );
2625
  }
2626
+ echo '</ul>';
2627
 
2628
+ $exportlist = htmlspecialchars( implode( ', ', $exportlist ) );
2629
  ?>
2630
+ <p><input type="submit" id="removeallstopwords" name="removeallstopwords" value="<?php esc_attr_e( 'Remove all stopwords', 'relevanssi' ); ?>" class='button' /></p>
2631
  </td>
2632
  </tr>
2633
  <tr>
2634
  <th scope="row">
2635
+ <?php esc_html_e( 'Exportable list of stopwords', 'relevanssi' ); ?>
2636
  </th>
2637
  <td>
2638
+ <textarea name="stopwords" id="stopwords" rows="2" cols="80"><?php echo esc_textarea( $exportlist ); ?></textarea>
2639
+ <p class="description"><?php esc_html_e( 'You can copy the list of stopwords here if you want to back up the list, copy it to a different blog or otherwise need the list.', 'relevanssi' ); ?></p>
2640
  </td>
2641
  </tr>
2642
  </table>
2643
 
2644
  <?php
 
2645
  }
2646
 
2647
+ /**
2648
+ * Displays the contextual help menu.
2649
+ *
2650
+ * @global object $wpdb The WP database interface.
2651
+ */
2652
  function relevanssi_admin_help() {
2653
  global $wpdb;
2654
 
2655
  $screen = get_current_screen();
2656
  $screen->add_help_tab( array(
2657
+ 'id' => 'relevanssi-searching',
2658
+ 'title' => __( 'Searching', 'relevanssi' ),
2659
+ 'content' => '<ul>' .
2660
+ // Translators: %1$s is 'orderby', %2$s is the Codex page URL.
2661
+ '<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>' .
2662
+ '<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' ) .
2663
+ '<pre>add_filter( \'relevanssi_fuzzy_query\', \'rlv_partial_inside_words\' );
2664
+ function rlv_partial_inside_words( $query ) {
2665
  return "(term LIKE \'%#term#%\')";
2666
  }</pre></li>' .
2667
+ // Translators: %s is 'relevanssi_throttle_limit'.
2668
+ '<li>' . sprintf( __( 'In order to adjust the throttle limit, you can use the %s filter hook.', 'relevanssi' ), '<code>relevanssi_throttle_limit</code>' ) .
2669
  '<pre>add_filter("relevanssi_throttle_limit", function( $limit ) { return 200; } );</pre>' .
2670
+ '<li>' . __( "It's not usually necessary to adjust the limit from 500, but in some cases performance gains can be achieved by setting a lower limit. We don't suggest going under 200, as low values will make the results worse.", 'relevanssi' ) . '</li>' .
2671
+ '</ul>',
2672
  ));
2673
  $screen->add_help_tab( array(
2674
+ 'id' => 'relevanssi-search-restrictions',
2675
+ 'title' => __( 'Restrictions', 'relevanssi' ),
2676
+ 'content' => '<ul>' .
2677
+ '<li>' . __( 'If you want the general search to target all posts, but have a single search form target only certain posts, you can add a hidden input variable to the search form. ', 'relevanssi' ) . '</li>' .
2678
+ '<li>' . __( 'For example in order to restrict the search to categories 10, 14 and 17, you could add this to the search form:', 'relevanssi' ) .
2679
+ '<pre>&lt;input type="hidden" name="cats" value="10,14,17" /&gt;</pre></li>' .
2680
+ '<li>' . __( 'To restrict the search to posts tagged with alfa AND beta, you could add this to the search form:', 'relevanssi' ) .
2681
+ '<pre>&lt;input type="hidden" name="tag" value="alfa+beta" /&gt;</pre></li>' .
2682
+ // Translators: %s is the link to the Codex page.
2683
+ '<li>' . sprintf( __( 'For all the possible options, see the Codex documentation for %s.', 'relevanssi' ), '<a href="https://codex.wordpress.org/Class_Reference/WP_Query">WP_Query</a>' ) . '</li>' .
2684
+ '</ul>',
2685
  ));
2686
  $screen->add_help_tab( array(
2687
+ 'id' => 'relevanssi-search-exclusions',
2688
+ 'title' => __( 'Exclusions', 'relevanssi' ),
2689
+ 'content' => '<ul>' .
2690
+ // Translators: %s is the link to the Codex page.
2691
+ '<li>' . sprintf( __( 'For more exclusion options, see the Codex documentation for %s. For example, to exclude tag ID 10, use', 'relevanssi' ), '<a href="https://codex.wordpress.org/Class_Reference/WP_Query">WP_Query</a>' ) .
2692
  '<pre>&lt;input type="hidden" name="tag__not_in" value="10" /&gt;</pre></li>' .
2693
+ // Translators: %s is 'relevanssi_do_not_index'.
2694
+ '<li>' . sprintf( __( 'To exclude posts from the index and not just from the search, you can use the %s filter hook. This would not index posts that have a certain taxonomy term:', 'relevanssi' ), '<code>relevanssi_do_not_index</code>' ) .
2695
+ '<pre>add_filter( \'relevanssi_do_not_index\', \'rlv_index_filter\', 10, 2 );
2696
+ function rlv_index_filter( $block, $post_id ) {
2697
+ if ( has_term( \'jazz\', \'genre\', $post_id ) ) {
2698
+ $block = true;
2699
+ }
2700
  return $block;
2701
  }
2702
+ </pre></li>' .
2703
+ // Translators: %s is a link to the Relevanssi knowledge base.
2704
+ '<li>' . sprintf( __( "For more examples, see <a href='%s'>the related knowledge base posts</a>.", 'relevanssi' ), 'https://www.relevanssi.com/tag/relevanssi_do_not_index/' ) . '</li>' .
2705
+ '</ul>',
2706
+ ));
2707
+ $screen->add_help_tab( array(
2708
+ 'id' => 'relevanssi-logging',
2709
+ 'title' => __( 'Logs', 'relevanssi' ),
2710
+ 'content' => '<ul>' .
2711
+ // Translators: %s is 'relevanssi_user_searches_limit'.
2712
+ '<li>' . sprintf( __( 'By default, the User searches page shows 20 most common keywords. In order to see more, you can adjust the value with the %s filter hook, like this:', 'relevanssi' ), '<code>relevanssi_user_searches_limit</code>' ) .
2713
+ "<pre>add_filter( 'relevanssi_user_searches_limit', function() { return 50; } );</pre></li>" .
2714
+ // Translators: %s is the name of the database table.
2715
+ '<li>' . sprintf( __( 'The complete logs are stored in the %s database table, where you can access them if you need more information than what the User searches page provides.', 'relevanssi' ), '<code>' . $wpdb->prefix . 'relevanssi_log</code>' ) . '</li>' .
2716
+ '</ul>',
2717
  ));
2718
  $screen->add_help_tab( array(
2719
+ 'id' => 'relevanssi-excerpts',
2720
+ 'title' => __( 'Excerpts', 'relevanssi' ),
2721
+ 'content' => '<ul>' .
2722
+ '<li>' . __( 'Building custom excerpts can be slow. If you are not actually using the excerpts, make sure you disable the option.', 'relevanssi' ) . '</li>' .
2723
+ // Translators: %s is 'the_excerpt()'.
2724
+ '<li>' . sprintf( __( 'Custom snippets require that the search results template uses %s to print out the excerpts.', 'relevanssi' ), '<code>the_excerpt()</code>' ) . '</li>' .
2725
+ '<li>' . __( 'Generally, Relevanssi generates the excerpts from post content. If you want to include custom field content in the excerpt-building, this can be done with a simple setting from the excerpt settings.', 'relevanssi' ) . '</li>' .
2726
+ // Translators: %1$s is 'relevanssi_pre_excerpt_content', %2$s is 'relevanssi_excerpt_content'.
2727
+ '<li>' . sprintf( __( 'If you want more control over what content Relevanssi uses to create the excerpts, you can use the %1$s and %2$s filter hooks to adjust the content.', 'relevanssi' ), '<code>relevanssi_pre_excerpt_content</code>', '<code>relevanssi_excerpt_content</code>' ) . '</li>' .
2728
+ // Translators: %s is 'relevanssi_disable_shortcodes_excerpt'.
2729
+ '<li>' . sprintf( __( 'Some shortcode do not work well with Relevanssi excerpt-generation. Relevanssi disables some shortcodes automatically to prevent problems. This can be adjusted with the %s filter hook.', 'relevanssi' ), '<code>relevanssi_disable_shortcodes_excerpt</code>' ) . '</li>' .
2730
+ // Translators: %s is 'relevanssi_optimize_excerpts'.
2731
+ '<li>' . sprintf( __( "If you want Relevanssi to build excerpts faster and don't mind that they may be less than perfect in quality, add a filter that returns true on hook %s.", 'relevanssi' ), '<code>relevanssi_optimize_excerpts</code>' ) .
2732
+ "<pre>add_filter( 'relevanssi_optimize_excerpts', '__return_true' );</pre></li>" .
2733
+ '</ul>',
2734
  ));
2735
  $screen->add_help_tab( array(
2736
+ 'id' => 'relevanssi-highlights',
2737
+ 'title' => __( 'Highlights', 'relevanssi' ),
2738
+ 'content' => '<ul>' .
2739
+ '<li>' . __( "Title highlights don't appear automatically, because that led to problems with highlights appearing in wrong places and messing up navigation menus, for example.", 'relevanssi' ) . '</li>' .
2740
+ // Translators: %1$s is 'the_title()', %2$s is 'relevanssi_the_title()'.
2741
+ '<li>' . sprintf( __( 'In order to see title highlights from Relevanssi, replace %1$s in the search results template with %2$s. It does the same thing, but supports Relevanssi title highlights.', 'relevanssi' ), '<code>the_title()</code>', '<code>relevanssi_the_title()</code>' ) . '</li>' .
2742
+ '</ul>',
 
 
 
 
2743
  ));
2744
  $screen->add_help_tab( array(
2745
+ 'id' => 'relevanssi-punctuation',
2746
+ 'title' => __( 'Punctuation', 'relevanssi' ),
2747
+ 'content' => '<ul>' .
2748
+ '<li>' . __( 'Relevanssi removes punctuation. Some punctuation is removed, some replaced with spaces. Advanced indexing settings include some of the more common settings people want to change.', 'relevanssi' ) . '</li>' .
2749
+ // Translators: %1$s is 'relevanssi_punctuation_filter', %2$s is 'relevanssi_remove_punctuation'.
2750
+ '<li>' . sprintf( __( 'For more fine-tuned changes, you can use %1$s filter hook to adjust what is replaced with what, and %2$s filter hook to completely override the default punctuation control.', 'relevanssi' ), '<code>relevanssi_punctuation_filter</code>', '<code>relevanssi_remove_punctuation</code>' ) . '</li>' .
2751
+ // Translators: %s is the URL to the Knowledge Base entry.
2752
+ '<li>' . sprintf( __( "For more examples, see <a href='%s'>the related knowledge base posts</a>.", 'relevanssi' ), 'https://www.relevanssi.com/tag/relevanssi_remove_punct/' ) . '</li>' .
2753
+ '</ul>',
2754
  ));
2755
  $screen->add_help_tab( array(
2756
+ 'id' => 'relevanssi-helpful-shortcodes',
2757
+ 'title' => __( 'Helpful shortcodes', 'relevanssi' ),
2758
+ 'content' => '<ul>' .
2759
+ // Translators: %s is '[noindex]'.
2760
+ '<li>' . sprintf( __( "If you have content that you don't want indexed, you can wrap that content in a %s shortcode.", 'relevanssi' ), '<code>[noindex]</code>' ) . '</li>' .
2761
+ // Translators: %s is '[searchform]'.
2762
+ '<li>' . sprintf( __( 'If you need a search form on some page on your site, you can use the %s shortcode to print out a basic search form.', 'relevanssi' ), '<code>[searchform]</code>' ) . '</li>' .
2763
+ // Translators: %1$s is '[searchform post_types="page"]', %2$s is '[searchform cats="10,14,17"]'.
2764
+ '<li>' . sprintf( __( 'If you need to add query variables to the search form, the shortcode takes parameters, which are then printed out as hidden input fields. To get a search form with a post type restriction, you can use %1$s. To restrict the search to categories 10, 14 and 17, you can use %2$s and so on.', 'relevanssi' ), '<code>[searchform post_types="page"]</code>', '<code>[searchform cats="10,14,17"]</code>' ) . '</li>' .
2765
+ '</ul>',
2766
  ));
2767
  $screen->add_help_tab( array(
2768
+ 'id' => 'relevanssi-title-woocommerce',
2769
+ 'title' => __( 'WooCommerce', 'relevanssi' ),
2770
+ 'content' => '<ul>' .
2771
+ '<li>' . __( "If your SKUs include hyphens or other punctuation, do note that Relevanssi replaces most punctuation with spaces. That's going to cause issues with SKU searches.", 'relevanssi' ) . '</li>' .
2772
+ // Translators: %s is the Knowledge Base URL.
2773
+ '<li>' . sprintf( __( "For more details how to fix that issue, see <a href='%s'>WooCommerce tips in Relevanssi user manual</a>.", 'relevanssi' ), 'https://www.relevanssi.com/user-manual/woocommerce/' ) . '</li>' .
2774
+ '</ul>',
2775
  ));
2776
  $screen->add_help_tab( array(
2777
+ 'id' => 'relevanssi-exact-match',
2778
+ 'title' => __( 'Exact match bonus', 'relevanssi' ),
2779
+ 'content' => '<ul>' .
2780
+ // Translators: %s is the name of the filter hook.
2781
+ '<li>' . sprintf( __( 'To adjust the amount of the exact match bonus, you can use the %s filter hook. It works like this:', 'relevanssi' ), '<code>relevanssi_exact_match_bonus</code>' ) .
2782
+ "<pre>add_filter( 'relevanssi_exact_match_bonus', 'rlv_adjust_bonus' );
2783
+ function rlv_adjust_bonus( \$bonus ) {
2784
+ return array( 'title' => 10, 'content' => 5 );
2785
+ }</li>" .
2786
+ // Translators: %1$s is the title weight and %2$s is the content weight.
2787
+ '<li>' . sprintf( esc_html__( 'The default values are %1$s for titles and %2$s for content.', 'relevanssi' ), '<code>5</code>', '<code>2</code>' ) . '</ul>',
2788
  ));
2789
  $screen->set_help_sidebar(
2790
  '<p><strong>' . __( 'For more information:', 'relevanssi' ) . '</strong></p>' .
2791
+ '<p><a href="https://www.relevanssi.com/knowledge-base/" target="_blank">' . __( 'Plugin knowledge base', 'relevanssi' ) . '</a></p>' .
2792
+ '<p><a href="https://wordpress.org/tags/relevanssi?forum_id=10" target="_blank">' . __( 'WordPress.org forum', 'relevanssi' ) . '</a></p>'
2793
  );
2794
  }
2795
 
2796
+ /**
2797
+ * Adds admin scripts to Relevanssi pages.
2798
+ *
2799
+ * Hooks to the 'admin_enqueue_scripts' action hook.
2800
+ *
2801
+ * @global array $relevanssi_variables The global Relevanssi variables array.
2802
+ *
2803
+ * @param string $hook The hook suffix for the current admin page.
2804
+ */
2805
+ function relevanssi_add_admin_scripts( $hook ) {
2806
  global $relevanssi_variables;
2807
 
2808
+ $plugin_dir_url = plugin_dir_url( $relevanssi_variables['file'] );
2809
 
2810
  // Only enqueue on Relevanssi pages.
2811
  $acceptable_hooks = array(
2812
+ 'toplevel_page_relevanssi-premium/relevanssi',
2813
+ 'settings_page_relevanssi-premium/relevanssi',
2814
+ 'toplevel_page_relevanssi/relevanssi',
2815
+ 'settings_page_relevanssi/relevanssi',
2816
  );
2817
+ if ( ! in_array( $hook, $acceptable_hooks, true ) ) {
2818
+ return;
2819
+ }
2820
 
2821
  wp_enqueue_style( 'wp-color-picker' );
2822
  wp_enqueue_script( 'relevanssi_admin_js', $plugin_dir_url . 'lib/admin_scripts.js', array( 'wp-color-picker' ) );
2823
+ if ( ! RELEVANSSI_PREMIUM ) {
2824
+ wp_enqueue_script( 'relevanssi_admin_js_free', $plugin_dir_url . 'lib/admin_scripts_free.js', array( 'relevanssi_admin_js' ) );
2825
+ }
2826
+ if ( RELEVANSSI_PREMIUM ) {
2827
+ wp_enqueue_script( 'relevanssi_admin_js_premium', $plugin_dir_url . 'premium/admin_scripts_premium.js', array( 'relevanssi_admin_js' ) );
2828
+ }
2829
  wp_enqueue_style( 'relevanssi_admin_css', $plugin_dir_url . 'lib/admin_styles.css' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2830
 
2831
+ $localizations = array(
2832
+ 'confirm' => __( 'Click OK to copy Relevanssi options to all subsites', 'relevanssi' ),
2833
+ 'confirm_stopwords' => __( 'Are you sure you want to remove all stopwords?', 'relevanssi' ),
2834
+ 'truncating_index' => __( 'Wiping out the index...', 'relevanssi' ),
2835
+ 'done' => __( 'Done.', 'relevanssi' ),
2836
+ 'indexing_users' => __( 'Indexing users...', 'relevanssi' ),
2837
+ 'indexing_taxonomies' => __( 'Indexing the following taxonomies:', 'relevanssi' ),
2838
+ 'counting_posts' => __( 'Counting posts...', 'relevanssi' ),
2839
+ 'counting_terms' => __( 'Counting taxonomy terms...', 'relevanssi' ),
2840
+ 'counting_users' => __( 'Counting users...', 'relevanssi' ),
2841
+ 'posts_found' => __( 'posts found.', 'relevanssi' ),
2842
+ 'terms_found' => __( 'taxonomy terms found.', 'relevanssi' ),
2843
+ 'users_found' => __( 'users found.', 'relevanssi' ),
2844
+ 'taxonomy_disabled' => __( 'Taxonomy term indexing is disabled.', 'relevanssi' ),
2845
+ 'user_disabled' => __( 'User indexing is disabled.', 'relevanssi' ),
2846
+ 'indexing_complete' => __( 'Indexing complete.', 'relevanssi' ),
2847
+ 'excluded_posts' => __( 'posts excluded.', 'relevanssi' ),
2848
+ 'options_changed' => __( 'Settings have changed, please save the options before indexing.', 'relevanssi' ),
2849
+ 'reload_state' => __( 'Reload the page to refresh the state of the index.', 'relevanssi' ),
2850
+ 'pdf_reset_confirm' => __( 'Are you sure you want to delete all attachment content from the index?', 'relevanssi' ),
2851
+ 'pdf_reset_done' => __( 'Relevanssi attachment data wiped clean.', 'relevanssi' ),
2852
+ 'hour' => __( 'hour', 'relevanssi' ),
2853
+ 'hours' => __( 'hours', 'relevanssi' ),
2854
+ 'about' => __( 'about', 'relevanssi' ),
2855
+ 'sixty_min' => __( 'about an hour', 'relevanssi' ),
2856
+ 'ninety_min' => __( 'about an hour and a half', 'relevanssi' ),
2857
+ 'minute' => __( 'minute', 'relevanssi' ),
2858
+ 'minutes' => __( 'minutes', 'relevanssi' ),
2859
+ 'underminute' => __( 'less than a minute', 'relevanssi' ),
2860
+ 'notimeremaining' => __( "we're done!", 'relevanssi' ),
2861
  );
2862
 
2863
+ wp_localize_script( 'relevanssi_admin_js', 'relevanssi', $localizations );
2864
+
2865
+ $nonce = array(
2866
+ 'indexing_nonce' => wp_create_nonce( 'relevanssi_indexing_nonce' ),
2867
+ );
2868
+
2869
+ wp_localize_script( 'relevanssi_admin_js', 'nonce', $nonce );
2870
+
2871
  }
2872
 
2873
+ /**
2874
+ * Sanitizes hex color strings.
2875
+ *
2876
+ * A copy of sanitize_hex_color(), because that isn't always available.
2877
+ *
2878
+ * @param string $color A hex color string to sanitize.
2879
+ *
2880
+ * @return string Sanitized hex string, or an empty string.
2881
+ */
2882
+ function relevanssi_sanitize_hex_color( $color ) {
2883
  if ( '' === $color ) {
2884
  return '';
2885
  }
2886
+
2887
  // 3 or 6 hex digits, or the empty string.
2888
+ if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
2889
  return $color;
2890
  }
2891
+
2892
+ return '';
2893
+ }
lib/search.php CHANGED
@@ -1,125 +1,194 @@
1
  <?php
2
-
3
- function relevanssi_query($posts, $query = false) {
4
- $admin_search = get_option('relevanssi_admin_search');
5
- ($admin_search == 'on') ? $admin_search = true : $admin_search = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  global $relevanssi_active;
8
 
9
- if (!$query) return $posts;
 
 
10
 
11
- $search_ok = true; // we will search!
12
- if (!$query->is_search()) {
13
- $search_ok = false; // no, we can't
14
  }
15
- if (!$query->is_main_query()) {
16
- $search_ok = false; // no, we can't
17
  }
18
 
19
  // Uses $wp_query->is_admin instead of is_admin() to help with Ajax queries that
20
  // use 'admin_ajax' hook (which sets is_admin() to true whether it's an admin search
21
  // or not.
22
- if ($query->is_search() && $query->is_admin) {
23
- $search_ok = false; // but if this is an admin search, reconsider
24
- if ($admin_search) $search_ok = true; // yes, we can search!
 
 
25
  }
26
 
27
- if ($query->is_admin && empty($query->query_vars['s'])) {
28
- $search_ok = false;
29
  }
30
 
31
- // Disable search in media library search
32
- if ($search_ok) {
33
- if ($query->query_vars['post_type'] == 'attachment' && $query->query_vars['post_status'] == 'inherit,private') {
34
  $search_ok = false;
35
  }
36
  }
37
 
38
- $search_ok = apply_filters('relevanssi_search_ok', $search_ok);
 
 
 
 
 
 
 
 
 
39
 
40
- if ($relevanssi_active) {
41
- $search_ok = false; // Relevanssi is already in action
42
  }
43
 
44
- if ($search_ok) {
45
- $query = apply_filters('relevanssi_modify_wp_query', $query);
46
- $posts = relevanssi_do_query($query);
 
 
 
 
 
 
 
 
 
 
47
  }
48
-
49
  return $posts;
50
  }
51
 
52
-
53
- // This is my own magic working.
54
- function relevanssi_search($args) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  global $wpdb, $relevanssi_variables;
56
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
57
 
58
- $filtered_args = apply_filters( 'relevanssi_search_filters', $args );
59
- $q = $filtered_args['q'];
60
- $tax_query = $filtered_args['tax_query'];
 
 
 
 
 
61
  $tax_query_relation = $filtered_args['tax_query_relation'];
62
- $post_query = $filtered_args['post_query'];
63
- $parent_query = $filtered_args['parent_query'];
64
- $meta_query = $filtered_args['meta_query'];
65
- $date_query = $filtered_args['date_query'];
66
- $expost = $filtered_args['expost'];
67
- $post_type = $filtered_args['post_type'];
68
- $post_status = $filtered_args['post_status'];
69
- $operator = $filtered_args['operator'];
70
- $search_blogs = $filtered_args['search_blogs'];
71
- $author = $filtered_args['author'];
72
- $orderby = $filtered_args['orderby'];
73
- $order = $filtered_args['order'];
74
- $fields = $filtered_args['fields'];
75
- $sentence = $filtered_args['sentence'];
76
-
77
- $hits = array();
78
-
79
- $query_restrictions = "";
80
- if (!isset($tax_query_relation)) $tax_query_relation = "or";
81
- $tax_query_relation = strtolower($tax_query_relation);
82
- $term_tax_id = array();
83
- $term_tax_ids = array();
84
- $not_term_tax_ids = array();
85
- $and_term_tax_ids = array();
86
-
87
- if (is_array($tax_query)) {
 
 
 
88
  $is_sub_row = false;
89
- foreach ($tax_query as $row) {
90
- if (isset($row['terms'])) {
91
- list($query_restrictions, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids) = relevanssi_process_tax_query_row($row, $is_sub_row, $tax_query_relation, $query_restrictions, $tax_query_relation, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids);
92
- }
93
- else {
94
  $row_tax_query_relation = $tax_query_relation;
95
- if (isset($row['relation'])) $row_tax_query_relation = strtolower($row['relation']);
96
- foreach ($row as $subrow) {
 
 
97
  $is_sub_row = true;
98
- if (isset($subrow['terms'])) {
99
- list($query_restrictions, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids) = relevanssi_process_tax_query_row($subrow, $is_sub_row, $tax_query_relation, $query_restrictions, $tax_query_relation, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids);
100
- // For subrows, we only want the query_restrictions
101
  }
102
  }
103
  }
104
  }
105
 
106
- if ($tax_query_relation == 'or') {
107
- $term_tax_ids = array_unique($term_tax_ids);
108
- if (count($term_tax_ids) > 0) {
109
- $term_tax_ids = implode(',', $term_tax_ids);
110
- $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr
111
- WHERE tr.term_taxonomy_id IN ($term_tax_ids))";
112
- // Clean: all variables are Relevanssi-generated
113
  }
114
- if (count($not_term_tax_ids) > 0) {
115
- $not_term_tax_ids = implode(',', $not_term_tax_ids);
116
- $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr
117
- WHERE tr.term_taxonomy_id IN ($not_term_tax_ids))";
118
- // Clean: all variables are Relevanssi-generated
119
  }
120
- if (count($and_term_tax_ids) > 0) {
121
- $and_term_tax_ids = implode(',', $and_term_tax_ids);
122
- $n = count(explode(',', $and_term_tax_ids));
123
  $query_restrictions .= " AND relevanssi.doc IN (
124
  SELECT ID FROM $wpdb->posts WHERE 1=1
125
  AND (
@@ -128,374 +197,451 @@ function relevanssi_search($args) {
128
  WHERE tr.term_taxonomy_id IN ($and_term_tax_ids)
129
  AND tr.object_id = $wpdb->posts.ID ) = $n
130
  )";
131
- // Clean: all variables are Relevanssi-generated
132
  }
133
  }
134
  }
135
 
136
- if (is_array($post_query)) {
137
- if (!empty($post_query['in'])) {
138
  $valid_values = array();
139
- foreach($post_query['in'] as $post_in_id) {
140
- if (is_numeric($post_in_id)) $valid_values[] = $post_in_id;
 
 
 
 
 
 
 
141
  }
142
- $posts = implode(',', $valid_values);
143
- if (!empty($posts)) $query_restrictions .= " AND relevanssi.doc IN ($posts)";
144
- // Clean: $posts is checked to be integers
145
  }
146
- if (!empty($post_query['not in'])) {
147
  $valid_values = array();
148
- foreach($post_query['not in'] as $post_not_in_id) {
149
- if (is_numeric($post_not_in_id)) $valid_values[] = $post_not_in_id;
 
 
 
 
 
 
 
150
  }
151
- $posts = implode(',', $valid_values);
152
- if (!empty($posts)) $query_restrictions .= " AND relevanssi.doc NOT IN ($posts)";
153
- // Clean: $posts is checked to be integers
154
  }
155
  }
156
 
157
- if (is_array($parent_query)) {
158
- if (!empty($parent_query['parent in'])) {
159
  $valid_values = array();
160
- foreach($parent_query['parent in'] as $post_in_id) {
161
- if (is_numeric($post_in_id)) $valid_values[] = $post_in_id;
 
 
 
 
 
 
 
162
  }
163
- $posts = implode(',', $valid_values);
164
- if (!empty($posts)) $query_restrictions .= " AND relevanssi.doc IN (SELECT ID FROM $wpdb->posts WHERE post_parent IN ($posts))";
165
- // Clean: $posts is checked to be integers
166
  }
167
- if (!empty($parent_query['parent not in'])) {
168
  $valid_values = array();
169
- foreach($parent_query['parent not in'] as $post_not_in_id) {
170
- if (is_numeric($post_not_in_id)) $valid_values[] = $post_not_in_id;
 
 
 
 
 
 
 
171
  }
172
- $posts = implode(',', $valid_values);
173
- if (!empty($posts)) $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT ID FROM $wpdb->posts WHERE post_parent IN ($posts))";
174
- // Clean: $posts is checked to be integers
175
  }
176
  }
177
 
178
- if (is_array($meta_query)) {
179
- $meta_query_restrictions = "";
180
 
181
- $mq_vars = array('meta_query' => $meta_query );
182
 
183
  $mq = new WP_Meta_Query();
184
  $mq->parse_query_vars( $mq_vars );
185
- $meta_sql = $mq->get_sql('post', 'relevanssi', 'doc');
186
- $meta_join = "";
187
- $meta_where = "";
188
- if ($meta_sql) {
189
- $meta_join = $meta_sql['join'];
190
  $meta_where = $meta_sql['where'];
191
  }
192
 
193
  $query_restrictions .= $meta_where;
194
  }
195
 
196
- if (!empty($date_query)) {
197
- if (is_object($date_query) && method_exists($date_query, 'get_sql')) {
198
- $sql = $date_query->get_sql(); // AND ( the query itself )
199
  $query_restrictions .= " AND relevanssi.doc IN ( SELECT DISTINCT(ID) FROM $wpdb->posts WHERE 1 $sql )";
200
- // Clean: $sql generated by $date_query->get_sql() query
201
  }
202
  }
203
 
204
  // If $post_type is not set, see if there are post types to exclude from the search.
205
  // If $post_type is set, there's no need to exclude, as we only include.
206
- !$post_type ? $negative_post_type = relevanssi_get_negative_post_type() : $negative_post_type = NULL;
 
 
 
207
 
208
- $non_post_post_type = NULL;
209
  $non_post_post_types_array = array();
210
- if (function_exists('relevanssi_get_non_post_post_types')) {
 
211
  $non_post_post_types_array = relevanssi_get_non_post_post_types();
212
  }
213
 
214
- if ($post_type) {
215
- if ($post_type == -1) $post_type = null; // Facetious sets post_type to -1 if not selected
216
- if (!is_array($post_type)) {
217
- $post_types = explode(',', $post_type);
218
- }
219
- else {
220
  $post_types = $post_type;
221
  }
 
222
  // This array will contain all regular post types involved in the search parameters.
223
- $post_post_types = array_diff($post_types, $non_post_post_types_array);
224
 
225
  // This array has the non-post post types involved.
226
- $non_post_post_types = array_intersect($post_types, $non_post_post_types_array);
227
 
228
  // Escape both for SQL queries, just in case.
229
- $non_post_post_types = esc_sql($non_post_post_types);
230
- $post_types = esc_sql($post_post_types);
231
 
232
- // Implode to a parameter string, or set to NULL if empty.
233
- $non_post_post_type = count($non_post_post_types) ? "'" . implode( "', '", $non_post_post_types) . "'" : NULL;
234
- $post_type = count($post_types) ? "'" . implode( "', '", $post_types) . "'" : NULL;
 
 
 
 
 
 
235
  }
236
 
237
- if ($post_status) {
238
- if (!is_array($post_status)) {
239
- $post_statuses = esc_sql(explode(',', $post_status));
240
- }
241
- else {
242
- $post_statuses = esc_sql($post_status);
243
  }
244
 
245
- $post_status = count($post_statuses) ? "'" . implode( "', '", $post_statuses) . "'" : NULL;
 
 
 
246
  }
247
 
248
- //Added by OdditY:
249
- //Exclude Post_IDs (Pages) for non-admin search ->
250
- $postex = '';
251
- if (!empty($expost)) {
252
- if ($expost != "") {
253
- $aexpids = explode(",",$expost);
254
- foreach ($aexpids as $exid){
255
- $exid = esc_sql(trim($exid, ' -'));
256
- $postex .= " AND relevanssi.doc != '$exid'";
257
- // Clean: escaped
258
- }
259
  }
 
260
  }
261
- // <- OdditY End
262
 
263
- if ($expost) { //added by OdditY
264
- $query_restrictions .= $postex;
265
  }
266
 
267
- $remove_stopwords = apply_filters('relevanssi_remove_stopwords_in_titles', true);
268
- if (function_exists('wp_encode_emoji')) $q = wp_encode_emoji($q);
269
-
270
- if ($sentence) {
271
- $q = str_replace('"', '', $q);
272
  $q = '"' . $q . '"';
273
  }
274
 
275
- $phrases = relevanssi_recognize_phrases($q);
276
 
277
- if (function_exists('relevanssi_recognize_negatives')) {
278
- $negative_terms = relevanssi_recognize_negatives($q);
279
- }
280
- else {
281
  $negative_terms = false;
282
  }
283
 
284
- if (function_exists('relevanssi_recognize_positives')) {
285
- $positive_terms = relevanssi_recognize_positives($q);
286
- }
287
- else {
288
  $positive_terms = false;
289
  }
290
-
291
- $terms = relevanssi_tokenize($q, $remove_stopwords);
292
 
293
- if (count($terms) < 1) {
 
 
 
 
 
 
 
 
 
294
  // Tokenizer killed all the search terms.
295
  return $hits;
296
  }
297
- $terms = array_keys($terms); // don't care about tf in query
298
 
299
- if ($negative_terms) {
300
- $terms = array_diff($terms, $negative_terms);
301
- /* if (count($terms) < 1) {
302
- return $hits;
303
- }
304
- */ }
305
 
306
- // Go get the count from the options table, but keep running the full query if it's not available
307
- $D = get_option('relevanssi_doc_count');
308
- if (!$D || $D < 1) {
309
- $D = $wpdb->get_var("SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi");
310
- // Clean: no external inputs
311
- update_option('relevanssi_doc_count', $D);
312
  }
313
 
314
  $total_hits = 0;
315
 
316
- $title_matches = array();
317
- $tag_matches = array();
318
- $comment_matches = array();
319
- $link_matches = array();
320
- $body_matches = array();
321
  $category_matches = array();
322
  $taxonomy_matches = array();
323
- $scores = array();
324
- $term_hits = array();
325
 
326
- $fuzzy = get_option('relevanssi_fuzzy');
327
 
328
- if (function_exists('relevanssi_negatives_positives')) {
329
- $query_restrictions .= relevanssi_negatives_positives($negative_terms, $positive_terms, $relevanssi_table);
330
- // Clean: escaped in the function
331
  }
332
 
333
- if (!empty($author)) {
334
- $author_in = array();
335
  $author_not_in = array();
336
- foreach ($author as $id) {
337
- if (!is_numeric($id)) continue;
338
- if ($id > 0) {
339
- $author_in[] = $id;
340
  }
341
- else {
342
- $author_not_in[] = abs($id);
 
 
343
  }
344
  }
345
- if (count($author_in) > 0) {
346
- $authors = implode(',', $author_in);
347
  $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
348
  WHERE posts.post_author IN ($authors))";
349
- // Clean: $authors is always just numbers
350
  }
351
- if (count($author_not_in) > 0) {
352
- $authors = implode(',', $author_not_in);
353
  $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
354
  WHERE posts.post_author IN ($authors))";
355
- // Clean: $authors is always just numbers
356
  }
357
  }
358
 
359
- if ($post_type) {
360
- // A post type is set: add a restriction
361
  $restriction = " AND (
362
  relevanssi.doc IN (
363
  SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
364
  WHERE posts.post_type IN ($post_type)
365
  ) *np*
366
- )";
367
- // Clean: $post_type is escaped
368
 
369
  // There are post types involved that are taxonomies or users, so can't
370
  // match to wp_posts. Add a relevanssi.type restriction.
371
- if ($non_post_post_type) {
372
- $restriction = str_replace('*np*', "OR (relevanssi.type IN ($non_post_post_type))", $restriction);
373
- // Clean: $non_post_post_types is escaped
374
  } else {
375
  // No non-post post types, so remove the placeholder.
376
- $restriction = str_replace('*np*', '', $restriction);
377
  }
378
  $query_restrictions .= $restriction;
379
- }
380
- else {
381
- // No regular post types
382
- if ($non_post_post_type) {
383
  // But there is a non-post post type restriction.
384
  $query_restrictions .= " AND (relevanssi.type IN ($non_post_post_type))";
385
- // Clean: $non_post_post_types is escaped
386
  }
387
  }
388
 
389
- if ($negative_post_type) {
390
  $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
391
  WHERE posts.post_type NOT IN ($negative_post_type))) OR (doc = -1))";
392
- // Clean: $negative_post_type is escaped
393
  }
394
 
395
- if ($post_status) {
396
  global $wp_query;
397
- if ($wp_query->is_admin) {
398
  $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
399
  WHERE posts.post_status IN ($post_status))))";
400
- }
401
- else {
402
- // the -1 is there to get user profiles and category pages
403
  $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
404
  WHERE posts.post_status IN ($post_status))) OR (doc = -1))";
405
  }
406
- // Clean: $post_status is escaped
407
  }
408
 
409
- if ($phrases) {
410
  $query_restrictions .= " $phrases";
411
- // Clean: $phrases is escaped earlier
412
  }
413
 
414
- if (isset($_REQUEST['by_date'])) {
415
- $n = $_REQUEST['by_date'];
416
 
417
- $u = substr($n, -1, 1);
418
- switch ($u) {
419
  case 'h':
420
- $unit = "HOUR";
421
  break;
422
  case 'd':
423
- $unit = "DAY";
424
  break;
425
  case 'm':
426
- $unit = "MONTH";
427
  break;
428
  case 'y':
429
- $unit = "YEAR";
430
  break;
431
  case 'w':
432
- $unit = "WEEK";
433
  break;
434
  default:
435
- $unit = "DAY";
436
  }
437
 
438
- $n = preg_replace('/[hdmyw]/', '', $n);
439
 
440
- if (is_numeric($n)) {
441
  $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
442
  WHERE posts.post_date > DATE_SUB(NOW(), INTERVAL $n $unit))";
443
- // Clean: $n is always numeric, $unit is Relevanssi-generated
444
- }
445
- }
446
-
447
- $query_restrictions = apply_filters('relevanssi_where', $query_restrictions); // Charles St-Pierre
448
- $query_join = "";
449
- if (!empty($meta_join)) $query_join = $meta_join;
450
- $query_join = apply_filters('relevanssi_join', $query_join);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
  $no_matches = true;
453
- if ("always" == $fuzzy) {
454
- $o_term_cond = apply_filters('relevanssi_fuzzy_query', "(relevanssi.term LIKE '#term#%' OR relevanssi.term_reverse LIKE CONCAT(REVERSE('#term#'), '%')) ");
455
- }
456
- else {
 
 
 
 
 
 
 
 
457
  $o_term_cond = " relevanssi.term = '#term#' ";
458
  }
459
 
460
- if (count($terms) < 1) {
461
- $o_term_cond = " relevanssi.term = relevanssi.term ";
462
- $terms[] = "term";
463
  }
464
 
465
- $post_type_weights = get_option('relevanssi_post_type_weights');
466
- if (function_exists('relevanssi_get_recency_bonus')) {
467
- list($recency_bonus, $recency_cutoff_date) = relevanssi_get_recency_bonus();
 
 
 
468
  }
469
- else {
470
- $recency_bonus = false;
471
- $recency_cutoff_date = false;
 
 
 
 
 
 
 
 
 
 
 
 
472
  }
473
- $min_length = get_option('relevanssi_min_word_length');
 
 
474
  $search_again = false;
475
-
476
- $content_option = get_option('relevanssi_content_boost');
477
- if (empty($content_option)) $content_option = 1;
478
- $content_boost = floatval($content_option);
479
- $title_boost = floatval(get_option('relevanssi_title_boost'));
480
- $link_boost = floatval(get_option('relevanssi_link_boost'));
481
- $comment_boost = floatval(get_option('relevanssi_comment_boost'));
482
 
483
  $include_these_posts = array();
484
 
485
  do {
486
- foreach ($terms as $term) {
487
- $term = trim($term); // numeric search terms will start with a space
488
- if (apply_filters('relevanssi_block_one_letter_searches', relevanssi_strlen($term) < 2)) continue;
489
- $term = esc_sql($term);
 
 
 
 
 
 
 
 
 
490
 
491
- if (strpos($o_term_cond, 'LIKE') !== false) {
492
- $term = $wpdb->esc_like($term);
493
  }
494
 
495
- $term_cond = str_replace('#term#', $term, $o_term_cond);
496
 
497
- !empty($post_type_weights['post_tag']) ? $tag = $post_type_weights['post_tag'] : $tag = $relevanssi_variables['post_type_weight_defaults']['post_tag'];
498
- !empty($post_type_weights['category']) ? $cat = $post_type_weights['category'] : $cat = $relevanssi_variables['post_type_weight_defaults']['category'];
 
 
 
 
 
 
499
 
500
  $query = "SELECT DISTINCT(relevanssi.doc), relevanssi.*, relevanssi.title * $title_boost +
501
  relevanssi.content * $content_boost + relevanssi.comment * $comment_boost +
@@ -503,81 +649,95 @@ function relevanssi_search($args) {
503
  relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
504
  relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
505
  FROM $relevanssi_table AS relevanssi $query_join WHERE $term_cond $query_restrictions";
506
- // Clean: $query_restrictions is escaped, $term_cond is escaped
507
 
508
- $query = apply_filters('relevanssi_query_filter', $query);
509
- $matches = $wpdb->get_results($query);
510
-
511
- if (count($matches) < 1) {
 
 
 
 
 
 
 
512
  continue;
513
- }
514
- else {
515
  $no_matches = false;
516
- if (count($include_these_posts) > 0) {
517
- $post_ids_to_add = implode(',', array_keys($include_these_posts));
518
- $existing_ids = array();
519
- foreach ($matches as $match) {
520
  $existing_ids[] = $match->doc;
521
  }
522
- $existing_ids = implode(',', $existing_ids);
523
- $query = "SELECT relevanssi.*, relevanssi.title * $title_boost +
524
  relevanssi.content + relevanssi.comment * $comment_boost +
525
  relevanssi.tag * $tag + relevanssi.link * $link_boost +
526
  relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
527
  relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
528
  FROM $relevanssi_table AS relevanssi WHERE relevanssi.doc IN ($post_ids_to_add)
529
  AND relevanssi.doc NOT IN ($existing_ids) AND $term_cond";
530
- // Clean: no unescaped user inputs
531
- $matches_to_add = $wpdb->get_results($query);
532
- $matches = array_merge($matches, $matches_to_add);
533
  }
534
  }
535
 
536
- relevanssi_populate_array($matches);
537
  global $relevanssi_post_types;
538
 
539
- $total_hits += count($matches);
540
 
541
  $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi
542
  $query_join WHERE $term_cond $query_restrictions";
543
- // Clean: $query_restrictions is escaped, $term_cond is escaped
544
- $query = apply_filters('relevanssi_df_query_filter', $query);
 
 
 
 
 
 
 
545
 
546
- $df = $wpdb->get_var($query);
547
 
548
- if ($df < 1 && "sometimes" == $fuzzy) {
549
  $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi
550
  $query_join WHERE (relevanssi.term LIKE '$term%'
551
  OR relevanssi.term_reverse LIKE CONCAT(REVERSE('$term), %')) $query_restrictions";
552
- // Clean: $query_restrictions is escaped, $term is escaped
553
- $query = apply_filters('relevanssi_df_query_filter', $query);
554
- $df = $wpdb->get_var($query);
 
555
  }
556
 
557
- $idf = log($D + 1 / (1 + $df));
558
- $idf = $idf * $idf;
559
- if ($idf < 1) $idf = 1;
560
- foreach ($matches as $match) {
561
- if ('user' == $match->type) {
 
 
562
  $match->doc = 'u_' . $match->item;
563
- }
564
- else if (!in_array($match->type, array('post', 'attachment'))) {
565
  $match->doc = '**' . $match->type . '**' . $match->item;
566
  }
567
 
568
- if (isset($match->taxonomy_detail)) {
569
- $match->taxonomy_score = 0;
570
- $match->taxonomy_detail = unserialize($match->taxonomy_detail);
571
- if (is_array($match->taxonomy_detail)) {
572
- foreach ($match->taxonomy_detail as $tax => $count) {
573
- if ($tax == 'post_tag') {
574
  $match->tag = $count;
575
  }
576
- if (empty($post_type_weights[$tax])) {
577
  $match->taxonomy_score += $count * 1;
578
- }
579
- else {
580
- $match->taxonomy_score += $count * $post_type_weights[$tax];
581
  }
582
  }
583
  }
@@ -594,7 +754,7 @@ function relevanssi_search($args) {
594
  $match->customfield +
595
  $match->mysqlcolumn;
596
 
597
- $term_hits[$match->doc][$term] =
598
  $match->title +
599
  $match->content +
600
  $match->comment +
@@ -609,900 +769,1263 @@ function relevanssi_search($args) {
609
 
610
  $match->weight = $match->tf * $idf;
611
 
612
- if ($recency_bonus) {
613
- $post = relevanssi_get_post($match->doc);
614
- if (strtotime($post->post_date) > $recency_cutoff_date)
615
  $match->weight = $match->weight * $recency_bonus['bonus'];
 
616
  }
617
 
618
- isset($body_matches[$match->doc]) ? $body_matches[$match->doc] += $match->content : $body_matches[$match->doc] = $match->content;
619
- isset($title_matches[$match->doc]) ? $title_matches[$match->doc] += $match->title : $title_matches[$match->doc] = $match->title;
620
- isset($link_matches[$match->doc]) ? $link_matches[$match->doc] += $match->link : $link_matches[$match->doc] = $match->link;
621
- isset($tag_matches[$match->doc]) ? $tag_matches[$match->doc] += $match->tag : $tag_matches[$match->doc] = $match->tag;
622
- isset($category_matches[$match->doc]) ? $category_matches[$match->doc] += $match->category : $category_matches[$match->doc] = $match->category;
623
- isset($taxonomy_matches[$match->doc]) ? $taxonomy_matches[$match->doc] += $match->taxonomy : $taxonomy_matches[$match->doc] = $match->taxonomy;
624
- isset($comment_matches[$match->doc]) ? $comment_matches[$match->doc] += $match->comment : $comment_matches[$match->doc] = $match->comment;
625
-
626
- isset($relevanssi_post_types[$match->doc]) ? $type = $relevanssi_post_types[$match->doc] : $type = null;
627
- if (!empty($post_type_weights[$type])) {
628
- $match->weight = $match->weight * $post_type_weights[$type];
629
  }
630
 
631
- $match = apply_filters('relevanssi_match', $match, $idf, $term);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
 
633
- if ($match->weight == 0) continue; // the filters killed the match
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
 
635
  $post_ok = true;
636
- $post_ok = apply_filters('relevanssi_post_ok', $post_ok, $match->doc);
637
-
638
- if ($post_ok) {
639
- $doc_terms[$match->doc][$term] = true; // count how many terms are matched to a doc
640
- isset($doc_weight[$match->doc]) ? $doc_weight[$match->doc] += $match->weight : $doc_weight[$match->doc] = $match->weight;
641
- isset($scores[$match->doc]) ? $scores[$match->doc] += $match->weight : $scores[$match->doc] = $match->weight;
642
- if (is_numeric($match->doc)) {
643
- // this is to weed out taxonomies and users (t_XXX, u_XXX)
644
- $include_these_posts[$match->doc] = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
  }
646
  }
647
  }
648
  }
649
 
650
- if (!isset($doc_weight)) {
651
  $doc_weight = array();
652
  $no_matches = true;
653
  }
654
- if ($no_matches) {
655
- if ($search_again) {
656
- // no hits even with fuzzy search!
657
  $search_again = false;
658
- }
659
- else {
660
- if ("sometimes" == $fuzzy) {
661
  $search_again = true;
662
- $o_term_cond = "(term LIKE '%#term#' OR term LIKE '#term#%') ";
663
  }
664
  }
665
- }
666
- else {
667
  $search_again = false;
668
  }
669
  $params = array(
670
- 'no_matches' => $no_matches,
671
- 'doc_weight' => $doc_weight,
672
- 'terms' => $terms,
673
- 'o_term_cond' => $o_term_cond,
674
  'search_again' => $search_again,
675
  );
676
- $params = apply_filters('relevanssi_search_again', $params);
 
 
 
 
 
 
 
 
 
677
  $search_again = $params['search_again'];
678
- $terms = $params['terms'];
679
- $o_term_cond = $params['o_term_cond'];
680
- $doc_weight = $params['doc_weight'];
681
- $no_matches = $params['no_matches'];
682
- } while ($search_again);
683
-
684
- $strip_stops = true;
685
- $temp_terms_without_stops = array_keys(relevanssi_tokenize(implode(' ', $terms), $strip_stops));
686
- $terms_without_stops = array();
687
- foreach ($temp_terms_without_stops as $temp_term) {
688
- if (strlen($temp_term) >= $min_length)
689
- array_push($terms_without_stops, $temp_term);
690
- }
691
- $total_terms = count($terms_without_stops);
692
-
693
- if (isset($doc_weight))
694
- $doc_weight = apply_filters('relevanssi_results', $doc_weight);
695
-
696
- if (isset($doc_weight) && count($doc_weight) > 0) {
697
- arsort($doc_weight);
 
 
 
 
 
 
 
 
 
 
698
  $i = 0;
699
- foreach ($doc_weight as $doc => $weight) {
700
- if (count($doc_terms[$doc]) < $total_terms && $operator == "AND") {
701
  // AND operator in action:
702
- // doc didn't match all terms, so it's discarded
703
  continue;
704
  }
705
 
706
- if (!empty($fields)) {
707
- if ($fields == 'ids') {
708
- $hits[intval($i)] = $doc;
709
  }
710
- if ($fields == 'id=>parent') {
711
- $object = new StdClass();
712
- $object->ID = $doc;
713
- $object->post_parent = wp_get_post_parent_id($doc);
714
 
715
- $hits[intval($i)] = $object;
716
  }
717
- }
718
- else {
719
- $hits[intval($i)] = relevanssi_get_post($doc);
720
- $hits[intval($i)]->relevance_score = round($weight, 2);
721
  }
722
  $i++;
723
  }
724
  }
725
 
726
- if (count($hits) < 1) {
727
- if ($operator == "AND" AND get_option('relevanssi_disable_or_fallback') != 'on') {
728
- $or_args = $args;
729
- $or_args['operator'] = "OR";
730
  global $wp_query;
731
- $wp_query->set("operator", "OR");
732
-
733
- $or_args['q'] = relevanssi_add_synonyms($q);
734
- $return = relevanssi_search($or_args);
735
- extract($return);
736
- }
737
- $params = array('args' => $args);
738
- $params = apply_filters('relevanssi_fallback', $params);
739
- $args = $params['args'];
740
- if (isset($params['return'])) {
741
- $return = $params['return'];
742
- extract($return);
743
- }
744
- }
745
-
746
- global $wp;
747
- $default_order = get_option('relevanssi_default_orderby', 'relevance');
748
- if (empty($orderby)) $orderby = $default_order;
749
- // the sorting function checks for non-existing keys, cannot whitelist here
750
-
751
- if (is_array($orderby)) {
752
- $orderby = apply_filters('relevanssi_orderby', $orderby);
753
-
754
- relevanssi_object_sort($hits, $orderby);
755
- }
756
- else {
757
- if (empty($order)) $order = 'desc';
758
- $order = strtolower($order);
759
- $order_accepted_values = array('asc', 'desc');
760
- if (!in_array($order, $order_accepted_values)) $order = 'desc';
761
-
762
- $orderby = apply_filters('relevanssi_orderby', $orderby);
763
- $order = apply_filters('relevanssi_order', $order);
764
-
765
- if ($orderby != 'relevance') {
766
- $orderby_array = array($orderby => $order);
767
- relevanssi_object_sort($hits, $orderby_array);
768
- }
769
- }
770
-
771
- $return = array('hits' => $hits, 'body_matches' => $body_matches, 'title_matches' => $title_matches,
772
- 'tag_matches' => $tag_matches, 'category_matches' => $category_matches, 'taxonomy_matches' => $taxonomy_matches,
773
- 'comment_matches' => $comment_matches, 'scores' => $scores,
774
- 'term_hits' => $term_hits, 'query' => $q, 'link_matches' => $link_matches);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775
 
776
  return $return;
777
  }
778
 
779
- function relevanssi_do_query(&$query) {
780
- // this all is basically lifted from Kenny Katzgrau's wpSearch
781
- // thanks, Kenny!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
782
  global $relevanssi_active;
783
-
784
  $relevanssi_active = true;
785
- $posts = array();
786
 
787
- if ( function_exists( 'mb_strtolower' ) )
788
- $q = trim(stripslashes(mb_strtolower($query->query_vars["s"])));
789
- else
790
- $q = trim(stripslashes(strtolower($query->query_vars["s"])));
791
 
792
- $search_multisite = false;
793
- if (isset($query->query_vars['searchblogs']) && (string) get_current_blog_id() != $query->query_vars['searchblogs']) {
794
- $search_multisite = true;
795
- }
796
 
797
- if (isset($query->query_vars['searchblogs']) && $search_multisite) {
798
- $multi_args['search_blogs'] = $query->query_vars['searchblogs'];
799
- $multi_args['q'] = $q;
 
 
 
800
 
801
- $post_type = false;
802
- if (isset($query->query_vars["post_type"]) && $query->query_vars["post_type"] != 'any') {
803
- $multi_args['post_type'] = $query->query_vars["post_type"];
 
804
  }
805
- if (isset($query->query_vars["post_types"]) && $query->query_vars["post_types"] != 'any') {
806
- $multi_args['post_type'] = $query->query_vars["post_types"];
 
807
  }
808
 
809
- if (isset($query->query_vars['order'])) $multi_args['order'] = $query->query_vars['order'];
810
- if (isset($query->query_vars['orderby'])) $multi_args['orderby'] = $query->query_vars['orderby'];
811
-
812
- $operator = "";
813
- if (function_exists('relevanssi_set_operator')) {
814
- $operator = relevanssi_set_operator($query);
815
- $operator = strtoupper($operator); // just in case
816
  }
817
- if ($operator != "OR" && $operator != "AND") $operator = get_option("relevanssi_implicit_operator");
818
- $multi_args['operator'] = $operator;
819
 
820
- $meta_query = array();
821
- if ( ! empty( $query->query_vars["meta_query"] ) ) {
822
- $meta_query = $query->query_vars["meta_query"];
823
- }
 
 
 
 
824
 
825
- if ( isset( $query->query_vars["customfield_key"] ) ) {
826
- $build_meta_query = array();
 
 
 
 
 
827
 
828
- // Use meta key
829
- $build_meta_query['key'] = $query->query_vars["customfield_key"];
 
 
 
 
830
 
831
- /**
832
- * Check the value is not empty for ordering purpose,
833
- * Set it or not for the current meta query
834
- */
835
- if ( ! empty( $query->query_vars["customfield_value"] ) ) {
836
- $build_meta_query['value'] = $query->query_vars["customfield_value"];
 
837
  }
 
838
 
839
- // Set the compare
840
- $build_meta_query['compare'] = '=';
 
 
841
 
842
- $meta_query[] = $build_meta_query;
843
- }
844
 
845
- if ( ! empty($query->query_vars["meta_key"] ) || ! empty($query->query_vars["meta_value"] ) || ! empty( $query->query_vars["meta_value_num"] ) ) {
 
846
 
847
- $build_meta_query = array();
 
 
 
 
 
 
848
 
849
- // Use meta key
850
- $build_meta_query['key'] = $query->query_vars["meta_key"];
851
 
852
- $value = null;
853
- if ( ! empty( $query->query_vars["meta_value"] ) ) {
854
- $value = $query->query_vars["meta_value"];
855
- } elseif ( ! empty( $query->query_vars["meta_value_num"] ) ) {
856
- $value = $query->query_vars["meta_value_num"];
857
  }
858
 
859
- /**
860
- * Check the meta value, as it could be not set for ordering purpose
861
- * set it or not for the current meta query
862
- */
863
- if ( ! empty( $value ) ) {
864
- $build_meta_query['value'] = $value;
865
- }
866
 
867
- // Set meta compare
868
- $build_meta_query['compare'] = ! empty( $query->query_vars["meta_compare"] ) ? $query->query_vars["meta_compare"] : '=';
869
 
870
- $meta_query[] = $build_meta_query;
871
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
872
 
873
- $multi_args['meta_query'] = $meta_query;
874
- if (function_exists('relevanssi_search_multi')) {
875
- $return = relevanssi_search_multi($multi_args);
 
 
 
 
 
876
  }
877
  }
878
- else {
879
  $tax_query = array();
880
- $tax_query_relation = apply_filters('relevanssi_default_tax_query_relation', 'OR');
881
- if (isset($query->tax_query) && empty($query->tax_query->queries)) {
 
 
 
 
 
882
  // Tax query is empty, let's get rid of it.
883
  $query->tax_query = null;
884
  }
885
- if (isset($query->query_vars['tax_query'])) {
886
- // This is user-created tax_query array as described in WP Codex
887
- foreach ($query->query_vars['tax_query'] as $type => $item) {
888
- if (is_string($type) && $type == 'relation') {
889
  $tax_query_relation = $item;
890
- }
891
- else {
892
  $tax_query[] = $item;
893
  }
894
  }
895
- }
896
- else if (isset($query->tax_query)) {
897
- // This is the WP-created Tax_Query object, which is different from above
898
- foreach ($query->tax_query as $type => $item) {
899
- if (is_string($type) && $type == 'relation') {
900
  $tax_query_relation = $item;
901
  }
902
- if (is_string($type) && $type == 'queries') {
903
- foreach ($item as $tax_query_row) {
904
  $tax_query[] = $tax_query_row;
905
  }
906
  }
907
  }
908
- }
909
- else {
910
  $cat = false;
911
- if (isset($query->query_vars["cats"])) {
912
- $cat = $query->query_vars["cats"];
913
- }
914
- if (empty($cat)) {
915
- $cat = get_option('relevanssi_cat');
916
- if (0 == $cat) {
917
- $cat = false;
918
- }
919
- }
920
- if ($cat) {
921
- $cat = explode(',', $cat);
922
- $tax_query[] = array('taxonomy' => 'category', 'field' => 'id', 'terms' => $cat);
923
- }
924
- if (!empty($query->query_vars['category_name']) && empty($query->query_vars['category__in'])) {
925
- $cat = explode(',', $query->query_vars['category_name']);
926
- $tax_query[] = array('taxonomy' => 'category', 'field' => 'slug', 'terms' => $cat);
927
- }
928
- if (!empty($query->query_vars['category__in'])) {
929
- $tax_query[] = array('taxonomy' => 'category', 'field' => 'id', 'terms' => $query->query_vars['category__in']);
930
- }
931
- if (!empty($query->query_vars['category__not_in'])) {
932
- $tax_query[] = array('taxonomy' => 'category', 'field' => 'id', 'terms' => $query->query_vars['category__not_in'], 'operator' => 'NOT IN');
933
- }
934
- if (!empty($query->query_vars['category__and'])) {
935
- $tax_query[] = array('taxonomy' => 'category', 'field' => 'id', 'terms' => $query->query_vars['category__and'], 'operator' => 'AND', 'include_children' => false);
936
- }
937
- $excat = get_option('relevanssi_excat');
938
- if (isset($excat) && $excat != 0) {
939
- $tax_query[] = array('taxonomy' => 'category', 'field' => 'id', 'terms' => $excat, 'operator' => 'NOT IN');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
940
  }
941
 
942
  $tag = false;
943
- if (isset($query->query_vars["tags"])) {
944
- $tag = $query->query_vars["tags"];
945
  }
946
- if ($tag) {
947
- if (strpos($tag, '+') !== false) {
948
- $tag = explode('+', $tag);
949
  $operator = 'and';
950
- }
951
- else {
952
- $tag = explode(',', $tag);
953
  $operator = 'or';
954
  }
955
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => $tag, 'operator' => $operator);
956
- }
957
- if (!empty($query->query_vars['tag_id'])) {
958
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => $query->query_vars['tag_id']);
959
- }
960
- if (!empty($query->query_vars['tag_id'])) {
961
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => $query->query_vars['tag_id']);
962
- }
963
- if (!empty($query->query_vars['tag__in'])) {
964
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => $query->query_vars['tag__in']);
965
- }
966
- if (!empty($query->query_vars['tag__not_in'])) {
967
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => $query->query_vars['tag__not_in'], 'operator' => 'NOT IN');
968
- }
969
- if (!empty($query->query_vars['tag__and'])) {
970
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => $query->query_vars['tag__and'], 'operator' => 'AND');
971
- }
972
- if (!empty($query->query_vars['tag__not_in'])) {
973
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => $query->query_vars['tag__not_in'], 'operator' => 'NOT IN');
974
- }
975
- if (!empty($query->query_vars['tag_slug__in'])) {
976
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'slug', 'terms' => $query->query_vars['tag_slug__in']);
977
- }
978
- if (!empty($query->query_vars['tag_slug__not_in'])) {
979
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'slug', 'terms' => $query->query_vars['tag_slug__not_in'], 'operator' => 'NOT IN');
980
- }
981
- if (!empty($query->query_vars['tag_slug__and'])) {
982
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'slug', 'terms' => $query->query_vars['tag_slug__and'], 'operator' => 'AND');
983
- }
984
- $extag = get_option('relevanssi_extag');
985
- if (isset($extag) && $extag != 0) {
986
- $tax_query[] = array('taxonomy' => 'post_tag', 'field' => 'id', 'terms' => $extag, 'operator' => 'NOT IN');
987
- }
988
-
989
- if (isset($query->query_vars["taxonomy"])) {
990
- if (function_exists('relevanssi_process_taxonomies')) {
991
- $tax_query = relevanssi_process_taxonomies($query->query_vars["taxonomy"], $query->query_vars["term"], $tax_query);
992
- }
993
- else {
994
- if (!empty($query->query_vars["term"])) $term = $query->query_vars["term"];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
995
 
996
- $tax_query[] = array('taxonomy' => $query->query_vars["taxonomy"], 'field' => 'slug', 'terms' => $term);
 
 
 
 
997
  }
998
  }
999
  }
1000
 
1001
  $author = false;
1002
- if (!empty($query->query_vars["author"])) {
1003
- $author = explode(',', $query->query_vars["author"]);
1004
  }
1005
- if (!empty($query->query_vars["author_name"])) {
1006
- $author_object = get_user_by('slug', $query->query_vars["author_name"]);
1007
- $author[] = $author_object->ID;
1008
  }
1009
 
1010
  $post_query = array();
1011
- if (!empty($query->query_vars['p'])) {
1012
- $post_query = array('in' => array($query->query_vars['p']));
1013
  }
1014
- if (!empty($query->query_vars['page_id'])) {
1015
- $post_query = array('in' => array($query->query_vars['page_id']));
1016
  }
1017
- if (!empty($query->query_vars['post__in'])) {
1018
- $post_query = array('in' => $query->query_vars['post__in']);
1019
  }
1020
- if (!empty($query->query_vars['post__not_in'])) {
1021
- $post_query = array('not in' => $query->query_vars['post__not_in']);
1022
  }
1023
 
1024
  $parent_query = array();
1025
- if (!empty($query->query_vars['post_parent'])) {
1026
- $parent_query = array('parent in' => array($query->query_vars['post_parent']));
1027
  }
1028
- if (!empty($query->query_vars['post_parent__in'])) {
1029
- $parent_query = array('parent in' => $query->query_vars['post_parent__in']);
1030
  }
1031
- if (!empty($query->query_vars['post_parent__not_in'])) {
1032
- $parent_query = array('parent not in' => $query->query_vars['post_parent__not_in']);
1033
  }
1034
 
1035
- $meta_query_relation = apply_filters('relevanssi_default_meta_query_relation', 'AND');
1036
- $meta_query = array();
1037
- if ( ! empty( $query->query_vars["meta_query"] ) ) {
1038
- $meta_query = $query->query_vars["meta_query"];
1039
- }
 
 
 
 
 
1040
 
1041
- if ( isset( $query->query_vars["customfield_key"] ) ) {
1042
  $build_meta_query = array();
1043
 
1044
- // Use meta key
1045
- $build_meta_query['key'] = $query->query_vars["customfield_key"];
1046
 
1047
  /**
1048
- * Check the value is not empty for ordering purpose,
1049
- * Set it or not for the current meta query
1050
- */
1051
- if ( ! empty( $query->query_vars["customfield_value"] ) ) {
1052
- $build_meta_query['value'] = $query->query_vars["customfield_value"];
1053
  }
1054
 
1055
- // Set the compare
1056
  $build_meta_query['compare'] = '=';
1057
- $meta_query[] = $build_meta_query;
1058
- }
1059
 
1060
- if ( ! empty($query->query_vars["meta_key"] ) || ! empty($query->query_vars["meta_value"] ) || ! empty( $query->query_vars["meta_value_num"] ) ) {
1061
  $build_meta_query = array();
1062
 
1063
- // Use meta key
1064
- $build_meta_query['key'] = $query->query_vars["meta_key"];
1065
 
1066
- $value = null;
1067
- if ( ! empty( $query->query_vars["meta_value"] ) ) {
1068
- $value = $query->query_vars["meta_value"];
1069
- } elseif ( ! empty( $query->query_vars["meta_value_num"] ) ) {
1070
- $value = $query->query_vars["meta_value_num"];
1071
  }
1072
 
1073
  /**
1074
- * Check the meta value, as it could be not set for ordering purpose
1075
- * set it or not for the current meta query
1076
  */
1077
  if ( ! empty( $value ) ) {
1078
  $build_meta_query['value'] = $value;
1079
  }
1080
 
1081
- // Set meta compare
1082
- $build_meta_query['compare'] = ! empty( $query->query_vars["meta_compare"] ) ? $query->query_vars["meta_compare"] : '=';
 
 
 
1083
 
1084
  $meta_query[] = $build_meta_query;
1085
- }
1086
 
1087
  $date_query = false;
1088
- if (!empty($query->date_query)) {
1089
- if (is_object($query->date_query) && get_class($query->date_query) == 'WP_Date_Query') { // there is no is_WP_Date_Query_Object() function
1090
  $date_query = $query->date_query;
1091
  } else {
1092
- $date_query = new WP_Date_Query($query->date_query);
1093
  }
1094
  }
1095
 
1096
  $search_blogs = false;
1097
- if (isset($query->query_vars["search_blogs"])) {
1098
- $search_blogs = $query->query_vars["search_blogs"];
1099
  }
1100
 
1101
  $post_type = false;
1102
- if (isset($query->query_vars["post_type"]) && $query->query_vars["post_type"] != 'any') {
1103
- $post_type = $query->query_vars["post_type"];
1104
  }
1105
- if (isset($query->query_vars["post_types"]) && $query->query_vars["post_types"] != 'any') {
1106
- $post_type = $query->query_vars["post_types"];
1107
  }
1108
 
1109
- if ($post_type == -1) $post_type = false;
1110
-
1111
  $post_status = false;
1112
- if (isset($query->query_vars["post_status"]) && $query->query_vars["post_status"] != 'any') {
1113
- $post_status = $query->query_vars["post_status"];
1114
  }
1115
 
1116
- $expost = get_option("relevanssi_exclude_posts");
1117
 
1118
- // In admin (and when not AJAX), search everything
1119
  if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
1120
- $excat = null;
1121
- $extag = null;
1122
  $expost = null;
1123
  }
1124
 
1125
  $sentence = false;
1126
- if (isset($query->query_vars['sentence']) && !empty($query->query_vars['sentence'])) {
1127
  $sentence = true;
1128
  }
1129
 
1130
- $operator = "";
1131
- if (function_exists('relevanssi_set_operator')) {
1132
- $operator = relevanssi_set_operator($query);
1133
- $operator = strtoupper($operator); // just in case
 
 
 
1134
  }
1135
- if ($operator != "OR" && $operator != "AND") $operator = get_option("relevanssi_implicit_operator");
1136
- $query->query_vars["operator"] = $operator;
1137
 
1138
- isset($query->query_vars['orderby']) ? $orderby = $query->query_vars['orderby'] : $orderby = null;
1139
- isset($query->query_vars['order']) ? $order = $query->query_vars['order'] : $order = null;
 
 
 
 
 
 
1140
 
1141
- $fields = "";
1142
- if (!empty($query->query_vars['fields'])) {
1143
- if ($query->query_vars['fields'] == 'ids') {
1144
  $fields = 'ids';
1145
  }
1146
- if ($query->query_vars['fields'] == 'id=>parent') {
1147
  $fields = 'id=>parent';
1148
  }
1149
  }
1150
 
1151
- // Add synonyms
1152
- // This is done here so the new terms will get highlighting
1153
- if ("OR" == $operator) {
1154
- // Synonyms are only used in OR queries
1155
- $q = relevanssi_add_synonyms($q);
 
 
 
 
 
 
 
1156
  }
1157
 
1158
  $search_params = array(
1159
- 'q' => $q,
1160
- 'tax_query' => $tax_query,
1161
  'tax_query_relation' => $tax_query_relation,
1162
- 'post_query' => $post_query,
1163
- 'parent_query' => $parent_query,
1164
- 'meta_query' => $meta_query,
1165
- 'date_query' => $date_query,
1166
- 'expost' => $expost,
1167
- 'post_type' => $post_type,
1168
- 'post_status' => $post_status,
1169
- 'operator' => $operator,
1170
- 'search_blogs' => $search_blogs,
1171
- 'author' => $author,
1172
- 'orderby' => $orderby,
1173
- 'order' => $order,
1174
- 'fields' => $fields,
1175
- 'sentence' => $sentence);
1176
-
1177
- $return = relevanssi_search($search_params);
1178
- }
1179
-
1180
- isset($return['hits']) ? $hits = $return['hits'] : $hits = array();
1181
- isset($return['query']) ? $q = $return['query'] : $q = "";
1182
-
1183
- $filter_data = array($hits, $q);
1184
- $hits_filters_applied = apply_filters('relevanssi_hits_filter', $filter_data);
1185
- $hits = array_values($hits_filters_applied[0]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1186
  // array_values() to make sure the $hits array is indexed in numerical order
1187
  // Manipulating the array with array_unique() for example may mess with that.
 
1188
 
1189
- $query->found_posts = sizeof($hits);
1190
- if (!isset($query->query_vars["posts_per_page"]) || $query->query_vars["posts_per_page"] == 0) {
1191
- // assume something sensible to prevent "division by zero error";
1192
- $query->query_vars["posts_per_page"] = -1;
1193
  }
1194
- if ($query->query_vars["posts_per_page"] == -1) {
1195
- $query->max_num_pages = sizeof($hits);
1196
- }
1197
- else {
1198
- $query->max_num_pages = ceil(sizeof($hits) / $query->query_vars["posts_per_page"]);
1199
  }
1200
 
1201
- $update_log = get_option('relevanssi_log_queries');
1202
- if ('on' == $update_log) {
1203
- relevanssi_update_log($q, sizeof($hits));
1204
  }
1205
 
1206
- $make_excerpts = get_option('relevanssi_excerpts');
1207
- if ($query->is_admin) $make_excerpts = false;
1208
-
1209
- if (isset($query->query_vars['paged']) && $query->query_vars['paged'] > 0) {
1210
- $wpSearch_low = ($query->query_vars['paged'] - 1) * $query->query_vars["posts_per_page"];
1211
- }
1212
- else {
1213
- $wpSearch_low = 0;
1214
  }
1215
 
1216
- if (!isset($query->query_vars["posts_per_page"]) || $query->query_vars["posts_per_page"] == -1) {
1217
- $wpSearch_high = sizeof($hits);
 
 
1218
  }
1219
- else {
1220
- $wpSearch_high = $wpSearch_low + $query->query_vars["posts_per_page"] - 1;
 
 
 
1221
  }
1222
 
1223
- if (isset($query->query_vars['offset']) && $query->query_vars['offset'] > 0) {
1224
- $wpSearch_high += $query->query_vars['offset'];
1225
- $wpSearch_low += $query->query_vars['offset'];
1226
  }
1227
 
1228
- if ($wpSearch_high > sizeof($hits)) $wpSearch_high = sizeof($hits);
 
 
1229
 
1230
- for ($i = $wpSearch_low; $i <= $wpSearch_high; $i++) {
1231
- if (isset($hits[intval($i)])) {
1232
- $post = $hits[intval($i)];
1233
- }
1234
- else {
1235
  continue;
1236
  }
1237
 
1238
- if ($post == NULL) {
1239
- // apparently sometimes you can get a null object
1240
  continue;
1241
  }
1242
 
1243
- //Added by OdditY - Highlight Result Title too ->
1244
- if("on" == get_option('relevanssi_hilite_title') && empty($fields)){
1245
- if (function_exists('qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage')) {
1246
- $post->post_highlighted_title = strip_tags(qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage($post->post_title));
1247
- }
1248
- else {
1249
- $post->post_highlighted_title = strip_tags($post->post_title);
1250
  }
1251
- $highlight = get_option('relevanssi_highlight');
1252
- if ("none" != $highlight) {
1253
- if (!is_admin()) {
1254
- $post->post_highlighted_title = relevanssi_highlight_terms($post->post_highlighted_title, $q);
1255
  }
1256
  }
1257
  }
1258
- // OdditY end <-
1259
 
1260
- if ('on' == $make_excerpts && empty($fields)) {
1261
  $post->original_excerpt = $post->post_excerpt;
1262
- $post->post_excerpt = relevanssi_do_excerpt($post, $q);
1263
  }
1264
 
1265
- if ('on' == get_option('relevanssi_show_matches') && empty($fields)) {
1266
  $post_id = $post->ID;
1267
- if ($post->post_type == 'user') {
1268
- $post_id = "u_" . $post->user_id;
1269
- }
1270
- else if (isset($post->term_id)) {
1271
  $post_id = '**' . $post->post_type . '**' . $post->term_id;
1272
  }
1273
- $post->post_excerpt .= relevanssi_show_matches($return, $post_id);
1274
  }
1275
 
1276
- if (empty($fields) && isset($return['scores'][$post->ID])) $post->relevance_score = round($return['scores'][$post->ID], 2);
 
 
1277
 
1278
  $posts[] = $post;
1279
  }
1280
 
1281
- $query->posts = $posts;
1282
- $query->post_count = count($posts);
1283
 
1284
  return $posts;
1285
  }
1286
 
1287
- function relevanssi_limit_filter($query) {
1288
- if (get_option('relevanssi_throttle', 'on') == 'on') {
1289
- $limit = get_option('relevanssi_throttle_limit', 500);
1290
- if (!is_numeric($limit)) $limit = 500; // Backup, if the option is set to something useless.
1291
- if ($limit < 0) $limit = 500;
 
 
 
 
 
 
 
 
 
 
 
1292
  return $query . " ORDER BY tf DESC LIMIT $limit";
1293
- }
1294
- else {
1295
  return $query;
1296
  }
1297
  }
1298
 
 
 
 
 
 
 
 
 
 
1299
  function relevanssi_get_negative_post_type() {
1300
- $negative_post_type = NULL;
1301
  global $wp_query;
1302
 
 
1303
  $negative_post_type_list = array();
1304
 
1305
- if (isset($wp_query->query_vars['include_attachments']) && in_array($wp_query->query_vars['include_attachments'], array("0", "off", "false"))) {
1306
- $negative_post_type_list[] = "attachment";
1307
  }
1308
 
1309
- if (get_option('relevanssi_respect_exclude') == 'on') {
1310
  // If Relevanssi is set to respect exclude_from_search, find out which
1311
  // post types should be excluded from search.
1312
- if (function_exists('get_post_types')) {
1313
- $pt_1 = get_post_types(array('exclude_from_search' => '1'));
1314
- $pt_2 = get_post_types(array('exclude_from_search' => true));
1315
- $negative_post_type_list = array_merge($negative_post_type_list, $pt_1, $pt_2);
1316
- }
1317
  }
1318
 
1319
  // Post types to exclude.
1320
- if (count($negative_post_type_list) > 0) {
1321
- $negative_post_types = esc_sql(array_unique($negative_post_type_list));
1322
- $negative_post_type = count($negative_post_types) ? "'" . implode( "', '", $negative_post_types) . "'" : NULL;
 
 
 
1323
  }
1324
 
1325
  return $negative_post_type;
1326
  }
1327
 
1328
- function relevanssi_process_tax_query_row($row, $is_sub_row, $global_relation, $query_restrictions, $tax_query_relation, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1329
  global $wpdb;
1330
 
1331
- $local_term_tax_ids = array();
1332
  $local_not_term_tax_ids = array();
1333
  $local_and_term_tax_ids = array();
1334
 
1335
  $using_term_tax_id = false;
1336
- if (!isset($row['field'])) $row['field'] = 'term_id'; // in case 'field' is not set, go with the WP default of "term_id"
1337
- if ($row['field'] == 'slug') {
1338
- $slug = $row['terms'];
 
 
1339
  $numeric_slugs = array();
1340
- $slug_in = null;
1341
- if (is_array($slug)) {
1342
- $slugs = array();
1343
  $term_id = array();
1344
- foreach ($slug as $t_slug) {
1345
- $term = get_term_by('slug', $t_slug, $row['taxonomy']);
1346
- if (!$term && is_numeric($t_slug)) {
1347
  $numeric_slugs[] = "'$t_slug'";
1348
- }
1349
- else {
1350
- if (isset($term->term_id)) {
1351
- $t_slug = sanitize_title($t_slug);
1352
  $term_id[] = $term->term_id;
1353
- $slugs[] = "'$t_slug'";
1354
  }
1355
  }
1356
  }
1357
- if (!empty($slugs)) $slug_in = implode(',', $slugs);
1358
- }
1359
- else {
1360
- $term = get_term_by('slug', $slug, $row['taxonomy'], OBJECT);
1361
- if (!$term && is_numeric($slug)) {
1362
- $numeric_slugs[] = $slug;
1363
  }
1364
- else {
1365
- if (isset($term->term_id)) {
1366
- $slug = sanitize_title($slug);
 
 
 
 
1367
  $term_id = $term->term_id;
1368
  $slug_in = "'$slug'";
1369
  }
1370
  }
1371
  }
1372
- if (!empty($slug_in)) {
1373
- $row_taxonomy = sanitize_text_field($row['taxonomy']);
 
1374
  $tt_q = "SELECT tt.term_taxonomy_id
1375
  FROM $wpdb->term_taxonomy AS tt
1376
  LEFT JOIN $wpdb->terms AS t ON (tt.term_id=t.term_id)
1377
  WHERE tt.taxonomy = '$row_taxonomy' AND t.slug IN ($slug_in)";
1378
- // Clean: $row_taxonomy is sanitized, each slug in $slug_in is sanitized
1379
- $term_tax_id = $wpdb->get_col($tt_q);
 
 
 
1380
  }
1381
- if (!empty($numeric_slugs)) $row['field'] = 'term_id';
1382
  }
1383
- if ($row['field'] == 'name') {
1384
- $name = $row['terms'];
1385
  $numeric_names = array();
1386
- $name_in = null;
1387
- if (is_array($name)) {
1388
- $names = array();
1389
  $term_id = array();
1390
- foreach ($name as $t_name) {
1391
- $term = get_term_by('name', $t_name, $row['taxonomy']);
1392
- if (!$term && is_numeric($t_names)) {
1393
  $numeric_names[] = "'$t_name'";
1394
- }
1395
- else {
1396
- if (isset($term->term_id)) {
1397
- $t_name = sanitize_title($t_name);
1398
  $term_id[] = $term->term_id;
1399
- $names[] = "'$t_name'";
1400
  }
1401
  }
1402
  }
1403
- if (!empty($names)) $name_in = implode(',', $names);
1404
- }
1405
- else {
1406
- $term = get_term_by('name', $name, $row['taxonomy']);
1407
- if (!$term && is_numeric($name)) {
1408
- $numeric_slugs[] = $name;
1409
  }
1410
- else {
1411
- if (isset($term->term_id)) {
1412
- $name = sanitize_title($name);
 
 
 
 
1413
  $term_id = $term->term_id;
1414
  $name_in = "'$name'";
1415
  }
1416
  }
1417
  }
1418
- if (!empty($name_in)) {
1419
- $row_taxonomy = sanitize_text_field($row['taxonomy']);
 
1420
  $tt_q = "SELECT tt.term_taxonomy_id
1421
  FROM $wpdb->term_taxonomy AS tt
1422
  LEFT JOIN $wpdb->terms AS t ON (tt.term_id=t.term_id)
1423
  WHERE tt.taxonomy = '$row_taxonomy' AND t.name IN ($name_in)";
1424
- // Clean: $row_taxonomy is sanitized, each name in $name_in is sanitized
1425
- $term_tax_id = $wpdb->get_col($tt_q);
 
 
 
1426
  }
1427
- if (!empty($numeric_names)) $row['field'] = 'term_id';
1428
  }
1429
- if ($row['field'] == 'id' || $row['field'] == 'term_id') {
1430
- $id = $row['terms'];
1431
  $term_id = $id;
1432
- if (is_array($id)) {
1433
  $numeric_values = array();
1434
- foreach ($id as $t_id) {
1435
- if (is_numeric($t_id)) $numeric_values[] = $t_id;
 
 
1436
  }
1437
- $id = implode(',', $numeric_values);
1438
- }
1439
- $row_taxonomy = sanitize_text_field($row['taxonomy']);
1440
- $tt_q = "SELECT tt.term_taxonomy_id
1441
- FROM $wpdb->term_taxonomy AS tt
1442
- LEFT JOIN $wpdb->terms AS t ON (tt.term_id=t.term_id)
1443
- WHERE tt.taxonomy = '$row_taxonomy' AND t.term_id IN ($id)";
1444
- // Clean: $row_taxonomy is sanitized, $id is checked to be numeric
1445
- $id_term_tax_id = $wpdb->get_col($tt_q);
1446
- if (!empty($term_tax_id) && is_array($term_tax_id)) {
1447
- $term_tax_id = array_unique(array_merge($term_tax_id, $id_term_tax_id));
1448
  }
1449
- else {
1450
- $term_tax_id = $id_term_tax_id;
 
 
 
 
 
 
 
 
 
 
 
 
1451
  }
1452
  }
1453
- if ($row['field'] == 'term_taxonomy_id') {
1454
  $using_term_tax_id = true;
1455
- $id = $row['terms'];
1456
- $term_tax_id = $id;
1457
- if (is_array($id)) {
1458
  $numeric_values = array();
1459
- foreach ($id as $t_id) {
1460
- if (is_numeric($t_id)) $numeric_values[] = $t_id;
 
 
1461
  }
1462
- $term_tax_id = implode(',', $numeric_values);
1463
  }
1464
  }
1465
 
1466
- if (!isset($row['include_children']) || $row['include_children'] == true) {
1467
- if (!$using_term_tax_id && isset($term_id)) {
1468
- if (!is_array($term_id)) {
1469
- $term_id = array($term_id);
1470
  }
1471
- }
1472
- else {
1473
- if (!is_array($term_tax_id)) {
1474
- $term_tax_id = array($term_tax_id);
1475
- $term_id = $term_tax_id;
1476
  }
1477
  }
1478
- if (empty($term_tax_id)) $term_tax_id = array();
1479
- if (!is_array($term_tax_id)) $term_tax_id = array($term_tax_id);
1480
- if (isset($term_id) && is_array($term_id)) {
1481
- foreach ($term_id as $t_id) {
1482
- if ($using_term_tax_id) {
1483
- $t_term = get_term_by('term_taxonomy_id', $t_id, $row['taxonomy']);
1484
- $t_id = $t_term->ID;
 
 
 
 
1485
  }
1486
- $kids = get_term_children($t_id, $row['taxonomy']);
1487
- foreach ($kids as $kid) {
1488
- $term = get_term_by('id', $kid, $row['taxonomy']);
1489
- $kid_term_tax_id = relevanssi_get_term_tax_id('id', $kid, $row['taxonomy']);
1490
- $term_tax_id[] = $kid_term_tax_id;
1491
  }
1492
  }
1493
  }
1494
  }
1495
 
1496
- $term_tax_id = array_unique($term_tax_id);
1497
- if (!empty($term_tax_id)) {
1498
- $n = count($term_tax_id);
1499
- $term_tax_id = implode(',', $term_tax_id);
1500
 
1501
- $tq_operator = 'IN'; // Assuming the default operator "IN", unless something else is provided.
1502
- if (isset($row['operator'])) $tq_operator = strtoupper($row['operator']);
1503
- if ($tq_operator != 'IN' && $tq_operator != 'NOT IN' && $tq_operator != 'AND') $tq_operator = 'IN';
1504
- if ($tax_query_relation == 'and') {
1505
- if ($tq_operator == 'AND') {
 
 
 
 
1506
  $query_restrictions .= " AND relevanssi.doc IN (
1507
  SELECT ID FROM $wpdb->posts WHERE 1=1
1508
  AND (
@@ -1511,45 +2034,48 @@ function relevanssi_process_tax_query_row($row, $is_sub_row, $global_relation, $
1511
  WHERE tr.term_taxonomy_id IN ($term_tax_id)
1512
  AND tr.object_id = $wpdb->posts.ID ) = $n
1513
  )";
1514
- // Clean: $term_tax_id and $n are Relevanssi-generated
1515
- }
1516
- else {
1517
  $query_restrictions .= " AND relevanssi.doc $tq_operator (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr
1518
  WHERE tr.term_taxonomy_id IN ($term_tax_id))";
1519
- // Clean: all variables are Relevanssi-generated
 
 
 
 
 
 
 
 
 
 
1520
  }
1521
  }
1522
- else {
1523
- if ($tq_operator === 'IN') $local_term_tax_ids[] = $term_tax_id;
1524
- if ($tq_operator === 'NOT IN') $local_not_term_tax_ids[] = $term_tax_id;
1525
- if ($tq_operator === 'AND') $local_and_term_tax_ids[] = $term_tax_id;
1526
- }
1527
- }
1528
- else {
1529
  global $wp_query;
1530
  $wp_query->is_category = false;
1531
  }
1532
-
1533
- if ($is_sub_row && $global_relation === 'and' && $tax_query_relation === 'or') {
1534
- $local_term_tax_ids = array_unique($local_term_tax_ids);
1535
- $local_not_term_tax_ids = array_unique($local_not_term_tax_ids);
1536
- $local_and_term_tax_ids = array_unique($local_and_term_tax_ids);
1537
- if (count($local_term_tax_ids) > 0) {
1538
- $local_term_tax_ids = implode(',', $local_term_tax_ids);
1539
  $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr
1540
  WHERE tr.term_taxonomy_id IN ($local_term_tax_ids))";
1541
- // Clean: all variables are Relevanssi-generated
1542
  }
1543
- if (count($local_not_term_tax_ids) > 0) {
1544
- $local_not_term_tax_ids = implode(',', $local_not_term_tax_ids);
1545
- $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr
1546
  WHERE tr.term_taxonomy_id IN ($local_not_term_tax_ids))";
1547
- // Clean: all variables are Relevanssi-generated
1548
  }
1549
- if (count($local_and_term_tax_ids) > 0) {
1550
- $local_and_term_tax_ids = implode(',', $local_and_term_tax_ids);
1551
- $n = count(explode(',', $local_and_term_tax_ids));
1552
- $query_restrictions .= " AND relevanssi.doc IN (
1553
  SELECT ID FROM $wpdb->posts WHERE 1=1
1554
  AND (
1555
  SELECT COUNT(1)
@@ -1557,19 +2083,23 @@ function relevanssi_process_tax_query_row($row, $is_sub_row, $global_relation, $
1557
  WHERE tr.term_taxonomy_id IN ($local_and_term_tax_ids)
1558
  AND tr.object_id = $wpdb->posts.ID ) = $n
1559
  )";
1560
- // Clean: all variables are Relevanssi-generated
1561
  }
1562
  }
1563
-
1564
  $copy_term_tax_ids = false;
1565
- if (!$is_sub_row) $copy_term_tax_ids = true;
1566
- if ($is_sub_row && $global_relation === 'or') $copy_term_tax_ids = true;
 
 
 
 
1567
 
1568
- if ($copy_term_tax_ids) {
1569
- $term_tax_ids = array_merge($term_tax_ids, $local_term_tax_ids);
1570
- $not_term_tax_ids = array_merge($not_term_tax_ids, $local_not_term_tax_ids);
1571
- $and_term_tax_ids = array_merge($and_term_tax_ids, $local_and_term_tax_ids);
1572
  }
1573
 
1574
- return array($query_restrictions, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids);
1575
- }
1
  <?php
2
+ /**
3
+ * /lib/search.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
+
11
+ /**
12
+ * Triggers the Relevanssi search query.
13
+ *
14
+ * Attaches to 'the_posts' filter hook, checks to see if there's a place for a
15
+ * search and runs relevanssi_do_query() if there is. Do not call directly; for
16
+ * direct Relevanssi access, use relevanssi_do_query().
17
+ *
18
+ * @global boolean $relevanssi_active True, if Relevanssi is already running.
19
+ *
20
+ * @param array $posts An array of post objects.
21
+ * @param WP_Query $query The WP_Query object, default false.
22
+ */
23
+ function relevanssi_query( $posts, $query = false ) {
24
+ $admin_search_option = get_option( 'relevanssi_admin_search' );
25
+ $admin_search = false;
26
+ if ( 'on' === $admin_search_option ) {
27
+ $admin_search = true;
28
+ }
29
 
30
  global $relevanssi_active;
31
 
32
+ if ( ! $query ) {
33
+ return $posts;
34
+ }
35
 
36
+ $search_ok = true; // We will search!
37
+ if ( ! $query->is_search() ) {
38
+ $search_ok = false; // No, we can't, not a search.
39
  }
40
+ if ( ! $query->is_main_query() ) {
41
+ $search_ok = false; // No, we can't, not the main query.
42
  }
43
 
44
  // Uses $wp_query->is_admin instead of is_admin() to help with Ajax queries that
45
  // use 'admin_ajax' hook (which sets is_admin() to true whether it's an admin search
46
  // or not.
47
+ if ( $query->is_search() && $query->is_admin ) {
48
+ $search_ok = false; // But if this is an admin search, reconsider.
49
+ if ( $admin_search ) {
50
+ $search_ok = true; // Yes, we can search!
51
+ }
52
  }
53
 
54
+ if ( $query->is_admin && empty( $query->query_vars['s'] ) ) {
55
+ $search_ok = false; // No search term.
56
  }
57
 
58
+ // Disable Relevanssi in the media library search.
59
+ if ( $search_ok ) {
60
+ if ( 'attachment' === $query->query_vars['post_type'] && 'inherit,private' === $query->query_vars['post_status'] ) {
61
  $search_ok = false;
62
  }
63
  }
64
 
65
+ /**
66
+ * Filters whether Relevanssi search can be run or not.
67
+ *
68
+ * This can be used to for example activate Relevanssi in cases where there is
69
+ * no search term available.
70
+ *
71
+ * @param boolean True, if Relevanssi can be allowed to run.
72
+ * @param WP_Query The current query object.
73
+ */
74
+ $search_ok = apply_filters( 'relevanssi_search_ok', $search_ok, $query );
75
 
76
+ if ( $relevanssi_active ) {
77
+ $search_ok = false; // Relevanssi is already in action.
78
  }
79
 
80
+ if ( $search_ok ) {
81
+ /**
82
+ * Filters the WP_Query object before Relevanssi.
83
+ *
84
+ * Can be used to modify the WP_Query object before Relevanssi sees it.
85
+ * Fairly close to pre_get_posts, but is often the better idea, because this
86
+ * only affects Relevanssi searches, and nothing else. Do note that this is
87
+ * a filter and needs to return the modified query object.
88
+ *
89
+ * @param WP_Query The WP_Query object.
90
+ */
91
+ $query = apply_filters( 'relevanssi_modify_wp_query', $query );
92
+ $posts = relevanssi_do_query( $query );
93
  }
94
+
95
  return $posts;
96
  }
97
 
98
+ /**
99
+ * Does the actual searching.
100
+ *
101
+ * This function gets the search arguments, finds posts and returns all the results
102
+ * it finds. If you wish to access Relevanssi directly, use relevanssi_do_query(),
103
+ * which takes a WP_Query object as a parameter, formats the arguments nicely and
104
+ * returns a specified subset of posts. This is for internal use.
105
+ *
106
+ * @global object $wpdb The WordPress database interface.
107
+ * @global array $relevanssi_variables The global Relevanssi variables array.
108
+ * @global WP_Query $wp_query The WP_Query object.
109
+ * @global array $relevanssi_post_types Cache array for post type values.
110
+ *
111
+ * @param array $args Array of arguments.
112
+ *
113
+ * @return array An array of return values.
114
+ */
115
+ function relevanssi_search( $args ) {
116
  global $wpdb, $relevanssi_variables;
117
  $relevanssi_table = $relevanssi_variables['relevanssi_table'];
118
 
119
+ /**
120
+ * Filters the search parameters.
121
+ *
122
+ * @param array The search parameters.
123
+ */
124
+ $filtered_args = apply_filters( 'relevanssi_search_filters', $args );
125
+ $q = $filtered_args['q'];
126
+ $tax_query = $filtered_args['tax_query'];
127
  $tax_query_relation = $filtered_args['tax_query_relation'];
128
+ $post_query = $filtered_args['post_query'];
129
+ $parent_query = $filtered_args['parent_query'];
130
+ $meta_query = $filtered_args['meta_query'];
131
+ $date_query = $filtered_args['date_query'];
132
+ $expost = $filtered_args['expost'];
133
+ $post_type = $filtered_args['post_type'];
134
+ $post_status = $filtered_args['post_status'];
135
+ $operator = $filtered_args['operator'];
136
+ $search_blogs = $filtered_args['search_blogs'];
137
+ $author = $filtered_args['author'];
138
+ $orderby = $filtered_args['orderby'];
139
+ $order = $filtered_args['order'];
140
+ $fields = $filtered_args['fields'];
141
+ $sentence = $filtered_args['sentence'];
142
+ $by_date = $filtered_args['by_date'];
143
+
144
+ $hits = array();
145
+ $query_restrictions = '';
146
+
147
+ if ( ! isset( $tax_query_relation ) ) {
148
+ $tax_query_relation = 'or';
149
+ }
150
+ $tax_query_relation = relevanssi_strtolower( $tax_query_relation );
151
+ $term_tax_id = array();
152
+ $term_tax_ids = array();
153
+ $not_term_tax_ids = array();
154
+ $and_term_tax_ids = array();
155
+
156
+ if ( is_array( $tax_query ) ) {
157
  $is_sub_row = false;
158
+ foreach ( $tax_query as $row ) {
159
+ if ( isset( $row['terms'] ) ) {
160
+ list( $query_restrictions, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids ) =
161
+ relevanssi_process_tax_query_row( $row, $is_sub_row, $tax_query_relation, $query_restrictions, $tax_query_relation, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids );
162
+ } else {
163
  $row_tax_query_relation = $tax_query_relation;
164
+ if ( isset( $row['relation'] ) ) {
165
+ $row_tax_query_relation = relevanssi_strtolower( $row['relation'] );
166
+ }
167
+ foreach ( $row as $subrow ) {
168
  $is_sub_row = true;
169
+ if ( isset( $subrow['terms'] ) ) {
170
+ list( $query_restrictions, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids ) =
171
+ relevanssi_process_tax_query_row( $subrow, $is_sub_row, $tax_query_relation, $query_restrictions, $tax_query_relation, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids );
172
  }
173
  }
174
  }
175
  }
176
 
177
+ if ( 'or' === $tax_query_relation ) {
178
+ $term_tax_ids = array_unique( $term_tax_ids );
179
+ if ( count( $term_tax_ids ) > 0 ) {
180
+ $term_tax_ids = implode( ',', $term_tax_ids );
181
+ $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr WHERE tr.term_taxonomy_id IN ($term_tax_ids))";
182
+ // Clean: all variables are Relevanssi-generated.
 
183
  }
184
+ if ( count( $not_term_tax_ids ) > 0 ) {
185
+ $not_term_tax_ids = implode( ',', $not_term_tax_ids );
186
+ $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr WHERE tr.term_taxonomy_id IN ($not_term_tax_ids))";
187
+ // Clean: all variables are Relevanssi-generated.
 
188
  }
189
+ if ( count( $and_term_tax_ids ) > 0 ) {
190
+ $and_term_tax_ids = implode( ',', $and_term_tax_ids );
191
+ $n = count( explode( ',', $and_term_tax_ids ) );
192
  $query_restrictions .= " AND relevanssi.doc IN (
193
  SELECT ID FROM $wpdb->posts WHERE 1=1
194
  AND (
197
  WHERE tr.term_taxonomy_id IN ($and_term_tax_ids)
198
  AND tr.object_id = $wpdb->posts.ID ) = $n
199
  )";
200
+ // Clean: all variables are Relevanssi-generated.
201
  }
202
  }
203
  }
204
 
205
+ if ( is_array( $post_query ) ) {
206
+ if ( ! empty( $post_query['in'] ) ) {
207
  $valid_values = array();
208
+ foreach ( $post_query['in'] as $post_in_id ) {
209
+ if ( is_numeric( $post_in_id ) ) {
210
+ $valid_values[] = $post_in_id;
211
+ }
212
+ }
213
+ $posts = implode( ',', $valid_values );
214
+ if ( ! empty( $posts ) ) {
215
+ $query_restrictions .= " AND relevanssi.doc IN ($posts)";
216
+ // Clean: $posts is checked to be integers.
217
  }
 
 
 
218
  }
219
+ if ( ! empty( $post_query['not in'] ) ) {
220
  $valid_values = array();
221
+ foreach ( $post_query['not in'] as $post_not_in_id ) {
222
+ if ( is_numeric( $post_not_in_id ) ) {
223
+ $valid_values[] = $post_not_in_id;
224
+ }
225
+ }
226
+ $posts = implode( ',', $valid_values );
227
+ if ( ! empty( $posts ) ) {
228
+ $query_restrictions .= " AND relevanssi.doc NOT IN ($posts)";
229
+ // Clean: $posts is checked to be integers.
230
  }
 
 
 
231
  }
232
  }
233
 
234
+ if ( is_array( $parent_query ) ) {
235
+ if ( ! empty( $parent_query['parent in'] ) ) {
236
  $valid_values = array();
237
+ foreach ( $parent_query['parent in'] as $post_in_id ) {
238
+ if ( is_numeric( $post_in_id ) ) {
239
+ $valid_values[] = $post_in_id;
240
+ }
241
+ }
242
+ $posts = implode( ',', $valid_values );
243
+ if ( ! empty( $posts ) ) {
244
+ $query_restrictions .= " AND relevanssi.doc IN (SELECT ID FROM $wpdb->posts WHERE post_parent IN ($posts))";
245
+ // Clean: $posts is checked to be integers.
246
  }
 
 
 
247
  }
248
+ if ( ! empty( $parent_query['parent not in'] ) ) {
249
  $valid_values = array();
250
+ foreach ( $parent_query['parent not in'] as $post_not_in_id ) {
251
+ if ( is_numeric( $post_not_in_id ) ) {
252
+ $valid_values[] = $post_not_in_id;
253
+ }
254
+ }
255
+ $posts = implode( ',', $valid_values );
256
+ if ( ! empty( $posts ) ) {
257
+ $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT ID FROM $wpdb->posts WHERE post_parent IN ($posts))";
258
+ // Clean: $posts is checked to be integers.
259
  }
 
 
 
260
  }
261
  }
262
 
263
+ if ( is_array( $meta_query ) ) {
264
+ $meta_query_restrictions = '';
265
 
266
+ $mq_vars = array( 'meta_query' => $meta_query );
267
 
268
  $mq = new WP_Meta_Query();
269
  $mq->parse_query_vars( $mq_vars );
270
+ $meta_sql = $mq->get_sql( 'post', 'relevanssi', 'doc' );
271
+ $meta_join = '';
272
+ $meta_where = '';
273
+ if ( $meta_sql ) {
274
+ $meta_join = $meta_sql['join'];
275
  $meta_where = $meta_sql['where'];
276
  }
277
 
278
  $query_restrictions .= $meta_where;
279
  }
280
 
281
+ if ( ! empty( $date_query ) ) {
282
+ if ( is_object( $date_query ) && method_exists( $date_query, 'get_sql' ) ) {
283
+ $sql = $date_query->get_sql(); // AND ( the query itself ).
284
  $query_restrictions .= " AND relevanssi.doc IN ( SELECT DISTINCT(ID) FROM $wpdb->posts WHERE 1 $sql )";
285
+ // Clean: $sql generated by $date_query->get_sql() query.
286
  }
287
  }
288
 
289
  // If $post_type is not set, see if there are post types to exclude from the search.
290
  // If $post_type is set, there's no need to exclude, as we only include.
291
+ $negative_post_type = null;
292
+ if ( ! $post_type ) {
293
+ $negative_post_type = relevanssi_get_negative_post_type();
294
+ }
295
 
296
+ $non_post_post_type = null;
297
  $non_post_post_types_array = array();
298
+ if ( function_exists( 'relevanssi_get_non_post_post_types' ) ) {
299
+ // Relevanssi Premium includes post types which are not actually posts.
300
  $non_post_post_types_array = relevanssi_get_non_post_post_types();
301
  }
302
 
303
+ if ( $post_type ) {
304
+ if ( ! is_array( $post_type ) ) {
305
+ $post_types = explode( ',', $post_type );
306
+ } else {
 
 
307
  $post_types = $post_type;
308
  }
309
+
310
  // This array will contain all regular post types involved in the search parameters.
311
+ $post_post_types = array_diff( $post_types, $non_post_post_types_array );
312
 
313
  // This array has the non-post post types involved.
314
+ $non_post_post_types = array_intersect( $post_types, $non_post_post_types_array );
315
 
316
  // Escape both for SQL queries, just in case.
317
+ $non_post_post_types = esc_sql( $non_post_post_types );
318
+ $post_types = esc_sql( $post_post_types );
319
 
320
+ // Implode to a parameter string, or set to null if empty.
321
+ $non_post_post_type = null;
322
+ if ( count( $non_post_post_types ) > 0 ) {
323
+ $non_post_post_type = "'" . implode( "', '", $non_post_post_types ) . "'";
324
+ }
325
+ $post_type = null;
326
+ if ( count( $post_types ) > 0 ) {
327
+ $post_type = "'" . implode( "', '", $post_types ) . "'";
328
+ }
329
  }
330
 
331
+ if ( $post_status ) {
332
+ if ( ! is_array( $post_status ) ) {
333
+ $post_statuses = esc_sql( explode( ',', $post_status ) );
334
+ } else {
335
+ $post_statuses = esc_sql( $post_status );
 
336
  }
337
 
338
+ $post_status = null;
339
+ if ( count( $post_statuses ) > 0 ) {
340
+ $post_status = "'" . implode( "', '", $post_statuses ) . "'";
341
+ }
342
  }
343
 
344
+ $posts_to_exclude = '';
345
+ if ( ! empty( $expost ) ) {
346
+ $excluded_post_ids = explode( ',', $expost );
347
+ foreach ( $excluded_post_ids as $excluded_post_id ) {
348
+ $exid = intval( trim( $excluded_post_id, ' -' ) );
349
+ $posts_to_exclude .= " AND relevanssi.doc != $excluded_post_id";
350
+ // Clean: escaped.
 
 
 
 
351
  }
352
+ $query_restrictions .= $posts_to_exclude;
353
  }
 
354
 
355
+ if ( function_exists( 'wp_encode_emoji' ) ) {
356
+ $q = wp_encode_emoji( $q );
357
  }
358
 
359
+ if ( $sentence ) {
360
+ $q = str_replace( '"', '', $q );
 
 
 
361
  $q = '"' . $q . '"';
362
  }
363
 
364
+ $phrases = relevanssi_recognize_phrases( $q );
365
 
366
+ if ( function_exists( 'relevanssi_recognize_negatives' ) ) {
367
+ // Relevanssi Premium supports negative minus operator.
368
+ $negative_terms = relevanssi_recognize_negatives( $q );
369
+ } else {
370
  $negative_terms = false;
371
  }
372
 
373
+ if ( function_exists( 'relevanssi_recognize_positives' ) ) {
374
+ // Relevanssi Premium supports a plus operator.
375
+ $positive_terms = relevanssi_recognize_positives( $q );
376
+ } else {
377
  $positive_terms = false;
378
  }
 
 
379
 
380
+ /**
381
+ * Filters whether stopwords are removed from titles.
382
+ *
383
+ * @param boolean If true, remove stopwords from titles.
384
+ */
385
+ $remove_stopwords = apply_filters( 'relevanssi_remove_stopwords_in_titles', true );
386
+
387
+ $terms = relevanssi_tokenize( $q, $remove_stopwords );
388
+
389
+ if ( count( $terms ) < 1 ) {
390
  // Tokenizer killed all the search terms.
391
  return $hits;
392
  }
393
+ $terms = array_keys( $terms ); // Don't care about tf in query.
394
 
395
+ if ( $negative_terms ) {
396
+ $terms = array_diff( $terms, $negative_terms );
397
+ }
 
 
 
398
 
399
+ // Go get the count from the options, but run the full query if it's not available.
400
+ $doc_count = get_option( 'relevanssi_doc_count' );
401
+ if ( ! $doc_count || $doc_count < 1 ) {
402
+ $doc_count = $wpdb->get_var( "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi" ); // WPCS: unprepared SQL ok, Relevanssi table name.
403
+ update_option( 'relevanssi_doc_count', $doc_count );
 
404
  }
405
 
406
  $total_hits = 0;
407
 
408
+ $title_matches = array();
409
+ $tag_matches = array();
410
+ $comment_matches = array();
411
+ $link_matches = array();
412
+ $body_matches = array();
413
  $category_matches = array();
414
  $taxonomy_matches = array();
415
+ $scores = array();
416
+ $term_hits = array();
417
 
418
+ $fuzzy = get_option( 'relevanssi_fuzzy' );
419
 
420
+ if ( function_exists( 'relevanssi_negatives_positives' ) ) {
421
+ $query_restrictions .= relevanssi_negatives_positives( $negative_terms, $positive_terms, $relevanssi_table );
422
+ // Clean: escaped in the function.
423
  }
424
 
425
+ if ( ! empty( $author ) ) {
426
+ $author_in = array();
427
  $author_not_in = array();
428
+ foreach ( $author as $id ) {
429
+ if ( ! is_numeric( $id ) ) {
430
+ continue;
 
431
  }
432
+ if ( $id > 0 ) {
433
+ $author_in[] = $id;
434
+ } else {
435
+ $author_not_in[] = abs( $id );
436
  }
437
  }
438
+ if ( count( $author_in ) > 0 ) {
439
+ $authors = implode( ',', $author_in );
440
  $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
441
  WHERE posts.post_author IN ($authors))";
442
+ // Clean: $authors is always just numbers.
443
  }
444
+ if ( count( $author_not_in ) > 0 ) {
445
+ $authors = implode( ',', $author_not_in );
446
  $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
447
  WHERE posts.post_author IN ($authors))";
448
+ // Clean: $authors is always just numbers.
449
  }
450
  }
451
 
452
+ if ( $post_type ) {
453
+ // A post type is set: add a restriction.
454
  $restriction = " AND (
455
  relevanssi.doc IN (
456
  SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
457
  WHERE posts.post_type IN ($post_type)
458
  ) *np*
459
+ )"; // Clean: $post_type is escaped.
 
460
 
461
  // There are post types involved that are taxonomies or users, so can't
462
  // match to wp_posts. Add a relevanssi.type restriction.
463
+ if ( $non_post_post_type ) {
464
+ $restriction = str_replace( '*np*', "OR (relevanssi.type IN ($non_post_post_type))", $restriction );
465
+ // Clean: $non_post_post_types is escaped.
466
  } else {
467
  // No non-post post types, so remove the placeholder.
468
+ $restriction = str_replace( '*np*', '', $restriction );
469
  }
470
  $query_restrictions .= $restriction;
471
+ } else {
472
+ // No regular post types.
473
+ if ( $non_post_post_type ) {
 
474
  // But there is a non-post post type restriction.
475
  $query_restrictions .= " AND (relevanssi.type IN ($non_post_post_type))";
476
+ // Clean: $non_post_post_types is escaped.
477
  }
478
  }
479
 
480
+ if ( $negative_post_type ) {
481
  $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
482
  WHERE posts.post_type NOT IN ($negative_post_type))) OR (doc = -1))";
483
+ // Clean: $negative_post_type is escaped.
484
  }
485
 
486
+ if ( $post_status ) {
487
  global $wp_query;
488
+ if ( $wp_query->is_admin ) {
489
  $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
490
  WHERE posts.post_status IN ($post_status))))";
491
+ } else {
492
+ // The -1 is there to get user profiles and category pages.
 
493
  $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
494
  WHERE posts.post_status IN ($post_status))) OR (doc = -1))";
495
  }
496
+ // Clean: $post_status is escaped.
497
  }
498
 
499
+ if ( $phrases ) {
500
  $query_restrictions .= " $phrases";
501
+ // Clean: $phrases is escaped earlier.
502
  }
503
 
504
+ if ( isset( $by_date ) ) {
505
+ $n = $by_date;
506
 
507
+ $u = substr( $n, -1, 1 );
508
+ switch ( $u ) {
509
  case 'h':
510
+ $unit = 'HOUR';
511
  break;
512
  case 'd':
513
+ $unit = 'DAY';
514
  break;
515
  case 'm':
516
+ $unit = 'MONTH';
517
  break;
518
  case 'y':
519
+ $unit = 'YEAR';
520
  break;
521
  case 'w':
522
+ $unit = 'WEEK';
523
  break;
524
  default:
525
+ $unit = 'DAY';
526
  }
527
 
528
+ $n = preg_replace( '/[hdmyw]/', '', $n );
529
 
530
+ if ( is_numeric( $n ) ) {
531
  $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM $wpdb->posts AS posts
532
  WHERE posts.post_date > DATE_SUB(NOW(), INTERVAL $n $unit))";
533
+ // Clean: $n is always numeric, $unit is Relevanssi-generated.
534
+ }
535
+ }
536
+
537
+ /**
538
+ * Filters the query restrictions for the Relevanssi query.
539
+ *
540
+ * Equivalent to the 'posts_where' filter.
541
+ *
542
+ * @author Charles St-Pierre
543
+ *
544
+ * @param string The MySQL code that restricts the query.
545
+ */
546
+ $query_restrictions = apply_filters( 'relevanssi_where', $query_restrictions );
547
+ $query_join = '';
548
+ if ( ! empty( $meta_join ) ) {
549
+ $query_join = $meta_join;
550
+ }
551
+ /**
552
+ * Filters the meta query JOIN for the Relevanssi search query.
553
+ *
554
+ * Somewhat equivalent to the 'posts_join' filter.
555
+ *
556
+ * @param string The JOINed query.
557
+ */
558
+ $query_join = apply_filters( 'relevanssi_join', $query_join );
559
 
560
  $no_matches = true;
561
+ if ( 'always' === $fuzzy ) {
562
+ /**
563
+ * Filters the partial matching search query.
564
+ *
565
+ * By default partial matching matches the beginnings and the ends of the
566
+ * words. If you want it to match inside words, add a function to this
567
+ * hook that returns '(relevanssi.term LIKE '%#term#%')'.
568
+ *
569
+ * @param string The partial matching query.
570
+ */
571
+ $o_term_cond = apply_filters( 'relevanssi_fuzzy_query', "(relevanssi.term LIKE '#term#%' OR relevanssi.term_reverse LIKE CONCAT(REVERSE('#term#'), '%')) " );
572
+ } else {
573
  $o_term_cond = " relevanssi.term = '#term#' ";
574
  }
575
 
576
+ if ( count( $terms ) < 1 ) {
577
+ $o_term_cond = ' relevanssi.term = relevanssi.term ';
578
+ $terms[] = 'term';
579
  }
580
 
581
+ $post_type_weights = get_option( 'relevanssi_post_type_weights' );
582
+
583
+ $recency_bonus = false;
584
+ $recency_cutoff_date = false;
585
+ if ( function_exists( 'relevanssi_get_recency_bonus' ) ) {
586
+ list( $recency_bonus, $recency_cutoff_date ) = relevanssi_get_recency_bonus();
587
  }
588
+
589
+ $exact_match_bonus = false;
590
+ if ( 'on' === get_option( 'relevanssi_exact_match_bonus' ) ) {
591
+ $exact_match_bonus = true;
592
+ /**
593
+ * Filters the exact match bonus.
594
+ *
595
+ * @param array The title bonus under 'title' (default 5) and the content
596
+ * bonus under 'content' (default 2).
597
+ */
598
+ $exact_match_boost = apply_filters( 'relevanssi_exact_match_bonus', array(
599
+ 'title' => 5,
600
+ 'content' => 2,
601
+ ));
602
+
603
  }
604
+
605
+ $min_length = get_option( 'relevanssi_min_word_length' );
606
+
607
  $search_again = false;
608
+
609
+ $content_boost = floatval( get_option( 'relevanssi_content_boost', 1 ) ); // Default value, because this option was added late.
610
+ $title_boost = floatval( get_option( 'relevanssi_title_boost' ) );
611
+ $link_boost = floatval( get_option( 'relevanssi_link_boost' ) );
612
+ $comment_boost = floatval( get_option( 'relevanssi_comment_boost' ) );
 
 
613
 
614
  $include_these_posts = array();
615
 
616
  do {
617
+ foreach ( $terms as $term ) {
618
+ $term = trim( $term ); // Numeric search terms will start with a space.
619
+ /**
620
+ * Allows the use of one letter search terms.
621
+ *
622
+ * Return false to allow one letter searches.
623
+ *
624
+ * @param boolean True, if search term is one letter long and will be blocked.
625
+ */
626
+ if ( apply_filters( 'relevanssi_block_one_letter_searches', relevanssi_strlen( $term ) < 2 ) ) {
627
+ continue;
628
+ }
629
+ $term = esc_sql( $term );
630
 
631
+ if ( false !== strpos( $o_term_cond, 'LIKE' ) ) {
632
+ $term = $wpdb->esc_like( $term );
633
  }
634
 
635
+ $term_cond = str_replace( '#term#', $term, $o_term_cond );
636
 
637
+ $tag = $relevanssi_variables['post_type_weight_defaults']['post_tag'];
638
+ $cat = $relevanssi_variables['post_type_weight_defaults']['category'];
639
+ if ( ! empty( $post_type_weights['post_tag'] ) ) {
640
+ $tag = $post_type_weights['post_tag'];
641
+ }
642
+ if ( ! empty( $post_type_weights['category'] ) ) {
643
+ $cat = $post_type_weights['category'];
644
+ }
645
 
646
  $query = "SELECT DISTINCT(relevanssi.doc), relevanssi.*, relevanssi.title * $title_boost +
647
  relevanssi.content * $content_boost + relevanssi.comment * $comment_boost +
649
  relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
650
  relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
651
  FROM $relevanssi_table AS relevanssi $query_join WHERE $term_cond $query_restrictions";
652
+ /** Clean: $query_restrictions is escaped, $term_cond is escaped. */
653
 
654
+ /**
655
+ * Filters the Relevanssi MySQL query.
656
+ *
657
+ * The last chance to filter the MySQL query before it is run.
658
+ *
659
+ * @param string MySQL query for the Relevanssi search.
660
+ */
661
+ $query = apply_filters( 'relevanssi_query_filter', $query );
662
+ $matches = $wpdb->get_results( $query ); // WPCS: unprepared SQL ok, the query is thoroughly escaped.
663
+
664
+ if ( count( $matches ) < 1 ) {
665
  continue;
666
+ } else {
 
667
  $no_matches = false;
668
+ if ( count( $include_these_posts ) > 0 ) {
669
+ $post_ids_to_add = implode( ',', array_keys( $include_these_posts ) );
670
+ $existing_ids = array();
671
+ foreach ( $matches as $match ) {
672
  $existing_ids[] = $match->doc;
673
  }
674
+ $existing_ids = implode( ',', $existing_ids );
675
+ $query = "SELECT relevanssi.*, relevanssi.title * $title_boost +
676
  relevanssi.content + relevanssi.comment * $comment_boost +
677
  relevanssi.tag * $tag + relevanssi.link * $link_boost +
678
  relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
679
  relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
680
  FROM $relevanssi_table AS relevanssi WHERE relevanssi.doc IN ($post_ids_to_add)
681
  AND relevanssi.doc NOT IN ($existing_ids) AND $term_cond";
682
+ // Clean: no unescaped user inputs.
683
+ $matches_to_add = $wpdb->get_results( $query ); // WPCS: unprepared SQL ok.
684
+ $matches = array_merge( $matches, $matches_to_add );
685
  }
686
  }
687
 
688
+ relevanssi_populate_array( $matches );
689
  global $relevanssi_post_types;
690
 
691
+ $total_hits += count( $matches );
692
 
693
  $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi
694
  $query_join WHERE $term_cond $query_restrictions";
695
+ // Clean: $query_restrictions is escaped, $term_cond is escaped.
696
+ /**
697
+ * Filters the DF query.
698
+ *
699
+ * This query is used to calculate the df for the tf * idf calculations.
700
+ *
701
+ * @param string MySQL query to filter.
702
+ */
703
+ $query = apply_filters( 'relevanssi_df_query_filter', $query );
704
 
705
+ $df = $wpdb->get_var( $query ); // WPCS: unprepared SQL ok.
706
 
707
+ if ( $df < 1 && 'sometimes' === $fuzzy ) {
708
  $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi
709
  $query_join WHERE (relevanssi.term LIKE '$term%'
710
  OR relevanssi.term_reverse LIKE CONCAT(REVERSE('$term), %')) $query_restrictions";
711
+ // Clean: $query_restrictions is escaped, $term is escaped.
712
+ /** Documented in lib/search.php. */
713
+ $query = apply_filters( 'relevanssi_df_query_filter', $query );
714
+ $df = $wpdb->get_var( $query ); // WPCS: unprepared SQL ok.
715
  }
716
 
717
+ $idf = log( $doc_count + 1 / ( 1 + $df ) );
718
+ $idf = $idf * $idf; // Adjustment to increase the value of IDF.
719
+ if ( $idf < 1 ) {
720
+ $idf = 1;
721
+ }
722
+ foreach ( $matches as $match ) {
723
+ if ( 'user' === $match->type ) {
724
  $match->doc = 'u_' . $match->item;
725
+ } elseif ( ! in_array( $match->type, array( 'post', 'attachment' ), true ) ) {
 
726
  $match->doc = '**' . $match->type . '**' . $match->item;
727
  }
728
 
729
+ if ( isset( $match->taxonomy_detail ) ) {
730
+ $match->taxonomy_score = 0;
731
+ $match->taxonomy_detail = json_decode( $match->taxonomy_detail );
732
+ if ( is_object( $match->taxonomy_detail ) ) {
733
+ foreach ( $match->taxonomy_detail as $tax => $count ) {
734
+ if ( 'post_tag' === $tax ) {
735
  $match->tag = $count;
736
  }
737
+ if ( empty( $post_type_weights[ $tax ] ) ) {
738
  $match->taxonomy_score += $count * 1;
739
+ } else {
740
+ $match->taxonomy_score += $count * $post_type_weights[ $tax ];
 
741
  }
742
  }
743
  }
754
  $match->customfield +
755
  $match->mysqlcolumn;
756
 
757
+ $term_hits[ $match->doc ][ $term ] =
758
  $match->title +
759
  $match->content +
760
  $match->comment +
769
 
770
  $match->weight = $match->tf * $idf;
771
 
772
+ if ( $recency_bonus ) {
773
+ $post = relevanssi_get_post( $match->doc );
774
+ if ( strtotime( $post->post_date ) > $recency_cutoff_date ) {
775
  $match->weight = $match->weight * $recency_bonus['bonus'];
776
+ }
777
  }
778
 
779
+ if ( $exact_match_bonus ) {
780
+ $post = relevanssi_get_post( $match->doc );
781
+ $clean_q = str_replace( '"', '', $q );
782
+ if ( stristr( $post->post_title, $clean_q ) !== false ) {
783
+ $match->weight *= $exact_match_boost['title'];
784
+ }
785
+ if ( stristr( $post->post_content, $clean_q ) !== false ) {
786
+ $match->weight *= $exact_match_boost['content'];
787
+ }
 
 
788
  }
789
 
790
+ if ( ! isset( $body_matches[ $match->doc ] ) ) {
791
+ $body_matches[ $match->doc ] = 0;
792
+ }
793
+ if ( ! isset( $title_matches[ $match->doc ] ) ) {
794
+ $title_matches[ $match->doc ] = 0;
795
+ }
796
+ if ( ! isset( $link_matches[ $match->doc ] ) ) {
797
+ $link_matches[ $match->doc ] = 0;
798
+ }
799
+ if ( ! isset( $tag_matches[ $match->doc ] ) ) {
800
+ $tag_matches[ $match->doc ] = 0;
801
+ }
802
+ if ( ! isset( $category_matches[ $match->doc ] ) ) {
803
+ $category_matches[ $match->doc ] = 0;
804
+ }
805
+ if ( ! isset( $taxonomy_matches[ $match->doc ] ) ) {
806
+ $taxonomy_matches[ $match->doc ] = 0;
807
+ }
808
+ if ( ! isset( $comment_matches[ $match->doc ] ) ) {
809
+ $comment_matches[ $match->doc ] = 0;
810
+ }
811
+ $body_matches[ $match->doc ] += $match->content;
812
+ $title_matches[ $match->doc ] += $match->title;
813
+ $link_matches[ $match->doc ] += $match->link;
814
+ $tag_matches[ $match->doc ] += $match->tag;
815
+ $category_matches[ $match->doc ] += $match->category;
816
+ $taxonomy_matches[ $match->doc ] += $match->taxonomy;
817
+ $comment_matches[ $match->doc ] += $match->comment;
818
+
819
+ $type = null;
820
+ if ( isset( $relevanssi_post_types[ $match->doc ] ) ) {
821
+ $type = $relevanssi_post_types[ $match->doc ];
822
+ }
823
+ if ( ! empty( $post_type_weights[ $type ] ) ) {
824
+ $match->weight = $match->weight * $post_type_weights[ $type ];
825
+ }
826
 
827
+ /**
828
+ * Filters the hit.
829
+ *
830
+ * This filter hook can be used to adjust the weights of found hits.
831
+ * Calculate the new weight and set the $match->weight to the new
832
+ * value.
833
+ *
834
+ * @param object $match The match object.
835
+ * @param int $idf The IDF value, if you want to recalculate
836
+ * TF * IDF values (TF is in $match->tf).
837
+ * @param string $term The current search term.
838
+ */
839
+ $match = apply_filters( 'relevanssi_match', $match, $idf, $term );
840
+ if ( $match->weight <= 0 ) {
841
+ continue; // The filters killed the match.
842
+ }
843
 
844
  $post_ok = true;
845
+ /**
846
+ * Filters whether the post can be shown to the user.
847
+ *
848
+ * This filter hook is used for 'relevanssi_default_post_ok' filter
849
+ * function which handles private posts and some membership plugins.
850
+ * If you want to add support for more membership plugins, this is
851
+ * the filter hook to use.
852
+ *
853
+ * @param boolean True, if the post can be shown to the current user.
854
+ * @param int The post ID.
855
+ */
856
+ $post_ok = apply_filters( 'relevanssi_post_ok', $post_ok, $match->doc );
857
+
858
+ if ( $post_ok ) {
859
+ $doc_terms[ $match->doc ][ $term ] = true; // Count how many terms are matched to a doc.
860
+ if ( ! isset( $doc_weight[ $match->doc ] ) ) {
861
+ $doc_weight[ $match->doc ] = 0;
862
+ }
863
+ $doc_weight[ $match->doc ] += $match->weight;
864
+ if ( ! isset( $scores[ $match->doc ] ) ) {
865
+ $scores[ $match->doc ] = 0;
866
+ }
867
+ $scores[ $match->doc ] += $match->weight;
868
+ if ( is_numeric( $match->doc ) ) {
869
+ // This is to weed out taxonomies and users (t_XXX, u_XXX).
870
+ $include_these_posts[ $match->doc ] = true;
871
  }
872
  }
873
  }
874
  }
875
 
876
+ if ( ! isset( $doc_weight ) ) {
877
  $doc_weight = array();
878
  $no_matches = true;
879
  }
880
+ if ( $no_matches ) {
881
+ if ( $search_again ) {
882
+ // No hits even with fuzzy search!
883
  $search_again = false;
884
+ } else {
885
+ if ( 'sometimes' === $fuzzy ) {
 
886
  $search_again = true;
887
+ $o_term_cond = "(term LIKE '%#term#' OR term LIKE '#term#%') ";
888
  }
889
  }
890
+ } else {
 
891
  $search_again = false;
892
  }
893
  $params = array(
894
+ 'no_matches' => $no_matches,
895
+ 'doc_weight' => $doc_weight,
896
+ 'terms' => $terms,
897
+ 'o_term_cond' => $o_term_cond,
898
  'search_again' => $search_again,
899
  );
900
+ /**
901
+ * Filters the parameters for fallback search.
902
+ *
903
+ * If you want to make Relevanssi search again with different parameters, you
904
+ * can use this filter hook to adjust the parameters. Set
905
+ * $params['search_again'] to true to make Relevanssi do a new search.
906
+ *
907
+ * @param array The search parameters.
908
+ */
909
+ $params = apply_filters( 'relevanssi_search_again', $params );
910
  $search_again = $params['search_again'];
911
+ $terms = $params['terms'];
912
+ $o_term_cond = $params['o_term_cond'];
913
+ $doc_weight = $params['doc_weight'];
914
+ $no_matches = $params['no_matches'];
915
+ } while ( $search_again );
916
+
917
+ $strip_stops = true;
918
+ $temp_terms_without_stops = array_keys( relevanssi_tokenize( implode( ' ', $terms ), $strip_stops ) );
919
+ $terms_without_stops = array();
920
+ foreach ( $temp_terms_without_stops as $temp_term ) {
921
+ if ( relevanssi_strlen( $temp_term ) >= $min_length ) {
922
+ array_push( $terms_without_stops, $temp_term );
923
+ }
924
+ }
925
+ $total_terms = count( $terms_without_stops );
926
+
927
+ if ( isset( $doc_weight ) ) {
928
+ /**
929
+ * Filters the results Relevanssi finds.
930
+ *
931
+ * Often you'll find 'relevanssi_hits_filter' more useful than this, but
932
+ * sometimes this is the right tool for filtering the results.
933
+ *
934
+ * @param array $doc_weight An array of (post ID, weight) pairs.
935
+ */
936
+ $doc_weight = apply_filters( 'relevanssi_results', $doc_weight );
937
+ }
938
+
939
+ if ( isset( $doc_weight ) && count( $doc_weight ) > 0 ) {
940
+ arsort( $doc_weight );
941
  $i = 0;
942
+ foreach ( $doc_weight as $doc => $weight ) {
943
+ if ( count( $doc_terms[ $doc ] ) < $total_terms && 'AND' === $operator ) {
944
  // AND operator in action:
945
+ // doc didn't match all terms, so it's discarded.
946
  continue;
947
  }
948
 
949
+ if ( ! empty( $fields ) ) {
950
+ if ( 'ids' === $fields ) {
951
+ $hits[ intval( $i ) ] = $doc;
952
  }
953
+ if ( 'id=>parent' === $fields ) {
954
+ $object = new StdClass();
955
+ $object->ID = $doc;
956
+ $object->post_parent = wp_get_post_parent_id( $doc );
957
 
958
+ $hits[ intval( $i ) ] = $object;
959
  }
960
+ } else {
961
+ $hits[ intval( $i ) ] = relevanssi_get_post( $doc );
962
+ $hits[ intval( $i ) ]->relevance_score = round( $weight, 2 );
 
963
  }
964
  $i++;
965
  }
966
  }
967
 
968
+ if ( count( $hits ) < 1 ) {
969
+ if ( 'AND' === $operator && 'on' !== get_option( 'relevanssi_disable_or_fallback' ) ) {
970
+ $or_args = $args;
971
+ $or_args['operator'] = 'OR';
972
  global $wp_query;
973
+ $wp_query->set( 'operator', 'OR' );
974
+
975
+ $or_args['q'] = relevanssi_add_synonyms( $q );
976
+ $return = relevanssi_search( $or_args );
977
+
978
+ $hits = $return['hits'];
979
+ $body_matches = $return['body_matches'];
980
+ $title_matches = $return['title_matches'];
981
+ $tag_matches = $return['tag_matches'];
982
+ $category_matches = $return['category_matches'];
983
+ $taxonomy_matches = $return['taxonomy_matches'];
984
+ $comment_matches = $return['comment_matches'];
985
+ $body_matches = $return['body_matches'];
986
+ $link_matches = $return['link_matches'];
987
+ $term_hits = $return['term_hits'];
988
+ $q = $return['query'];
989
+ }
990
+ $params = array( 'args' => $args );
991
+ /**
992
+ * Filters the fallback search parameters.
993
+ *
994
+ * This filter can be used to implement a fallback search. Take the
995
+ * parameters, do something with them, then return a proper return value
996
+ * array in $param['return'].
997
+ *
998
+ * @param array Search parameters.
999
+ */
1000
+ $params = apply_filters( 'relevanssi_fallback', $params );
1001
+ $args = $params['args'];
1002
+ if ( isset( $params['return'] ) ) {
1003
+ $return = $params['return'];
1004
+ $hits = $return['hits'];
1005
+ $body_matches = $return['body_matches'];
1006
+ $title_matches = $return['title_matches'];
1007
+ $tag_matches = $return['tag_matches'];
1008
+ $category_matches = $return['category_matches'];
1009
+ $taxonomy_matches = $return['taxonomy_matches'];
1010
+ $comment_matches = $return['comment_matches'];
1011
+ $body_matches = $return['body_matches'];
1012
+ $link_matches = $return['link_matches'];
1013
+ $term_hits = $return['term_hits'];
1014
+ $q = $return['query'];
1015
+ }
1016
+ }
1017
+
1018
+ $default_order = get_option( 'relevanssi_default_orderby', 'relevance' );
1019
+ if ( empty( $orderby ) ) {
1020
+ $orderby = $default_order;
1021
+ }
1022
+
1023
+ if ( is_array( $orderby ) ) {
1024
+ /**
1025
+ * Filters the 'orderby' value just before sorting.
1026
+ *
1027
+ * Relevanssi can use both array orderby ie. array( orderby => order ) with
1028
+ * multiple orderby parameters, or a single pair of orderby and order
1029
+ * parameters. To avoid problems, try sticking to one and don't use this
1030
+ * filter to make surprising changes between different formats.
1031
+ *
1032
+ * @param string The 'orderby' parameter.
1033
+ */
1034
+ $orderby = apply_filters( 'relevanssi_orderby', $orderby );
1035
+ relevanssi_object_sort( $hits, $orderby );
1036
+ } else {
1037
+ if ( empty( $order ) ) {
1038
+ $order = 'desc';
1039
+ }
1040
+ $order = strtolower( $order );
1041
+
1042
+ $order_accepted_values = array( 'asc', 'desc' );
1043
+ if ( ! in_array( $order, $order_accepted_values, true ) ) {
1044
+ $order = 'desc';
1045
+ }
1046
+
1047
+ /** Documented in lib/search.php. */
1048
+ $orderby = apply_filters( 'relevanssi_orderby', $orderby );
1049
+ /**
1050
+ * Filters the 'order' value just before sorting.
1051
+ *
1052
+ * @param string The 'order' parameter.
1053
+ */
1054
+ $order = apply_filters( 'relevanssi_order', $order );
1055
+
1056
+ if ( 'relevance' !== $orderby ) {
1057
+ $orderby_array = array( $orderby => $order );
1058
+ relevanssi_object_sort( $hits, $orderby_array );
1059
+ }
1060
+ }
1061
+ $return = array(
1062
+ 'hits' => $hits,
1063
+ 'body_matches' => $body_matches,
1064
+ 'title_matches' => $title_matches,
1065
+ 'tag_matches' => $tag_matches,
1066
+ 'category_matches' => $category_matches,
1067
+ 'taxonomy_matches' => $taxonomy_matches,
1068
+ 'comment_matches' => $comment_matches,
1069
+ 'scores' => $scores,
1070
+ 'term_hits' => $term_hits,
1071
+ 'query' => $q,
1072
+ 'link_matches' => $link_matches,
1073
+ );
1074
 
1075
  return $return;
1076
  }
1077
 
1078
+ /**
1079
+ * Takes a WP_Query object and runs the search query based on that
1080
+ *
1081
+ * This function can be used to run Relevanssi searches anywhere. Just create an
1082
+ * empty WP_Query object, give it some parameters, make sure 's' is set and contains
1083
+ * the search query, then run relevanssi_do_query() on the query object.
1084
+ *
1085
+ * This function is strongly influenced by Kenny Katzgrau's wpSearch plugin.
1086
+ *
1087
+ * @global boolean $relevanssi_active If true, Relevanssi is currently doing a
1088
+ * search.
1089
+ *
1090
+ * @param WP_Query $query A WP_Query object, passed as a reference. Relevanssi will
1091
+ * put the posts found in $query->posts, and also sets $query->post_count.
1092
+ *
1093
+ * @return array The found posts, an array of post objects.
1094
+ */
1095
+ function relevanssi_do_query( &$query ) {
1096
  global $relevanssi_active;
 
1097
  $relevanssi_active = true;
 
1098
 
1099
+ $posts = array();
 
 
 
1100
 
1101
+ $q = trim( stripslashes( relevanssi_strtolower( $query->query_vars['s'] ) ) );
 
 
 
1102
 
1103
+ $did_multisite_search = false;
1104
+ if ( is_multisite() ) {
1105
+ $search_multisite = false;
1106
+ if ( isset( $query->query_vars['searchblogs'] ) && (string) get_current_blog_id() !== $query->query_vars['searchblogs'] ) {
1107
+ $search_multisite = true;
1108
+ }
1109
 
1110
+ // Is searching all blogs enabled?
1111
+ $searchblogs_all = get_option( 'relevanssi_searchblogs_all', 'off' );
1112
+ if ( 'off' === $searchblogs_all ) {
1113
+ $searchblogs_all = false;
1114
  }
1115
+ if ( ! $search_multisite && $searchblogs_all ) {
1116
+ $search_multisite = true;
1117
+ $searchblogs = 'all';
1118
  }
1119
 
1120
+ // Searchblogs is not set from the query variables, check the option.
1121
+ $searchblogs_setting = get_option( 'relevanssi_searchblogs' );
1122
+ if ( ! $search_multisite && $searchblogs_setting ) {
1123
+ $search_multisite = true;
1124
+ $searchblogs = $searchblogs_setting;
 
 
1125
  }
 
 
1126
 
1127
+ if ( $search_multisite ) {
1128
+ $multi_args = array();
1129
+ if ( isset( $query->query_vars['searchblogs'] ) ) {
1130
+ $multi_args['search_blogs'] = $query->query_vars['searchblogs'];
1131
+ } else {
1132
+ $multi_args['search_blogs'] = $searchblogs;
1133
+ }
1134
+ $multi_args['q'] = $q;
1135
 
1136
+ $post_type = false;
1137
+ if ( isset( $query->query_vars['post_type'] ) && 'any' !== $query->query_vars['post_type'] ) {
1138
+ $multi_args['post_type'] = $query->query_vars['post_type'];
1139
+ }
1140
+ if ( isset( $query->query_vars['post_types'] ) && 'any' !== $query->query_vars['post_types'] ) {
1141
+ $multi_args['post_type'] = $query->query_vars['post_types'];
1142
+ }
1143
 
1144
+ if ( isset( $query->query_vars['order'] ) ) {
1145
+ $multi_args['order'] = $query->query_vars['order'];
1146
+ }
1147
+ if ( isset( $query->query_vars['orderby'] ) ) {
1148
+ $multi_args['orderby'] = $query->query_vars['orderby'];
1149
+ }
1150
 
1151
+ $operator = '';
1152
+ if ( function_exists( 'relevanssi_set_operator' ) ) {
1153
+ $operator = relevanssi_set_operator( $query );
1154
+ $operator = strtoupper( $operator ); // Just in case.
1155
+ }
1156
+ if ( 'OR' !== $operator && 'AND' !== $operator ) {
1157
+ $operator = get_option( 'relevanssi_implicit_operator' );
1158
  }
1159
+ $multi_args['operator'] = $operator;
1160
 
1161
+ $meta_query = array();
1162
+ if ( ! empty( $query->query_vars['meta_query'] ) ) {
1163
+ $meta_query = $query->query_vars['meta_query'];
1164
+ }
1165
 
1166
+ if ( isset( $query->query_vars['customfield_key'] ) ) {
1167
+ $build_meta_query = array();
1168
 
1169
+ // Use meta key.
1170
+ $build_meta_query['key'] = $query->query_vars['customfield_key'];
1171
 
1172
+ /**
1173
+ * Check the value is not empty for ordering purpose,
1174
+ * Set it or not for the current meta query
1175
+ */
1176
+ if ( ! empty( $query->query_vars['customfield_value'] ) ) {
1177
+ $build_meta_query['value'] = $query->query_vars['customfield_value'];
1178
+ }
1179
 
1180
+ // Set the compare.
1181
+ $build_meta_query['compare'] = '=';
1182
 
1183
+ $meta_query[] = $build_meta_query;
 
 
 
 
1184
  }
1185
 
1186
+ if ( ! empty( $query->query_vars['meta_key'] ) || ! empty( $query->query_vars['meta_value'] ) || ! empty( $query->query_vars['meta_value_num'] ) ) {
1187
+ $build_meta_query = array();
 
 
 
 
 
1188
 
1189
+ // Use meta key.
1190
+ $build_meta_query['key'] = $query->query_vars['meta_key'];
1191
 
1192
+ $value = null;
1193
+ if ( ! empty( $query->query_vars['meta_value'] ) ) {
1194
+ $value = $query->query_vars['meta_value'];
1195
+ } elseif ( ! empty( $query->query_vars['meta_value_num'] ) ) {
1196
+ $value = $query->query_vars['meta_value_num'];
1197
+ }
1198
+
1199
+ /**
1200
+ * Check the meta value, as it could be not set for ordering purpose
1201
+ * set it or not for the current meta query.
1202
+ */
1203
+ if ( ! empty( $value ) ) {
1204
+ $build_meta_query['value'] = $value;
1205
+ }
1206
+
1207
+ // Set meta compare.
1208
+ $build_meta_query['compare'] = '=';
1209
+ if ( ! empty( $query->query_vars['meta_compare'] ) ) {
1210
+ $query->query_vars['meta_compare'];
1211
+ }
1212
 
1213
+ $meta_query[] = $build_meta_query;
1214
+ }
1215
+
1216
+ $multi_args['meta_query'] = $meta_query;
1217
+ if ( function_exists( 'relevanssi_search_multi' ) ) {
1218
+ $return = relevanssi_search_multi( $multi_args );
1219
+ }
1220
+ $did_multisite_search = true;
1221
  }
1222
  }
1223
+ if ( ! $did_multisite_search ) {
1224
  $tax_query = array();
1225
+ /**
1226
+ * Filters the default tax_query relation.
1227
+ *
1228
+ * @param string The default relation, default 'OR'.
1229
+ */
1230
+ $tax_query_relation = apply_filters( 'relevanssi_default_tax_query_relation', 'OR' );
1231
+ if ( isset( $query->tax_query ) && empty( $query->tax_query->queries ) ) {
1232
  // Tax query is empty, let's get rid of it.
1233
  $query->tax_query = null;
1234
  }
1235
+ if ( isset( $query->query_vars['tax_query'] ) ) {
1236
+ // This is user-created tax_query array as described in WP Codex.
1237
+ foreach ( $query->query_vars['tax_query'] as $type => $item ) {
1238
+ if ( is_string( $type ) && 'relation' === $type ) {
1239
  $tax_query_relation = $item;
1240
+ } else {
 
1241
  $tax_query[] = $item;
1242
  }
1243
  }
1244
+ } elseif ( isset( $query->tax_query ) ) {
1245
+ // This is the WP-created Tax_Query object, which is different from above.
1246
+ foreach ( $query->tax_query as $type => $item ) {
1247
+ if ( is_string( $type ) && 'relation' === $type ) {
 
1248
  $tax_query_relation = $item;
1249
  }
1250
+ if ( is_string( $type ) && 'queries' === $type ) {
1251
+ foreach ( $item as $tax_query_row ) {
1252
  $tax_query[] = $tax_query_row;
1253
  }
1254
  }
1255
  }
1256
+ } else {
 
1257
  $cat = false;
1258
+ if ( isset( $query->query_vars['cats'] ) ) {
1259
+ $cat = $query->query_vars['cats'];
1260
+ }
1261
+ if ( empty( $cat ) ) {
1262
+ $cat = get_option( 'relevanssi_cat' );
1263
+ }
1264
+ if ( $cat ) {
1265
+ $cat = explode( ',', $cat );
1266
+ $tax_query[] = array(
1267
+ 'taxonomy' => 'category',
1268
+ 'field' => 'id',
1269
+ 'terms' => $cat,
1270
+ );
1271
+ }
1272
+ if ( ! empty( $query->query_vars['category_name'] ) && empty( $query->query_vars['category__in'] ) ) {
1273
+ $cat = explode( ',', $query->query_vars['category_name'] );
1274
+ $tax_query[] = array(
1275
+ 'taxonomy' => 'category',
1276
+ 'field' => 'slug',
1277
+ 'terms' => $cat,
1278
+ );
1279
+ }
1280
+ if ( ! empty( $query->query_vars['category__in'] ) ) {
1281
+ $tax_query[] = array(
1282
+ 'taxonomy' => 'category',
1283
+ 'field' => 'id',
1284
+ 'terms' => $query->query_vars['category__in'],
1285
+ );
1286
+ }
1287
+ if ( ! empty( $query->query_vars['category__not_in'] ) ) {
1288
+ $tax_query[] = array(
1289
+ 'taxonomy' => 'category',
1290
+ 'field' => 'id',
1291
+ 'terms' => $query->query_vars['category__not_in'],
1292
+ 'operator' => 'NOT IN',
1293
+ );
1294
+ }
1295
+ if ( ! empty( $query->query_vars['category__and'] ) ) {
1296
+ $tax_query[] = array(
1297
+ 'taxonomy' => 'category',
1298
+ 'field' => 'id',
1299
+ 'terms' => $query->query_vars['category__and'],
1300
+ 'operator' => 'AND',
1301
+ 'include_children' => false,
1302
+ );
1303
+ }
1304
+ $excat = get_option( 'relevanssi_excat' );
1305
+ if ( ! empty( $excat ) ) {
1306
+ $tax_query[] = array(
1307
+ 'taxonomy' => 'category',
1308
+ 'field' => 'id',
1309
+ 'terms' => $excat,
1310
+ 'operator' => 'NOT IN',
1311
+ );
1312
  }
1313
 
1314
  $tag = false;
1315
+ if ( ! empty( $query->query_vars['tags'] ) ) {
1316
+ $tag = $query->query_vars['tags'];
1317
  }
1318
+ if ( $tag ) {
1319
+ if ( false !== strpos( $tag, '+' ) ) {
1320
+ $tag = explode( '+', $tag );
1321
  $operator = 'and';
1322
+ } else {
1323
+ $tag = explode( ',', $tag );
 
1324
  $operator = 'or';
1325
  }
1326
+ $tax_query[] = array(
1327
+ 'taxonomy' => 'post_tag',
1328
+ 'field' => 'id',
1329
+ 'terms' => $tag,
1330
+ 'operator' => $operator,
1331
+ );
1332
+ }
1333
+ if ( ! empty( $query->query_vars['tag_id'] ) ) {
1334
+ $tax_query[] = array(
1335
+ 'taxonomy' => 'post_tag',
1336
+ 'field' => 'id',
1337
+ 'terms' => $query->query_vars['tag_id'],
1338
+ );
1339
+ }
1340
+ if ( ! empty( $query->query_vars['tag_id'] ) ) {
1341
+ $tax_query[] = array(
1342
+ 'taxonomy' => 'post_tag',
1343
+ 'field' => 'id',
1344
+ 'terms' => $query->query_vars['tag_id'],
1345
+ );
1346
+ }
1347
+ if ( ! empty( $query->query_vars['tag__in'] ) ) {
1348
+ $tax_query[] = array(
1349
+ 'taxonomy' => 'post_tag',
1350
+ 'field' => 'id',
1351
+ 'terms' => $query->query_vars['tag__in'],
1352
+ );
1353
+ }
1354
+ if ( ! empty( $query->query_vars['tag__not_in'] ) ) {
1355
+ $tax_query[] = array(
1356
+ 'taxonomy' => 'post_tag',
1357
+ 'field' => 'id',
1358
+ 'terms' => $query->query_vars['tag__not_in'],
1359
+ 'operator' => 'NOT IN',
1360
+ );
1361
+ }
1362
+ if ( ! empty( $query->query_vars['tag__and'] ) ) {
1363
+ $tax_query[] = array(
1364
+ 'taxonomy' => 'post_tag',
1365
+ 'field' => 'id',
1366
+ 'terms' => $query->query_vars['tag__and'],
1367
+ 'operator' => 'AND',
1368
+ );
1369
+ }
1370
+ if ( ! empty( $query->query_vars['tag__not_in'] ) ) {
1371
+ $tax_query[] = array(
1372
+ 'taxonomy' => 'post_tag',
1373
+ 'field' => 'id',
1374
+ 'terms' => $query->query_vars['tag__not_in'],
1375
+ 'operator' => 'NOT IN',
1376
+ );
1377
+ }
1378
+ if ( ! empty( $query->query_vars['tag_slug__in'] ) ) {
1379
+ $tax_query[] = array(
1380
+ 'taxonomy' => 'post_tag',
1381
+ 'field' => 'slug',
1382
+ 'terms' => $query->query_vars['tag_slug__in'],
1383
+ );
1384
+ }
1385
+ if ( ! empty( $query->query_vars['tag_slug__not_in'] ) ) {
1386
+ $tax_query[] = array(
1387
+ 'taxonomy' => 'post_tag',
1388
+ 'field' => 'slug',
1389
+ 'terms' => $query->query_vars['tag_slug__not_in'],
1390
+ 'operator' => 'NOT IN',
1391
+ );
1392
+ }
1393
+ if ( ! empty( $query->query_vars['tag_slug__and'] ) ) {
1394
+ $tax_query[] = array(
1395
+ 'taxonomy' => 'post_tag',
1396
+ 'field' => 'slug',
1397
+ 'terms' => $query->query_vars['tag_slug__and'],
1398
+ 'operator' => 'AND',
1399
+ );
1400
+ }
1401
+ $extag = get_option( 'relevanssi_extag' );
1402
+ if ( ! empty( $extag ) && '0' !== $extag ) {
1403
+ $tax_query[] = array(
1404
+ 'taxonomy' => 'post_tag',
1405
+ 'field' => 'id',
1406
+ 'terms' => $extag,
1407
+ 'operator' => 'NOT IN',
1408
+ );
1409
+ }
1410
+
1411
+ if ( isset( $query->query_vars['taxonomy'] ) ) {
1412
+ if ( function_exists( 'relevanssi_process_taxonomies' ) ) {
1413
+ $tax_query = relevanssi_process_taxonomies( $query->query_vars['taxonomy'], $query->query_vars['term'], $tax_query );
1414
+ } else {
1415
+ if ( ! empty( $query->query_vars['term'] ) ) {
1416
+ $term = $query->query_vars['term'];
1417
+ }
1418
 
1419
+ $tax_query[] = array(
1420
+ 'taxonomy' => $query->query_vars['taxonomy'],
1421
+ 'field' => 'slug',
1422
+ 'terms' => $term,
1423
+ );
1424
  }
1425
  }
1426
  }
1427
 
1428
  $author = false;
1429
+ if ( ! empty( $query->query_vars['author'] ) ) {
1430
+ $author = explode( ',', $query->query_vars['author'] );
1431
  }
1432
+ if ( ! empty( $query->query_vars['author_name'] ) ) {
1433
+ $author_object = get_user_by( 'slug', $query->query_vars['author_name'] );
1434
+ $author[] = $author_object->ID;
1435
  }
1436
 
1437
  $post_query = array();
1438
+ if ( ! empty( $query->query_vars['p'] ) ) {
1439
+ $post_query = array( 'in' => array( $query->query_vars['p'] ) );
1440
  }
1441
+ if ( ! empty( $query->query_vars['page_id'] ) ) {
1442
+ $post_query = array( 'in' => array( $query->query_vars['page_id'] ) );
1443
  }
1444
+ if ( ! empty( $query->query_vars['post__in'] ) ) {
1445
+ $post_query = array( 'in' => $query->query_vars['post__in'] );
1446
  }
1447
+ if ( ! empty( $query->query_vars['post__not_in'] ) ) {
1448
+ $post_query = array( 'not in' => $query->query_vars['post__not_in'] );
1449
  }
1450
 
1451
  $parent_query = array();
1452
+ if ( ! empty( $query->query_vars['post_parent'] ) ) {
1453
+ $parent_query = array( 'parent in' => array( $query->query_vars['post_parent'] ) );
1454
  }
1455
+ if ( ! empty( $query->query_vars['post_parent__in'] ) ) {
1456
+ $parent_query = array( 'parent in' => $query->query_vars['post_parent__in'] );
1457
  }
1458
+ if ( ! empty( $query->query_vars['post_parent__not_in'] ) ) {
1459
+ $parent_query = array( 'parent not in' => $query->query_vars['post_parent__not_in'] );
1460
  }
1461
 
1462
+ /**
1463
+ * Filters the default meta_query relation.
1464
+ *
1465
+ * @param string The meta_query relation, default 'AND'.
1466
+ */
1467
+ $meta_query_relation = apply_filters( 'relevanssi_default_meta_query_relation', 'AND' );
1468
+ $meta_query = array();
1469
+ if ( ! empty( $query->query_vars['meta_query'] ) ) {
1470
+ $meta_query = $query->query_vars['meta_query'];
1471
+ }
1472
 
1473
+ if ( isset( $query->query_vars['customfield_key'] ) ) {
1474
  $build_meta_query = array();
1475
 
1476
+ // Use meta key.
1477
+ $build_meta_query['key'] = $query->query_vars['customfield_key'];
1478
 
1479
  /**
1480
+ * Check the value is not empty for ordering purpose,
1481
+ * set it or not for the current meta query.
1482
+ */
1483
+ if ( ! empty( $query->query_vars['customfield_value'] ) ) {
1484
+ $build_meta_query['value'] = $query->query_vars['customfield_value'];
1485
  }
1486
 
1487
+ // Set the compare.
1488
  $build_meta_query['compare'] = '=';
1489
+ $meta_query[] = $build_meta_query;
1490
+ }
1491
 
1492
+ if ( ! empty( $query->query_vars['meta_key'] ) || ! empty( $query->query_vars['meta_value'] ) || ! empty( $query->query_vars['meta_value_num'] ) ) {
1493
  $build_meta_query = array();
1494
 
1495
+ // Use meta key.
1496
+ $build_meta_query['key'] = $query->query_vars['meta_key'];
1497
 
1498
+ $value = null;
1499
+ if ( ! empty( $query->query_vars['meta_value'] ) ) {
1500
+ $value = $query->query_vars['meta_value'];
1501
+ } elseif ( ! empty( $query->query_vars['meta_value_num'] ) ) {
1502
+ $value = $query->query_vars['meta_value_num'];
1503
  }
1504
 
1505
  /**
1506
+ * Check the meta value, as it could be not set for ordering purpose.
1507
+ * Set it or not for the current meta query.
1508
  */
1509
  if ( ! empty( $value ) ) {
1510
  $build_meta_query['value'] = $value;
1511
  }
1512
 
1513
+ // Set meta compare.
1514
+ $build_meta_query['compare'] = '=';
1515
+ if ( ! empty( $query->query_vars['meta_compare'] ) ) {
1516
+ $query->query_vars['meta_compare'];
1517
+ }
1518
 
1519
  $meta_query[] = $build_meta_query;
1520
+ }
1521
 
1522
  $date_query = false;
1523
+ if ( ! empty( $query->date_query ) ) {
1524
+ if ( is_object( $query->date_query ) && 'WP_Date_Query' === get_class( $query->date_query ) ) {
1525
  $date_query = $query->date_query;
1526
  } else {
1527
+ $date_query = new WP_Date_Query( $query->date_query );
1528
  }
1529
  }
1530
 
1531
  $search_blogs = false;
1532
+ if ( isset( $query->query_vars['search_blogs'] ) ) {
1533
+ $search_blogs = $query->query_vars['search_blogs'];
1534
  }
1535
 
1536
  $post_type = false;
1537
+ if ( isset( $query->query_vars['post_type'] ) && 'any' !== $query->query_vars['post_type'] ) {
1538
+ $post_type = $query->query_vars['post_type'];
1539
  }
1540
+ if ( isset( $query->query_vars['post_types'] ) && 'any' !== $query->query_vars['post_types'] ) {
1541
+ $post_type = $query->query_vars['post_types'];
1542
  }
1543
 
 
 
1544
  $post_status = false;
1545
+ if ( isset( $query->query_vars['post_status'] ) && 'any' !== $query->query_vars['post_status'] ) {
1546
+ $post_status = $query->query_vars['post_status'];
1547
  }
1548
 
1549
+ $expost = get_option( 'relevanssi_exclude_posts' );
1550
 
1551
+ // In admin (and when not AJAX), search everything.
1552
  if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
1553
+ $excat = null;
1554
+ $extag = null;
1555
  $expost = null;
1556
  }
1557
 
1558
  $sentence = false;
1559
+ if ( isset( $query->query_vars['sentence'] ) && ! empty( $query->query_vars['sentence'] ) ) {
1560
  $sentence = true;
1561
  }
1562
 
1563
+ $operator = '';
1564
+ if ( function_exists( 'relevanssi_set_operator' ) ) {
1565
+ $operator = relevanssi_set_operator( $query );
1566
+ $operator = strtoupper( $operator );
1567
+ }
1568
+ if ( ! in_array( $operator, array( 'OR', 'AND' ), true ) ) {
1569
+ $operator = get_option( 'relevanssi_implicit_operator' );
1570
  }
1571
+ $query->query_vars['operator'] = $operator;
 
1572
 
1573
+ $orderby = null;
1574
+ $order = null;
1575
+ if ( isset( $query->query_vars['orderby'] ) ) {
1576
+ $orderby = $query->query_vars['orderby'];
1577
+ }
1578
+ if ( isset( $query->query_vars['order'] ) ) {
1579
+ $order = $query->query_vars['order'];
1580
+ }
1581
 
1582
+ $fields = '';
1583
+ if ( ! empty( $query->query_vars['fields'] ) ) {
1584
+ if ( 'ids' === $query->query_vars['fields'] ) {
1585
  $fields = 'ids';
1586
  }
1587
+ if ( 'id=>parent' === $query->query_vars['fields'] ) {
1588
  $fields = 'id=>parent';
1589
  }
1590
  }
1591
 
1592
+ $by_date = '';
1593
+ if ( ! empty( $query->query_vars['by_date'] ) ) {
1594
+ if ( preg_match( '/\d+[hdmyw]/', $query->query_vars['by_date'] ) ) {
1595
+ // Accepted format is digits followed by h, d, m, y, or w.
1596
+ $by_date = $query->query_vars['by_date'];
1597
+ }
1598
+ }
1599
+ // Add synonyms.
1600
+ // This is done here so the new terms will get highlighting.
1601
+ if ( 'OR' === $operator ) {
1602
+ // Synonyms are only used in OR queries.
1603
+ $q = relevanssi_add_synonyms( $q );
1604
  }
1605
 
1606
  $search_params = array(
1607
+ 'q' => $q,
1608
+ 'tax_query' => $tax_query,
1609
  'tax_query_relation' => $tax_query_relation,
1610
+ 'post_query' => $post_query,
1611
+ 'parent_query' => $parent_query,
1612
+ 'meta_query' => $meta_query,
1613
+ 'date_query' => $date_query,
1614
+ 'expost' => $expost,
1615
+ 'post_type' => $post_type,
1616
+ 'post_status' => $post_status,
1617
+ 'operator' => $operator,
1618
+ 'search_blogs' => $search_blogs,
1619
+ 'author' => $author,
1620
+ 'orderby' => $orderby,
1621
+ 'order' => $order,
1622
+ 'fields' => $fields,
1623
+ 'sentence' => $sentence,
1624
+ 'by_date' => $by_date,
1625
+ );
1626
+
1627
+ $return = relevanssi_search( $search_params );
1628
+ }
1629
+
1630
+ $hits = array();
1631
+ if ( isset( $return['hits'] ) ) {
1632
+ $hits = $return['hits'];
1633
+ }
1634
+ $q = '';
1635
+ if ( isset( $return['query'] ) ) {
1636
+ $q = $return['query'];
1637
+ }
1638
+
1639
+ $filter_data = array( $hits, $q );
1640
+ /**
1641
+ * Filters the founds results.
1642
+ *
1643
+ * One of the key filters for Relevanssi. If you want to modify the results
1644
+ * Relevanssi finds, use this filter.
1645
+ *
1646
+ * @param array $filter_data The index 0 has an array of post objects found in
1647
+ * the search, index 1 has the search query string.
1648
+ *
1649
+ * @return array The return array composition is the same as the parameter array,
1650
+ * but Relevanssi only uses the index 0.
1651
+ */
1652
+ $hits_filters_applied = apply_filters( 'relevanssi_hits_filter', $filter_data );
1653
  // array_values() to make sure the $hits array is indexed in numerical order
1654
  // Manipulating the array with array_unique() for example may mess with that.
1655
+ $hits = array_values( $hits_filters_applied[0] );
1656
 
1657
+ $query->found_posts = count( $hits );
1658
+ if ( ! isset( $query->query_vars['posts_per_page'] ) || 0 === $query->query_vars['posts_per_page'] ) {
1659
+ // Assume something sensible to prevent "division by zero error".
1660
+ $query->query_vars['posts_per_page'] = -1;
1661
  }
1662
+ if ( -1 === $query->query_vars['posts_per_page'] ) {
1663
+ $query->max_num_pages = count( $hits );
1664
+ } else {
1665
+ $query->max_num_pages = ceil( count( $hits ) / $query->query_vars['posts_per_page'] );
 
1666
  }
1667
 
1668
+ $update_log = get_option( 'relevanssi_log_queries' );
1669
+ if ( 'on' === $update_log ) {
1670
+ relevanssi_update_log( $q, count( $hits ) );
1671
  }
1672
 
1673
+ $make_excerpts = get_option( 'relevanssi_excerpts' );
1674
+ if ( $query->is_admin ) {
1675
+ $make_excerpts = false;
 
 
 
 
 
1676
  }
1677
 
1678
+ if ( isset( $query->query_vars['paged'] ) && $query->query_vars['paged'] > 0 ) {
1679
+ $search_low_boundary = ( $query->query_vars['paged'] - 1 ) * $query->query_vars['posts_per_page'];
1680
+ } else {
1681
+ $search_low_boundary = 0;
1682
  }
1683
+
1684
+ if ( ! isset( $query->query_vars['posts_per_page'] ) || -1 === $query->query_vars['posts_per_page'] ) {
1685
+ $search_high_boundary = count( $hits );
1686
+ } else {
1687
+ $search_high_boundary = $search_low_boundary + $query->query_vars['posts_per_page'] - 1;
1688
  }
1689
 
1690
+ if ( isset( $query->query_vars['offset'] ) && $query->query_vars['offset'] > 0 ) {
1691
+ $search_high_boundary += $query->query_vars['offset'];
1692
+ $search_low_boundary += $query->query_vars['offset'];
1693
  }
1694
 
1695
+ if ( $search_high_boundary > count( $hits ) ) {
1696
+ $search_high_boundary = count( $hits );
1697
+ }
1698
 
1699
+ for ( $i = $search_low_boundary; $i <= $search_high_boundary; $i++ ) {
1700
+ if ( isset( $hits[ intval( $i ) ] ) ) {
1701
+ $post = $hits[ intval( $i ) ];
1702
+ } else {
 
1703
  continue;
1704
  }
1705
 
1706
+ if ( null === $post ) {
1707
+ // Sometimes you can get a null object.
1708
  continue;
1709
  }
1710
 
1711
+ if ( 'on' === get_option( 'relevanssi_hilite_title' ) && empty( $fields ) ) {
1712
+ if ( function_exists( 'qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
1713
+ $post->post_highlighted_title = strip_tags( qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $post->post_title ) );
1714
+ } else {
1715
+ $post->post_highlighted_title = strip_tags( $post->post_title );
 
 
1716
  }
1717
+ $highlight = get_option( 'relevanssi_highlight' );
1718
+ if ( 'none' !== $highlight ) {
1719
+ if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
1720
+ $post->post_highlighted_title = relevanssi_highlight_terms( $post->post_highlighted_title, $q );
1721
  }
1722
  }
1723
  }
 
1724
 
1725
+ if ( 'on' === $make_excerpts && empty( $fields ) ) {
1726
  $post->original_excerpt = $post->post_excerpt;
1727
+ $post->post_excerpt = relevanssi_do_excerpt( $post, $q );
1728
  }
1729
 
1730
+ if ( 'on' === get_option( 'relevanssi_show_matches' ) && empty( $fields ) ) {
1731
  $post_id = $post->ID;
1732
+ if ( 'user' === $post->post_type ) {
1733
+ $post_id = 'u_' . $post->user_id;
1734
+ } elseif ( isset( $post->term_id ) ) {
 
1735
  $post_id = '**' . $post->post_type . '**' . $post->term_id;
1736
  }
1737
+ $post->post_excerpt .= relevanssi_show_matches( $return, $post_id );
1738
  }
1739
 
1740
+ if ( empty( $fields ) && isset( $return['scores'][ $post->ID ] ) ) {
1741
+ $post->relevance_score = round( $return['scores'][ $post->ID ], 2 );
1742
+ }
1743
 
1744
  $posts[] = $post;
1745
  }
1746
 
1747
+ $query->posts = $posts;
1748
+ $query->post_count = count( $posts );
1749
 
1750
  return $posts;
1751
  }
1752
 
1753
+ /**
1754
+ * Limits the search queries to restrict the number of posts handled.
1755
+ *
1756
+ * @param string $query The MySQL query.
1757
+ *
1758
+ * @return string The query with the LIMIT parameter added, if necessary.
1759
+ */
1760
+ function relevanssi_limit_filter( $query ) {
1761
+ if ( 'on' === get_option( 'relevanssi_throttle', 'on' ) ) {
1762
+ $limit = get_option( 'relevanssi_throttle_limit', 500 );
1763
+ if ( ! is_numeric( $limit ) ) {
1764
+ $limit = 500;
1765
+ }
1766
+ if ( $limit < 0 ) {
1767
+ $limit = 500;
1768
+ }
1769
  return $query . " ORDER BY tf DESC LIMIT $limit";
1770
+ } else {
 
1771
  return $query;
1772
  }
1773
  }
1774
 
1775
+ /**
1776
+ * Fetches the list of post types that are excluded from the search.
1777
+ *
1778
+ * Figures out the post types that are not included in the search.
1779
+ *
1780
+ * @global WP_Query $wp_query The global WP_Query object.
1781
+ *
1782
+ * @return string SQL escaped list of excluded post types.
1783
+ */
1784
  function relevanssi_get_negative_post_type() {
 
1785
  global $wp_query;
1786
 
1787
+ $negative_post_type = null;
1788
  $negative_post_type_list = array();
1789
 
1790
+ if ( isset( $wp_query->query_vars['include_attachments'] ) && in_array( $wp_query->query_vars['include_attachments'], array( '0', 'off', 'false' ), true ) ) {
1791
+ $negative_post_type_list[] = 'attachment';
1792
  }
1793
 
1794
+ if ( 'on' === get_option( 'relevanssi_respect_exclude' ) ) {
1795
  // If Relevanssi is set to respect exclude_from_search, find out which
1796
  // post types should be excluded from search.
1797
+ $pt_1 = get_post_types( array( 'exclude_from_search' => '1' ) );
1798
+ $pt_2 = get_post_types( array( 'exclude_from_search' => true ) );
1799
+
1800
+ $negative_post_type_list = array_merge( $negative_post_type_list, $pt_1, $pt_2 );
 
1801
  }
1802
 
1803
  // Post types to exclude.
1804
+ if ( count( $negative_post_type_list ) > 0 ) {
1805
+ $negative_post_types = esc_sql( array_unique( $negative_post_type_list ) );
1806
+ $negative_post_type = null;
1807
+ if ( count( $negative_post_types ) ) {
1808
+ $negative_post_type = "'" . implode( "', '", $negative_post_types ) . "'";
1809
+ }
1810
  }
1811
 
1812
  return $negative_post_type;
1813
  }
1814
 
1815
+ /**
1816
+ * Processes one tax_query row.
1817
+ *
1818
+ * @global object $wpdb The WordPress database interface.
1819
+ *
1820
+ * @param array $row The tax_query row array.
1821
+ * @param boolean $is_sub_row True if this is a subrow.
1822
+ * @param string $global_relation The global tax_query relation (AND or OR).
1823
+ * @param string $query_restrictions The MySQL query restriction.
1824
+ * @param string $tax_query_relation The tax_query relation.
1825
+ * @param array $term_tax_ids Array of term taxonomy IDs.
1826
+ * @param array $not_term_tax_ids Array of excluded term taxonomy IDs.
1827
+ * @param array $and_term_tax_ids Array of AND term taxonomy IDs.
1828
+ *
1829
+ * @return array Returns an array where the first item is the updated
1830
+ * $query_restrictions, then $term_tax_ids, $not_term_tax_ids, and $and_term_tax_ids.
1831
+ */
1832
+ function relevanssi_process_tax_query_row( $row, $is_sub_row, $global_relation, $query_restrictions, $tax_query_relation, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids ) {
1833
  global $wpdb;
1834
 
1835
+ $local_term_tax_ids = array();
1836
  $local_not_term_tax_ids = array();
1837
  $local_and_term_tax_ids = array();
1838
 
1839
  $using_term_tax_id = false;
1840
+ if ( ! isset( $row['field'] ) ) {
1841
+ $row['field'] = 'term_id'; // In case 'field' is not set, go with the WP default of 'term_id'.
1842
+ }
1843
+ if ( 'slug' === $row['field'] ) {
1844
+ $slug = $row['terms'];
1845
  $numeric_slugs = array();
1846
+ $slug_in = null;
1847
+ if ( is_array( $slug ) ) {
1848
+ $slugs = array();
1849
  $term_id = array();
1850
+ foreach ( $slug as $t_slug ) {
1851
+ $term = get_term_by( 'slug', $t_slug, $row['taxonomy'] );
1852
+ if ( ! $term && is_numeric( $t_slug ) ) {
1853
  $numeric_slugs[] = "'$t_slug'";
1854
+ } else {
1855
+ if ( isset( $term->term_id ) ) {
1856
+ $t_slug = sanitize_title( $t_slug );
 
1857
  $term_id[] = $term->term_id;
1858
+ $slugs[] = "'$t_slug'";
1859
  }
1860
  }
1861
  }
1862
+ if ( ! empty( $slugs ) ) {
1863
+ $slug_in = implode( ',', $slugs );
 
 
 
 
1864
  }
1865
+ } else {
1866
+ $term = get_term_by( 'slug', $slug, $row['taxonomy'], OBJECT );
1867
+ if ( ! $term && is_numeric( $slug ) ) {
1868
+ $numeric_slugs[] = $slug;
1869
+ } else {
1870
+ if ( isset( $term->term_id ) ) {
1871
+ $slug = sanitize_title( $slug );
1872
  $term_id = $term->term_id;
1873
  $slug_in = "'$slug'";
1874
  }
1875
  }
1876
  }
1877
+ if ( ! empty( $slug_in ) ) {
1878
+ $row_taxonomy = sanitize_text_field( $row['taxonomy'] );
1879
+
1880
  $tt_q = "SELECT tt.term_taxonomy_id
1881
  FROM $wpdb->term_taxonomy AS tt
1882
  LEFT JOIN $wpdb->terms AS t ON (tt.term_id=t.term_id)
1883
  WHERE tt.taxonomy = '$row_taxonomy' AND t.slug IN ($slug_in)";
1884
+ // Clean: $row_taxonomy is sanitized, each slug in $slug_in is sanitized.
1885
+ $term_tax_id = $wpdb->get_col( $tt_q ); // WPCS: unprepared SQL ok.
1886
+ }
1887
+ if ( ! empty( $numeric_slugs ) ) {
1888
+ $row['field'] = 'term_id';
1889
  }
 
1890
  }
1891
+ if ( 'name' === $row['field'] ) {
1892
+ $name = $row['terms'];
1893
  $numeric_names = array();
1894
+ $name_in = null;
1895
+ if ( is_array( $name ) ) {
1896
+ $names = array();
1897
  $term_id = array();
1898
+ foreach ( $name as $t_name ) {
1899
+ $term = get_term_by( 'name', $t_name, $row['taxonomy'] );
1900
+ if ( ! $term && is_numeric( $t_names ) ) {
1901
  $numeric_names[] = "'$t_name'";
1902
+ } else {
1903
+ if ( isset( $term->term_id ) ) {
1904
+ $t_name = sanitize_title( $t_name );
 
1905
  $term_id[] = $term->term_id;
1906
+ $names[] = "'$t_name'";
1907
  }
1908
  }
1909
  }
1910
+ if ( ! empty( $names ) ) {
1911
+ $name_in = implode( ',', $names );
 
 
 
 
1912
  }
1913
+ } else {
1914
+ $term = get_term_by( 'name', $name, $row['taxonomy'] );
1915
+ if ( ! $term && is_numeric( $name ) ) {
1916
+ $numeric_slugs[] = $name;
1917
+ } else {
1918
+ if ( isset( $term->term_id ) ) {
1919
+ $name = sanitize_title( $name );
1920
  $term_id = $term->term_id;
1921
  $name_in = "'$name'";
1922
  }
1923
  }
1924
  }
1925
+ if ( ! empty( $name_in ) ) {
1926
+ $row_taxonomy = sanitize_text_field( $row['taxonomy'] );
1927
+
1928
  $tt_q = "SELECT tt.term_taxonomy_id
1929
  FROM $wpdb->term_taxonomy AS tt
1930
  LEFT JOIN $wpdb->terms AS t ON (tt.term_id=t.term_id)
1931
  WHERE tt.taxonomy = '$row_taxonomy' AND t.name IN ($name_in)";
1932
+ // Clean: $row_taxonomy is sanitized, each name in $name_in is sanitized.
1933
+ $term_tax_id = $wpdb->get_col( $tt_q ); // WPCS: unprepared SQL ok.
1934
+ }
1935
+ if ( ! empty( $numeric_names ) ) {
1936
+ $row['field'] = 'term_id';
1937
  }
 
1938
  }
1939
+ if ( 'id' === $row['field'] || 'term_id' === $row['field'] ) {
1940
+ $id = $row['terms'];
1941
  $term_id = $id;
1942
+ if ( is_array( $id ) ) {
1943
  $numeric_values = array();
1944
+ foreach ( $id as $t_id ) {
1945
+ if ( is_numeric( $t_id ) ) {
1946
+ $numeric_values[] = $t_id;
1947
+ }
1948
  }
1949
+ $id = implode( ',', $numeric_values );
 
 
 
 
 
 
 
 
 
 
1950
  }
1951
+ $row_taxonomy = sanitize_text_field( $row['taxonomy'] );
1952
+
1953
+ if ( ! empty( $id ) ) {
1954
+ $tt_q = "SELECT tt.term_taxonomy_id
1955
+ FROM $wpdb->term_taxonomy AS tt
1956
+ LEFT JOIN $wpdb->terms AS t ON (tt.term_id=t.term_id)
1957
+ WHERE tt.taxonomy = '$row_taxonomy' AND t.term_id IN ($id)";
1958
+ // Clean: $row_taxonomy is sanitized, $id is checked to be numeric.
1959
+ $id_term_tax_id = $wpdb->get_col( $tt_q ); // WPCS: unprepared SQL ok.
1960
+ if ( ! empty( $term_tax_id ) && is_array( $term_tax_id ) ) {
1961
+ $term_tax_id = array_unique( array_merge( $term_tax_id, $id_term_tax_id ) );
1962
+ } else {
1963
+ $term_tax_id = $id_term_tax_id;
1964
+ }
1965
  }
1966
  }
1967
+ if ( 'term_taxonomy_id' === $row['field'] ) {
1968
  $using_term_tax_id = true;
1969
+ $id = $row['terms'];
1970
+ $term_tax_id = $id;
1971
+ if ( is_array( $id ) ) {
1972
  $numeric_values = array();
1973
+ foreach ( $id as $t_id ) {
1974
+ if ( is_numeric( $t_id ) ) {
1975
+ $numeric_values[] = $t_id;
1976
+ }
1977
  }
1978
+ $term_tax_id = implode( ',', $numeric_values );
1979
  }
1980
  }
1981
 
1982
+ if ( ! isset( $row['include_children'] ) || true === $row['include_children'] ) {
1983
+ if ( ! $using_term_tax_id && isset( $term_id ) ) {
1984
+ if ( ! is_array( $term_id ) ) {
1985
+ $term_id = array( $term_id );
1986
  }
1987
+ } else {
1988
+ if ( ! is_array( $term_tax_id ) ) {
1989
+ $term_tax_id = array( $term_tax_id );
1990
+ $term_id = $term_tax_id;
 
1991
  }
1992
  }
1993
+ if ( empty( $term_tax_id ) ) {
1994
+ $term_tax_id = array();
1995
+ }
1996
+ if ( ! is_array( $term_tax_id ) ) {
1997
+ $term_tax_id = array( $term_tax_id );
1998
+ }
1999
+ if ( isset( $term_id ) && is_array( $term_id ) ) {
2000
+ foreach ( $term_id as $t_id ) {
2001
+ if ( $using_term_tax_id ) {
2002
+ $t_term = get_term_by( 'term_taxonomy_id', $t_id, $row['taxonomy'] );
2003
+ $t_id = $t_term->ID;
2004
  }
2005
+ $kids = get_term_children( $t_id, $row['taxonomy'] );
2006
+ foreach ( $kids as $kid ) {
2007
+ $term = get_term_by( 'id', $kid, $row['taxonomy'] );
2008
+ $kid_term_tax_id = relevanssi_get_term_tax_id( $kid, $row['taxonomy'] );
2009
+ $term_tax_id[] = $kid_term_tax_id;
2010
  }
2011
  }
2012
  }
2013
  }
2014
 
2015
+ $term_tax_id = array_unique( $term_tax_id );
2016
+ if ( ! empty( $term_tax_id ) ) {
2017
+ $n = count( $term_tax_id );
2018
+ $term_tax_id = implode( ',', $term_tax_id );
2019
 
2020
+ $tq_operator = 'IN'; // Assuming the default operator "IN", unless something else is provided.
2021
+ if ( isset( $row['operator'] ) ) {
2022
+ $tq_operator = strtoupper( $row['operator'] );
2023
+ }
2024
+ if ( ! in_array( $tq_operator, array( 'IN', 'NOT IN', 'AND' ), true ) ) {
2025
+ $tq_operator = 'IN';
2026
+ }
2027
+ if ( 'and' === $tax_query_relation ) {
2028
+ if ( 'AND' === $tq_operator ) {
2029
  $query_restrictions .= " AND relevanssi.doc IN (
2030
  SELECT ID FROM $wpdb->posts WHERE 1=1
2031
  AND (
2034
  WHERE tr.term_taxonomy_id IN ($term_tax_id)
2035
  AND tr.object_id = $wpdb->posts.ID ) = $n
2036
  )";
2037
+ // Clean: $term_tax_id and $n are Relevanssi-generated.
2038
+ } else {
 
2039
  $query_restrictions .= " AND relevanssi.doc $tq_operator (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr
2040
  WHERE tr.term_taxonomy_id IN ($term_tax_id))";
2041
+ // Clean: all variables are Relevanssi-generated.
2042
+ }
2043
+ } else {
2044
+ if ( 'IN' === $tq_operator ) {
2045
+ $local_term_tax_ids[] = $term_tax_id;
2046
+ }
2047
+ if ( 'NOT IN' === $tq_operator ) {
2048
+ $local_not_term_tax_ids[] = $term_tax_id;
2049
+ }
2050
+ if ( 'AND' === $tq_operator ) {
2051
+ $local_and_term_tax_ids[] = $term_tax_id;
2052
  }
2053
  }
2054
+ } else {
 
 
 
 
 
 
2055
  global $wp_query;
2056
  $wp_query->is_category = false;
2057
  }
2058
+
2059
+ if ( $is_sub_row && 'and' === $global_relation && 'or' === $tax_query_relation ) {
2060
+ $local_term_tax_ids = array_unique( $local_term_tax_ids );
2061
+ $local_not_term_tax_ids = array_unique( $local_not_term_tax_ids );
2062
+ $local_and_term_tax_ids = array_unique( $local_and_term_tax_ids );
2063
+ if ( count( $local_term_tax_ids ) > 0 ) {
2064
+ $local_term_tax_ids = implode( ',', $local_term_tax_ids );
2065
  $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr
2066
  WHERE tr.term_taxonomy_id IN ($local_term_tax_ids))";
2067
+ // Clean: all variables are Relevanssi-generated.
2068
  }
2069
+ if ( count( $local_not_term_tax_ids ) > 0 ) {
2070
+ $local_not_term_tax_ids = implode( ',', $local_not_term_tax_ids );
2071
+ $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(tr.object_id) FROM $wpdb->term_relationships AS tr
2072
  WHERE tr.term_taxonomy_id IN ($local_not_term_tax_ids))";
2073
+ // Clean: all variables are Relevanssi-generated.
2074
  }
2075
+ if ( count( $local_and_term_tax_ids ) > 0 ) {
2076
+ $local_and_term_tax_ids = implode( ',', $local_and_term_tax_ids );
2077
+ $n = count( explode( ',', $local_and_term_tax_ids ) );
2078
+ $query_restrictions .= " AND relevanssi.doc IN (
2079
  SELECT ID FROM $wpdb->posts WHERE 1=1
2080
  AND (
2081
  SELECT COUNT(1)
2083
  WHERE tr.term_taxonomy_id IN ($local_and_term_tax_ids)
2084
  AND tr.object_id = $wpdb->posts.ID ) = $n
2085
  )";
2086
+ // Clean: all variables are Relevanssi-generated.
2087
  }
2088
  }
2089
+
2090
  $copy_term_tax_ids = false;
2091
+ if ( ! $is_sub_row ) {
2092
+ $copy_term_tax_ids = true;
2093
+ }
2094
+ if ( $is_sub_row && 'or' === $global_relation ) {
2095
+ $copy_term_tax_ids = true;
2096
+ }
2097
 
2098
+ if ( $copy_term_tax_ids ) {
2099
+ $term_tax_ids = array_merge( $term_tax_ids, $local_term_tax_ids );
2100
+ $not_term_tax_ids = array_merge( $not_term_tax_ids, $local_not_term_tax_ids );
2101
+ $and_term_tax_ids = array_merge( $and_term_tax_ids, $local_and_term_tax_ids );
2102
  }
2103
 
2104
+ return array( $query_restrictions, $term_tax_ids, $not_term_tax_ids, $and_term_tax_ids );
2105
+ }
lib/shortcodes.php CHANGED
@@ -1,45 +1,121 @@
1
  <?php
 
 
 
 
 
 
 
 
2
 
3
- add_shortcode('search', 'relevanssi_shortcode');
4
- add_shortcode('noindex', 'relevanssi_noindex_shortcode');
5
- add_shortcode('searchform', 'relevanssi_search_form');
6
 
7
- function relevanssi_shortcode($atts, $content, $name) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  global $wpdb;
9
 
10
- extract(shortcode_atts(array('term' => false, 'phrase' => 'not'), $atts));
11
-
12
- if ($term != false) {
13
- $term = urlencode(strtolower($term));
14
- }
15
- else {
16
- $term = urlencode(strip_tags(strtolower($content)));
 
 
 
 
 
17
  }
18
-
19
- if ($phrase != 'not') {
20
- $term = '%22' . $term . '%22';
21
  }
22
-
23
- $link = get_bloginfo('url') . "/?s=$term";
24
-
25
- $pre = "<a href='$link'>";
26
- $post = "</a>";
27
 
28
- return $pre . do_shortcode($content) . $post;
 
 
 
 
29
  }
30
 
31
- function relevanssi_noindex_shortcode($atts, $content) {
32
- // When in general use, make the shortcode disappear.
33
- return do_shortcode($content);
 
 
 
 
 
 
 
 
 
34
  }
35
 
36
- function relevanssi_noindex_shortcode_indexing($atts, $content) {
37
- // When indexing, make the text disappear.
 
 
 
 
 
 
 
 
 
38
  return '';
39
  }
40
 
41
- function relevanssi_search_form() {
42
- return get_search_form(false);
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
- ?>
 
 
 
 
 
1
  <?php
2
+ /**
3
+ * /lib/shortcodes.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
 
11
+ add_shortcode( 'search', 'relevanssi_shortcode' );
12
+ add_shortcode( 'noindex', 'relevanssi_noindex_shortcode' );
13
+ add_shortcode( 'searchform', 'relevanssi_search_form' );
14
 
15
+ /**
16
+ * Creates a link to search results.
17
+ *
18
+ * Using this is generally not a brilliant idea, actually. Google doesn't like if you
19
+ * create links to internal search results.
20
+ *
21
+ * Usage: [search term='tomato']tomatoes[/search] would create a link like this:
22
+ * <a href="/?s=tomato">tomatoes</a>
23
+ *
24
+ * Set 'phrase' to something else than 'not' to make the search term a phrase.
25
+ *
26
+ * @global object $wpdb The WordPress database interface.
27
+ *
28
+ * @param array $atts The shortcode attributes. If 'term' is set, will use it as
29
+ * the search term, otherwise the content word is used as the term.
30
+ * @param string $content The content inside the shortcode tags.
31
+ *
32
+ * @return string A link to search results.
33
+ */
34
+ function relevanssi_shortcode( $atts, $content ) {
35
  global $wpdb;
36
 
37
+ $attributes = shortcode_atts( array(
38
+ 'term' => false,
39
+ 'phrase' => 'not',
40
+ ), $atts);
41
+
42
+ $term = $attributes['term'];
43
+ $phrase = $attributes['phrase'];
44
+
45
+ if ( false !== $term ) {
46
+ $term = rawurlencode( relevanssi_strtolower( $term ) );
47
+ } else {
48
+ $term = rawurlencode( strip_tags( relevanssi_strtolower( $content ) ) );
49
  }
50
+
51
+ if ( 'not' !== $phrase ) {
52
+ $term = '%22' . $term . '%22';
53
  }
 
 
 
 
 
54
 
55
+ $link = get_bloginfo( 'url' ) . "/?s=$term";
56
+ $pre = "<a rel='nofollow' href='$link'>"; // rel='nofollow' for Google.
57
+ $post = '</a>';
58
+
59
+ return $pre . do_shortcode( $content ) . $post;
60
  }
61
 
62
+ /**
63
+ * Does nothing.
64
+ *
65
+ * In normal use, the [noindex] shortcode does nothing.
66
+ *
67
+ * @param array $atts The shortcode attributes. Not used.
68
+ * @param string $content The content inside the shortcode tags.
69
+ *
70
+ * @return string The shortcode content.
71
+ */
72
+ function relevanssi_noindex_shortcode( $atts, $content ) {
73
+ return do_shortcode( $content );
74
  }
75
 
76
+ /**
77
+ * Returns nothing.
78
+ *
79
+ * During indexing, the [noindex] shortcode returns nothing.
80
+ *
81
+ * @param array $atts The shortcode attributes. Not used.
82
+ * @param string $content The content inside the shortcode tags.
83
+ *
84
+ * @return string An empty string.
85
+ */
86
+ function relevanssi_noindex_shortcode_indexing( $atts, $content ) {
87
  return '';
88
  }
89
 
90
+ /**
91
+ * Returns a search form.
92
+ *
93
+ * Returns a search form generated by get_search_form(). Any attributes passed to the
94
+ * shortcode will be passed onto the search form, for example like this:
95
+ *
96
+ * [searchform post_types='post,product']
97
+ *
98
+ * This would add a
99
+ *
100
+ * <input type="hidden" name="post_types" value="post,product" />
101
+ *
102
+ * to the search form.
103
+ *
104
+ * @param array $atts The shortcode attributes.
105
+ *
106
+ * @return string A search form.
107
+ */
108
+ function relevanssi_search_form( $atts ) {
109
+ $form = get_search_form( false );
110
+ if ( is_array( $atts ) ) {
111
+ $additional_fields = array();
112
+ foreach ( $atts as $key => $value ) {
113
+ $key = esc_attr( $key );
114
+ $value = esc_attr( $value );
115
 
116
+ $additional_fields[] = "<input type='hidden' name='$key' value='$value' />";
117
+ }
118
+ $form = str_replace( '</form>', implode( "\n", $additional_fields ) . '</form>', $form );
119
+ }
120
+ return $form;
121
+ }
lib/stopwords.php CHANGED
@@ -1,40 +1,65 @@
1
  <?php
 
 
 
 
 
 
 
 
2
 
3
- // Reads automatically the correct stopwords for the current language set in WPLANG.
 
 
 
 
 
4
  function relevanssi_populate_stopwords() {
5
  global $wpdb, $relevanssi_variables;
6
 
7
- $lang = get_option('WPLANG');
8
- if (empty($lang) && defined('WPLANG') && WPLANG != '') {
9
  $lang = WPLANG;
10
  }
11
- if (empty($lang)) $lang = "en_GB";
12
-
13
- if (file_exists($relevanssi_variables['plugin_dir'] . 'stopwords/stopwords.' . $lang)) {
14
- include($relevanssi_variables['plugin_dir'] . 'stopwords/stopwords.' . $lang);
15
-
16
- if (is_array($stopwords) && count($stopwords) > 0) {
17
- foreach ($stopwords as $word) {
18
- $q = $wpdb->prepare("INSERT IGNORE INTO " . $relevanssi_variables['stopword_table'] . " (stopword) VALUES (%s)", trim($word));
19
- $wpdb->query($q);
 
20
  }
21
  }
22
  }
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
25
  function relevanssi_fetch_stopwords() {
26
  global $wpdb, $relevanssi_variables;
27
-
28
- if (!isset($relevanssi_variables['stopword_list'])) $relevanssi_variables['stopword_list'] = array();
29
-
30
- if (count($relevanssi_variables['stopword_list']) < 1) {
31
- $results = $wpdb->get_results("SELECT stopword FROM " . $relevanssi_variables['stopword_table']);
32
- foreach ($results as $word) {
 
 
33
  $relevanssi_variables['stopword_list'][] = $word->stopword;
34
  }
35
  }
36
-
37
  return $relevanssi_variables['stopword_list'];
38
  }
39
-
40
- ?>
1
  <?php
2
+ /**
3
+ * /lib/stopwords.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
 
11
+ /**
12
+ * Reads automatically the correct stopwords for the current language set in WPLANG.
13
+ *
14
+ * @global object $wpdb The WordPress database interface.
15
+ * @global array $relevanssi_variables The global Relevanssi variables array.
16
+ */
17
  function relevanssi_populate_stopwords() {
18
  global $wpdb, $relevanssi_variables;
19
 
20
+ $lang = get_option( 'WPLANG' );
21
+ if ( empty( $lang ) && defined( 'WPLANG' ) && '' !== WPLANG ) {
22
  $lang = WPLANG;
23
  }
24
+ if ( empty( $lang ) ) {
25
+ $lang = 'en_US';
26
+ }
27
+
28
+ if ( file_exists( $relevanssi_variables['plugin_dir'] . 'stopwords/stopwords.' . $lang ) ) {
29
+ include $relevanssi_variables['plugin_dir'] . 'stopwords/stopwords.' . $lang;
30
+
31
+ if ( is_array( $stopwords ) && count( $stopwords ) > 0 ) {
32
+ foreach ( $stopwords as $word ) {
33
+ $wpdb->query( $wpdb->prepare( 'INSERT IGNORE INTO ' . $relevanssi_variables['stopword_table'] . ' (stopword) VALUES (%s)', trim( $word ) ) ); // WPCS: unprepared SQL ok.
34
  }
35
  }
36
  }
37
  }
38
 
39
+ /**
40
+ * Fetches the list of stopwords.
41
+ *
42
+ * Gets the list of stopwords from $relevanssi_variables, but if it's empty, fills
43
+ * the array from the database table.
44
+ *
45
+ * @global object $wpdb The WordPress database interface.
46
+ * @global array $relevanssi_variables The global Relevanssi variables array.
47
+ *
48
+ * @return array An array of stopwords.
49
+ */
50
  function relevanssi_fetch_stopwords() {
51
  global $wpdb, $relevanssi_variables;
52
+
53
+ if ( ! isset( $relevanssi_variables['stopword_list'] ) ) {
54
+ $relevanssi_variables['stopword_list'] = array();
55
+ }
56
+
57
+ if ( count( $relevanssi_variables['stopword_list'] ) < 1 ) {
58
+ $results = $wpdb->get_results( 'SELECT stopword FROM ' . $relevanssi_variables['stopword_table'] ); // WPCS: unprepared SQL ok.
59
+ foreach ( $results as $word ) {
60
  $relevanssi_variables['stopword_list'][] = $word->stopword;
61
  }
62
  }
63
+
64
  return $relevanssi_variables['stopword_list'];
65
  }
 
 
lib/uninstall.php CHANGED
@@ -1,30 +1,131 @@
1
  <?php
 
 
 
 
 
 
 
 
2
 
3
- function relevanssi_clear_database_tables() {
 
 
 
 
 
 
 
4
  global $wpdb;
5
 
6
- if (defined('RELEVANSSI_PREMIUM')) return; // Relevanssi Premium exists, do not delete the tables
 
 
 
7
 
8
- wp_clear_scheduled_hook('relevanssi_truncate_cache');
9
 
10
- $relevanssi_table = $wpdb->prefix . "relevanssi";
11
- $stopword_table = $wpdb->prefix . "relevanssi_stopwords";
12
- $log_table = $wpdb->prefix . "relevanssi_log";
13
 
14
- if($wpdb->get_var("SHOW TABLES LIKE '$stopword_table'") == $stopword_table) {
15
- $sql = "DROP TABLE $stopword_table";
16
- $wpdb->query($sql);
17
  }
18
 
19
- if($wpdb->get_var("SHOW TABLES LIKE '$relevanssi_table'") == $relevanssi_table) {
20
- $sql = "DROP TABLE $relevanssi_table";
21
- $wpdb->query($sql);
22
  }
23
 
24
- if($wpdb->get_var("SHOW TABLES LIKE '$log_table'") == $log_table) {
25
- $sql = "DROP TABLE $log_table";
26
- $wpdb->query($sql);
27
  }
28
  }
29
 
30
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ /**
3
+ * /lib/uninstall.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
 
11
+ /**
12
+ * Drops the database tables.
13
+ *
14
+ * Drops the Relevanssi database tables
15
+ *
16
+ * @global $wpdb The WordPress database interface.
17
+ */
18
+ function relevanssi_drop_database_tables() {
19
  global $wpdb;
20
 
21
+ if ( defined( 'RELEVANSSI_PREMIUM' ) && RELEVANSSI_PREMIUM && ! defined( 'UNINSTALLING_RELEVANSSI_PREMIUM' ) ) {
22
+ // Relevanssi Premium exists, do not drop the tables.
23
+ return;
24
+ }
25
 
26
+ wp_clear_scheduled_hook( 'relevanssi_truncate_cache' );
27
 
28
+ $relevanssi_table = $wpdb->prefix . 'relevanssi';
29
+ $stopword_table = $wpdb->prefix . 'relevanssi_stopwords';
30
+ $log_table = $wpdb->prefix . 'relevanssi_log';
31
 
32
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '$stopword_table'" ) === $stopword_table ) { // WPCS: unprepared SQL ok.
33
+ $wpdb->query( "DROP TABLE $stopword_table" ); // WPCS: unprepared SQL ok.
 
34
  }
35
 
36
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '$relevanssi_table'" ) === $relevanssi_table ) { // WPCS: unprepared SQL ok.
37
+ $wpdb->query( "DROP TABLE $relevanssi_table" ); // WPCS: unprepared SQL ok.
 
38
  }
39
 
40
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '$log_table'" ) === $log_table ) { // WPCS: unprepared SQL ok.
41
+ $wpdb->query( "DROP TABLE $log_table" ); // WPCS: unprepared SQL ok.
 
42
  }
43
  }
44
 
45
+ /**
46
+ * Uninstalls Relevanssi.
47
+ *
48
+ * Deletes all options and removes database tables.
49
+ *
50
+ * @global object $wpdb The WordPress database interface.
51
+ */
52
+ function relevanssi_uninstall_free() {
53
+ delete_option( 'relevanssi_admin_search' );
54
+ delete_option( 'relevanssi_bg_col' );
55
+ delete_option( 'relevanssi_cat' );
56
+ delete_option( 'relevanssi_comment_boost' );
57
+ delete_option( 'relevanssi_css' );
58
+ delete_option( 'relevanssi_class' );
59
+ delete_option( 'relevanssi_content_boost' );
60
+ delete_option( 'relevanssi_db_version' );
61
+ delete_option( 'relevanssi_default_orderby' );
62
+ delete_option( 'relevanssi_disable_or_fallback' );
63
+ delete_option( 'relevanssi_disable_shortcodes' );
64
+ delete_option( 'relevanssi_doc_count' );
65
+ delete_option( 'relevanssi_exact_match_bonus' );
66
+ delete_option( 'relevanssi_excat' );
67
+ delete_option( 'relevanssi_extag' );
68
+ delete_option( 'relevanssi_excerpt_length' );
69
+ delete_option( 'relevanssi_excerpt_type' );
70
+ delete_option( 'relevanssi_excerpt_allowable_tags' );
71
+ delete_option( 'relevanssi_excerpt_custom_fields' );
72
+ delete_option( 'relevanssi_excerpts' );
73
+ delete_option( 'relevanssi_exclude_posts' );
74
+ delete_option( 'relevanssi_expand_shortcodes' );
75
+ delete_option( 'relevanssi_fuzzy' );
76
+ delete_option( 'relevanssi_hide_branding' );
77
+ delete_option( 'relevanssi_highlight_comments' );
78
+ delete_option( 'relevanssi_highlight_docs_external' );
79
+ delete_option( 'relevanssi_highlight_docs' );
80
+ delete_option( 'relevanssi_highlight' );
81
+ delete_option( 'relevanssi_hilite_title' );
82
+ delete_option( 'relevanssi_implicit_operator' );
83
+ delete_option( 'relevanssi_index' );
84
+ delete_option( 'relevanssi_index_author' );
85
+ delete_option( 'relevanssi_index_comments' );
86
+ delete_option( 'relevanssi_index_drafts' );
87
+ delete_option( 'relevanssi_index_excerpt' );
88
+ delete_option( 'relevanssi_index_fields' );
89
+ delete_option( 'relevanssi_index_limit' );
90
+ delete_option( 'relevanssi_index_post_types' );
91
+ delete_option( 'relevanssi_index_taxonomies' );
92
+ delete_option( 'relevanssi_index_taxonomies_list' );
93
+ delete_option( 'relevanssi_index_terms' );
94
+ delete_option( 'relevanssi_indexed' );
95
+ delete_option( 'relevanssi_link_boost' );
96
+ delete_option( 'relevanssi_log_queries' );
97
+ delete_option( 'relevanssi_log_queries_with_ip' );
98
+ delete_option( 'relevanssi_min_word_length' );
99
+ delete_option( 'relevanssi_omit_from_logs' );
100
+ delete_option( 'relevanssi_polylang_all_languages' );
101
+ delete_option( 'relevanssi_post_type_weights' );
102
+ delete_option( 'relevanssi_punctuation' );
103
+ delete_option( 'relevanssi_respect_exclude' );
104
+ delete_option( 'relevanssi_show_matches_text' );
105
+ delete_option( 'relevanssi_show_matches' );
106
+ delete_option( 'relevanssi_show_post_controls' );
107
+ delete_option( 'relevanssi_synonyms' );
108
+ delete_option( 'relevanssi_thousand_separator' );
109
+ delete_option( 'relevanssi_throttle' );
110
+ delete_option( 'relevanssi_throttle_limit' );
111
+ delete_option( 'relevanssi_title_boost' );
112
+ delete_option( 'relevanssi_txt_col' );
113
+ delete_option( 'relevanssi_word_boundaries' );
114
+ delete_option( 'relevanssi_wpml_only_current' );
115
+
116
+ // Unused options, removed in case they are still left.
117
+ delete_option( 'relevanssi_cache_seconds' );
118
+ delete_option( 'relevanssi_custom_types' );
119
+ delete_option( 'relevanssi_enable_cache' );
120
+ delete_option( 'relevanssi_hidesponsor' );
121
+ delete_option( 'relevanssi_index_attachments' );
122
+ delete_option( 'relevanssi_index_type' );
123
+ delete_option( 'relevanssi_show_matches_txt' );
124
+ delete_option( 'relevanssi_tag_boost' );
125
+ delete_option( 'relevanssi_include_cats' );
126
+ delete_option( 'relevanssi_include_tags' );
127
+ delete_option( 'relevanssi_custom_taxonomies' );
128
+ delete_option( 'relevanssi_taxonomies_to_index' );
129
+
130
+ relevanssi_drop_database_tables();
131
+ }
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: search, relevance, better search
5
  Requires at least: 4.0
6
  Tested up to: 5.0
7
  Requires PHP: 5.6
8
- Stable tag: 4.0.5
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -129,6 +129,14 @@ Each document database is full of useless words. All the little words that appea
129
 
130
  == Changelog ==
131
 
 
 
 
 
 
 
 
 
132
  = 4.0.5 =
133
  * Relevanssi code has been reviewed and modified to follow WordPress coding standards. As a result, there have been minor improvements all around the code to make things more robust and secure.
134
  * Custom field detail is no longer serialized. It's now JSON. If you use custom field detail, rebuild the index and change your code to use json_decode() instead of unserialize().
@@ -140,5 +148,8 @@ Each document database is full of useless words. All the little words that appea
140
 
141
  == Upgrade notice ==
142
 
143
- = 4.0.4 =
 
 
 
144
  * Codebase review, lots of small improvements everywhere.
5
  Requires at least: 4.0
6
  Tested up to: 5.0
7
  Requires PHP: 5.6
8
+ Stable tag: 4.0.6
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
129
 
130
  == Changelog ==
131
 
132
+ = 4.0.6 =
133
+ * Indexing bugs squashed.
134
+ * Missing tag and category weight settings returned.
135
+ * Fusion builder shortcodes are removed from excerpts.
136
+ * MemberPress post control was backwards.
137
+ * User searches page reset buttons fixed.
138
+ * WPML language filter fix.
139
+
140
  = 4.0.5 =
141
  * Relevanssi code has been reviewed and modified to follow WordPress coding standards. As a result, there have been minor improvements all around the code to make things more robust and secure.
142
  * Custom field detail is no longer serialized. It's now JSON. If you use custom field detail, rebuild the index and change your code to use json_decode() instead of unserialize().
148
 
149
  == Upgrade notice ==
150
 
151
+ = 4.0.6 =
152
+ * Indexing bugs fixed and WPML support corrected.
153
+
154
+ = 4.0.5 =
155
  * Codebase review, lots of small improvements everywhere.
relevanssi.php CHANGED
@@ -1,65 +1,70 @@
1
  <?php
2
- /*
3
- Plugin Name: Relevanssi
4
- Plugin URI: https://www.relevanssi.com/
5
- Description: This plugin replaces WordPress search with a relevance-sorting search.
6
- Version: 4.0.4
7
- Author: Mikko Saari
8
- Author URI: http://www.mikkosaari.fi/
9
- Text Domain: relevanssi
10
- */
 
 
 
 
 
 
 
 
 
 
11
 
12
- /* Copyright 2018 Mikko Saari (email: mikko@mikkosaari.fi)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- This file is part of Relevanssi, a search plugin for WordPress.
15
 
16
- Relevanssi is free software: you can redistribute it and/or modify
17
- it under the terms of the GNU General Public License as published by
18
- the Free Software Foundation, either version 3 of the License, or
19
- (at your option) any later version.
20
-
21
- Relevanssi is distributed in the hope that it will be useful,
22
- but WITHOUT ANY WARRANTY; without even the implied warranty of
23
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
- GNU General Public License for more details.
25
-
26
- You should have received a copy of the GNU General Public License
27
- along with Relevanssi. If not, see <http://www.gnu.org/licenses/>.
28
- */
29
-
30
- // For debugging purposes
31
- // error_reporting(E_ALL);
32
- // ini_set("display_errors", 1);
33
- // define('WP-DEBUG', true);
34
-
35
- define('RELEVANSSI_PREMIUM', false);
36
-
37
- add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'relevanssi_action_links');
38
 
39
  global $relevanssi_variables;
40
  global $wpdb;
41
 
42
- $relevanssi_variables['relevanssi_table'] = $wpdb->prefix . "relevanssi";
43
- $relevanssi_variables['stopword_table'] = $wpdb->prefix . "relevanssi_stopwords";
44
- $relevanssi_variables['log_table'] = $wpdb->prefix . "relevanssi_log";
45
- $relevanssi_variables['content_boost_default'] = 1;
46
- $relevanssi_variables['comment_boost_default'] = 0.75;
47
- $relevanssi_variables['title_boost_default'] = 5;
48
- $relevanssi_variables['comment_boost_default'] = 0.75;
49
  $relevanssi_variables['post_type_weight_defaults']['post_tag'] = 0.75;
50
  $relevanssi_variables['post_type_weight_defaults']['category'] = 0.75;
51
- $relevanssi_variables['post_type_index_defaults'] = array('post', 'page');
52
- $relevanssi_variables['database_version'] = 5;
53
- $relevanssi_variables['file'] = __FILE__;
54
- $relevanssi_variables['plugin_dir'] = plugin_dir_path(__FILE__);
55
 
56
- require_once('lib/install.php');
57
- require_once('lib/init.php');
58
- require_once('lib/interface.php');
59
- require_once('lib/indexing.php');
60
- require_once('lib/stopwords.php');
61
- require_once('lib/search.php');
62
- require_once('lib/excerpts-highlights.php');
63
- require_once('lib/shortcodes.php');
64
- require_once('lib/common.php');
65
- require_once('lib/admin_ajax.php');
1
  <?php
2
+ /**
3
+ * Relevanssi
4
+ *
5
+ * /relevanssi.php
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
+ * @wordpress-plugin
13
+ * Plugin Name: Relevanssi
14
+ * Plugin URI: https://www.relevanssi.com/
15
+ * Description: This plugin replaces WordPress search with a relevance-sorting search.
16
+ * Version: 4.0.5
17
+ * Author: Mikko Saari
18
+ * Author URI: http://www.mikkosaari.fi/
19
+ * Text Domain: relevanssi
20
+ */
21
 
22
+ /**
23
+ * Copyright 2018 Mikko Saari (email: mikko@mikkosaari.fi)
24
+ * This file is part of Relevanssi, a search plugin for WordPress.
25
+ *
26
+ * Relevanssi is free software: you can redistribute it and/or modify
27
+ * it under the terms of the GNU General Public License as published by
28
+ * the Free Software Foundation, either version 3 of the License, or
29
+ * (at your option) any later version.
30
+ *
31
+ * Relevanssi is distributed in the hope that it will be useful,
32
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
33
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34
+ * GNU General Public License for more details.
35
+ *
36
+ * You should have received a copy of the GNU General Public License
37
+ * along with Relevanssi. If not, see <http://www.gnu.org/licenses/>.
38
+ */
39
 
40
+ define( 'RELEVANSSI_PREMIUM', false );
41
 
42
+ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'relevanssi_action_links' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  global $relevanssi_variables;
45
  global $wpdb;
46
 
47
+ $relevanssi_variables['relevanssi_table'] = $wpdb->prefix . 'relevanssi';
48
+ $relevanssi_variables['stopword_table'] = $wpdb->prefix . 'relevanssi_stopwords';
49
+ $relevanssi_variables['log_table'] = $wpdb->prefix . 'relevanssi_log';
50
+ $relevanssi_variables['content_boost_default'] = 1;
51
+ $relevanssi_variables['comment_boost_default'] = 0.75;
52
+ $relevanssi_variables['title_boost_default'] = 5;
53
+ $relevanssi_variables['comment_boost_default'] = 0.75;
54
  $relevanssi_variables['post_type_weight_defaults']['post_tag'] = 0.75;
55
  $relevanssi_variables['post_type_weight_defaults']['category'] = 0.75;
56
+ $relevanssi_variables['post_type_index_defaults'] = array( 'post', 'page' );
57
+ $relevanssi_variables['database_version'] = 5;
58
+ $relevanssi_variables['file'] = __FILE__;
59
+ $relevanssi_variables['plugin_dir'] = plugin_dir_path( __FILE__ );
60
 
61
+ require_once 'lib/install.php';
62
+ require_once 'lib/init.php';
63
+ require_once 'lib/interface.php';
64
+ require_once 'lib/indexing.php';
65
+ require_once 'lib/stopwords.php';
66
+ require_once 'lib/search.php';
67
+ require_once 'lib/excerpts-highlights.php';
68
+ require_once 'lib/shortcodes.php';
69
+ require_once 'lib/common.php';
70
+ require_once 'lib/admin-ajax.php';
relevanssi.po CHANGED
@@ -2,7 +2,7 @@ msgid ""
2
  msgstr ""
3
  "Project-Id-Version: Relevanssi\n"
4
  "Report-Msgid-Bugs-To: \n"
5
- "POT-Creation-Date: 2017-11-30 11:49+0200\n"
6
  "PO-Revision-Date: \n"
7
  "Last-Translator: Mikko Saari <mikko@mikkosaari.fi>\n"
8
  "Language-Team: \n"
@@ -10,1410 +10,172 @@ msgstr ""
10
  "MIME-Version: 1.0\n"
11
  "Content-Type: text/plain; charset=UTF-8\n"
12
  "Content-Transfer-Encoding: 8bit\n"
13
- "X-Poedit-KeywordsList: _e;__\n"
14
  "X-Poedit-Basepath: .\n"
15
- "X-Generator: Poedit 2.0.4\n"
16
  "X-Poedit-SearchPath-0: .\n"
 
17
 
18
- #: lib/admin_ajax.php:61
19
- msgid "User indexing is disabled."
20
- msgstr ""
21
-
22
- #: lib/excerpts-highlights.php:11
23
- msgid "There is no excerpt because this is a protected post."
24
- msgstr ""
25
-
26
- #: lib/indexing.php:225
27
- msgid "Indexing complete!"
28
- msgstr ""
29
-
30
- #: lib/indexing.php:228
31
- msgid "More to index..."
32
- msgstr ""
33
-
34
- #: lib/init.php:44
35
- msgid ""
36
- "You do not have an index! Remember to build the index (click the \"Build the "
37
- "index\" button), otherwise searching won't work."
38
- msgstr ""
39
-
40
- #: lib/init.php:60
41
- msgid ""
42
- "Multibyte string functions are not available. Relevanssi may not work well "
43
- "without them. Please install (or ask your host to install) the mbstring "
44
- "extension."
45
- msgstr ""
46
-
47
- #: lib/init.php:99 lib/init.php:100 lib/interface.php:1196
48
- msgid "User searches"
49
- msgstr ""
50
-
51
- #: lib/init.php:231
52
- msgid "Settings"
53
- msgstr ""
54
-
55
- #: lib/init.php:234
56
- msgid "Go Premium!"
57
- msgstr ""
58
-
59
- #: lib/interface.php:6
60
- msgid "Relevanssi Premium Search Options"
61
- msgstr ""
62
-
63
- #: lib/interface.php:9
64
- msgid "Relevanssi Search Options"
65
- msgstr ""
66
-
67
- #: lib/interface.php:78
68
- msgid "User Searches"
69
- msgstr ""
70
-
71
- #: lib/interface.php:80
72
- msgid "Relevanssi User Searches"
73
- msgstr ""
74
-
75
- #: lib/interface.php:105
76
- msgid "Enable query logging to see stats here."
77
- msgstr ""
78
-
79
- #: lib/interface.php:119
80
- msgid "Logs clear!"
81
- msgstr ""
82
-
83
- #: lib/interface.php:122
84
- msgid "Clearing the logs failed."
85
- msgstr ""
86
-
87
- #: lib/interface.php:388
88
- #, php-format
89
- msgid ""
90
- "<div id='message' class='updated fade'><p>Successfully added %d/%d terms to "
91
- "stopwords!</p></div>"
92
- msgstr ""
93
-
94
- #: lib/interface.php:395
95
- #, php-format
96
- msgid ""
97
- "<div id='message' class='updated fade'><p>Term '%s' added to stopwords!</p></"
98
- "div>"
99
- msgstr ""
100
-
101
- #: lib/interface.php:398
102
- #, php-format
103
- msgid ""
104
- "<div id='message' class='updated fade'><p>Couldn't add term '%s' to "
105
- "stopwords!</p></div>"
106
- msgstr ""
107
-
108
- #: lib/interface.php:437
109
- msgid ""
110
- "<div id='message' class='updated fade'><p>Stopwords removed! Remember to re-"
111
- "index.</p></div>"
112
- msgstr ""
113
-
114
- #: lib/interface.php:449
115
- #, php-format
116
- msgid "Term '%s' removed from stopwords! Re-index to get it back to index."
117
- msgstr ""
118
-
119
- #: lib/interface.php:459
120
- #, php-format
121
- msgid "Couldn't remove term '%s' from stopwords!"
122
- msgstr ""
123
-
124
- #: lib/interface.php:479
125
- msgid "25 most common words in the index"
126
- msgstr ""
127
-
128
- #: lib/interface.php:480
129
- msgid ""
130
- "These words are excellent stopword material. A word that appears in most of "
131
- "the posts in the database is quite pointless when searching. This is also an "
132
- "easy way to create a completely new stopword list, if one isn't available in "
133
- "your language. Click the icon after the word to add the word to the stopword "
134
- "list. The word will also be removed from the index, so rebuilding the index "
135
- "is not necessary."
136
- msgstr ""
137
-
138
- #: lib/interface.php:486
139
- msgid "Stopword Candidates"
140
- msgstr ""
141
-
142
- #: lib/interface.php:494
143
- msgid "Add to stopwords"
144
- msgstr ""
145
-
146
- #: lib/interface.php:516
147
- msgid "Total Searches"
148
- msgstr ""
149
-
150
- #: lib/interface.php:519
151
- msgid "Totals"
152
- msgstr ""
153
-
154
- #: lib/interface.php:524
155
- msgid "Common Queries"
156
- msgstr ""
157
-
158
- #: lib/interface.php:528
159
- #, php-format
160
- msgid ""
161
- "Here you can see the %d most common user search queries, how many times "
162
- "those queries were made and how many results were found for those queries."
163
- msgstr ""
164
-
165
- #: lib/interface.php:531 lib/interface.php:547 lib/interface.php:579
166
- msgid "Today and yesterday"
167
- msgstr ""
168
-
169
- #: lib/interface.php:535 lib/interface.php:551 lib/interface.php:580
170
- msgid "Last 7 days"
171
- msgstr ""
172
-
173
- #: lib/interface.php:539 lib/interface.php:555
174
- #, php-format
175
- msgid "Last %d days"
176
- msgstr ""
177
-
178
- #: lib/interface.php:544
179
- msgid "Unsuccessful Queries"
180
- msgstr ""
181
-
182
- #: lib/interface.php:562
183
- msgid "Reset Logs"
184
- msgstr ""
185
-
186
- #: lib/interface.php:565
187
- #, php-format
188
- msgid "To reset the logs, type \"reset\" into the box here %s and click %s"
189
- msgstr ""
190
-
191
- #: lib/interface.php:581
192
- msgid "Last 30 days"
193
- msgstr ""
194
-
195
- #: lib/interface.php:582
196
- msgid "Forever"
197
- msgstr ""
198
-
199
- #: lib/interface.php:584
200
- msgid "When"
201
- msgstr ""
202
-
203
- #: lib/interface.php:584
204
- msgid "Searches"
205
- msgstr ""
206
-
207
- #: lib/interface.php:616
208
- msgid "Query"
209
- msgstr ""
210
-
211
- #: lib/interface.php:616
212
- msgid "Hits"
213
- msgstr ""
214
-
215
- #: lib/interface.php:1063
216
- msgid "Overview"
217
- msgstr ""
218
-
219
- #: lib/interface.php:1064
220
- msgid "Indexing"
221
- msgstr ""
222
-
223
- #: lib/interface.php:1065
224
- msgid "Attachments"
225
- msgstr ""
226
-
227
- #: lib/interface.php:1066 lib/interface.php:2233
228
- msgid "Searching"
229
- msgstr ""
230
-
231
- #: lib/interface.php:1067
232
- msgid "Logging"
233
- msgstr ""
234
-
235
- #: lib/interface.php:1068
236
- msgid "Excerpts and highlights"
237
- msgstr ""
238
-
239
- #: lib/interface.php:1069 lib/interface.php:2099 lib/interface.php:2104
240
- msgid "Synonyms"
241
- msgstr ""
242
-
243
- #: lib/interface.php:1070 lib/interface.php:2130
244
- msgid "Stopwords"
245
- msgstr ""
246
-
247
- #: lib/interface.php:1072
248
- msgid "Import / Export options"
249
- msgstr ""
250
-
251
- #: lib/interface.php:1102
252
- msgid "Welcome to Relevanssi!"
253
- msgstr ""
254
-
255
- #: lib/interface.php:1112
256
- msgid "Getting started"
257
- msgstr ""
258
-
259
- #: lib/interface.php:1114
260
- msgid ""
261
- "You've already installed Relevanssi. That's a great first step towards good "
262
- "search experience!"
263
- msgstr ""
264
-
265
- #: lib/interface.php:1117
266
- #, php-format
267
- msgid ""
268
- "Now, you need an index. Head over to the %s%s%s tab to set up the basic "
269
- "indexing options and to build the index."
270
- msgstr ""
271
-
272
- #: lib/interface.php:1118
273
- msgid "You need to check at least the following options:"
274
- msgstr ""
275
-
276
- #: lib/interface.php:1119
277
- msgid "Make sure the post types you want to include in the index are indexed."
278
- msgstr ""
279
-
280
- #: lib/interface.php:1120
281
- #, php-format
282
- msgid ""
283
- "Do you use custom fields to store content you want included? If so, add "
284
- "those too. WooCommerce user? You probably want to include %s."
285
- msgstr ""
286
-
287
- #: lib/interface.php:1121
288
- msgid ""
289
- "Then just save the options and build the index. First time you have to do it "
290
- "manually, but after that, it's fully automatic: all changes are reflected in "
291
- "the index without reindexing. (That said, it's a good idea to rebuild the "
292
- "index once a year.)"
293
- msgstr ""
294
-
295
- #: lib/interface.php:1124
296
- msgid "Great, you already have an index!"
297
- msgstr ""
298
-
299
- #: lib/interface.php:1127
300
- #, php-format
301
- msgid ""
302
- "On the %s%s%s tab, choose whether you want the default operator to be AND "
303
- "(less results, but more precise) or OR (more results, less precise)."
304
- msgstr ""
305
-
306
- #: lib/interface.php:1130
307
- #, php-format
308
- msgid ""
309
- "The next step is the %s%s%s tab, where you can enable the custom excerpts "
310
- "that show the relevant part of post in the search results pages."
311
- msgstr ""
312
-
313
- #: lib/interface.php:1131
314
- msgid ""
315
- "There are couple of options related to that, so if you want highlighting in "
316
- "the results, you can adjust the styles for that to suit the look of your "
317
- "site."
318
- msgstr ""
319
-
320
- #: lib/interface.php:1134
321
- msgid ""
322
- "That's about it! Now you should have Relevanssi up and running. The rest of "
323
- "the options is mostly fine-tuning."
324
- msgstr ""
325
-
326
- #: lib/interface.php:1137
327
- msgid ""
328
- "Relevanssi doesn't have a separate search widget. Instead, Relevanssi uses "
329
- "the default search widget. Any standard search form will do!"
330
- msgstr ""
331
-
332
- #: lib/interface.php:1141
333
- msgid "For more information"
334
- msgstr ""
335
-
336
- #: lib/interface.php:1143
337
- msgid ""
338
- "Relevanssi uses the WordPress contextual help. Click 'Help' on the top right "
339
- "corner for more information on many Relevanssi topics."
340
- msgstr ""
341
-
342
- #: lib/interface.php:1144
343
- #, php-format
344
- msgid ""
345
- "%sRelevanssi knowledge base%s has lots of information about advanced "
346
- "Relevanssi use, including plenty of code samples."
347
- msgstr ""
348
-
349
- #: lib/interface.php:1149
350
- msgid "Relevanssi on Facebook"
351
- msgstr ""
352
-
353
- #: lib/interface.php:1152
354
- msgid ""
355
- "Check out the Relevanssi page on Facebook for news and updates about "
356
- "Relevanssi."
357
- msgstr ""
358
-
359
- #: lib/interface.php:1158
360
- msgid "Buy Relevanssi Premium"
361
- msgstr ""
362
-
363
- #: lib/interface.php:1161
364
- msgid "Buy Relevanssi Premium now"
365
- msgstr ""
366
-
367
- #: lib/interface.php:1161
368
- #, php-format
369
- msgid ""
370
- "use coupon code %s for 20%% discount (valid at least until the end of %s)"
371
- msgstr ""
372
-
373
- #: lib/interface.php:1162
374
- msgid "Here are some improvements Relevanssi Premium offers:"
375
- msgstr ""
376
-
377
- #: lib/interface.php:1164
378
- msgid "PDF content indexing"
379
- msgstr ""
380
-
381
- #: lib/interface.php:1165
382
- msgid "Index and search user profile pages"
383
- msgstr ""
384
-
385
- #: lib/interface.php:1166
386
- msgid "Index and search taxonomy term pages"
387
- msgstr ""
388
-
389
- #: lib/interface.php:1167
390
- msgid "Multisite searches across many subsites"
391
- msgstr ""
392
-
393
- #: lib/interface.php:1168
394
- msgid "WP CLI commands"
395
- msgstr ""
396
-
397
- #: lib/interface.php:1169
398
- msgid "Adjust weights separately for each post type and taxonomy"
399
- msgstr ""
400
-
401
- #: lib/interface.php:1170
402
- msgid "Internal link anchors can be search terms for the target posts"
403
- msgstr ""
404
-
405
- #: lib/interface.php:1171
406
- msgid "Index and search any columns in the wp_posts database"
407
- msgstr ""
408
-
409
- #: lib/interface.php:1172
410
- msgid ""
411
- "Hide Relevanssi branding from the User Searches page on a client installation"
412
- msgstr ""
413
-
414
- #: lib/interface.php:1186
415
- msgid "Enable logs"
416
- msgstr ""
417
-
418
- #: lib/interface.php:1190 lib/interface.php:1193
419
- msgid "Keep a log of user queries."
420
- msgstr ""
421
-
422
- #: lib/interface.php:1196
423
- #, php-format
424
- msgid ""
425
- "If enabled, Relevanssi will log user queries. The logs can be examined under "
426
- "'%s' on the Dashboard admin menu and are stored in the %s database table."
427
- msgstr ""
428
-
429
- #: lib/interface.php:1201
430
- msgid "Log user IP"
431
- msgstr ""
432
-
433
- #: lib/interface.php:1205 lib/interface.php:1208
434
- msgid "Log the user's IP with the queries."
435
- msgstr ""
436
-
437
- #: lib/interface.php:1211
438
- msgid "If enabled, Relevanssi will log user's IP adress with the queries."
439
- msgstr ""
440
-
441
- #: lib/interface.php:1216
442
- msgid "Exclude users"
443
- msgstr ""
444
-
445
- #: lib/interface.php:1220
446
- msgid ""
447
- "Comma-separated list of numeric user IDs or user login names that will not "
448
- "be logged."
449
- msgstr ""
450
-
451
- #: lib/interface.php:1226
452
- msgid "Trim logs"
453
- msgstr ""
454
-
455
- #: lib/interface.php:1230
456
- msgid "How many days of logs to keep in the database."
457
- msgstr ""
458
-
459
- #: lib/interface.php:1231
460
- #, php-format
461
- msgid " Set to %d for no trimming."
462
- msgstr ""
463
-
464
- #: lib/interface.php:1246
465
- msgid "Default operator"
466
- msgstr ""
467
-
468
- #: lib/interface.php:1250
469
- msgid "AND - require all terms"
470
- msgstr ""
471
-
472
- #: lib/interface.php:1251
473
- msgid "OR - any term present is enough"
474
- msgstr ""
475
-
476
- #: lib/interface.php:1253
477
- msgid "This setting determines the default operator for the search."
478
- msgstr ""
479
-
480
- #: lib/interface.php:1254
481
- #, php-format
482
- msgid ""
483
- "You can override this setting with the %s query parameter, like this: %s"
484
- msgstr ""
485
-
486
- #: lib/interface.php:1259
487
- msgid "Fallback to OR"
488
- msgstr ""
489
-
490
- #: lib/interface.php:1263 lib/interface.php:1266
491
- msgid "Disable the OR fallback."
492
- msgstr ""
493
-
494
- #: lib/interface.php:1269
495
- msgid ""
496
- "By default, if AND search fails to find any results, Relevanssi will switch "
497
- "the operator to OR and run the search again. You can prevent that by "
498
- "checking this option."
499
- msgstr ""
500
-
501
- #: lib/interface.php:1274
502
- msgid "Default order"
503
- msgstr ""
504
-
505
- #: lib/interface.php:1278
506
- msgid "Relevance (highly recommended)"
507
- msgstr ""
508
-
509
- #: lib/interface.php:1279
510
- msgid "Post date"
511
- msgstr ""
512
-
513
- #: lib/interface.php:1281
514
- #, php-format
515
- msgid ""
516
- "If you want to override this or use multi-layered ordering (eg. first order "
517
- "by relevance, but sort ties by post title), you can use the %s query "
518
- "variable. See Help for more information."
519
- msgstr ""
520
-
521
- #: lib/interface.php:1283
522
- msgid ""
523
- " If you want date-based results, see the recent post bonus in the Weights "
524
- "section."
525
- msgstr ""
526
-
527
- #: lib/interface.php:1289
528
- msgid "Keyword matching"
529
- msgstr ""
530
-
531
- #: lib/interface.php:1293
532
- msgid "Whole words"
533
- msgstr ""
534
-
535
- #: lib/interface.php:1294
536
- msgid "Partial words"
537
- msgstr ""
538
-
539
- #: lib/interface.php:1295
540
- msgid "Partial words if no hits for whole words"
541
- msgstr ""
542
-
543
- #: lib/interface.php:1297
544
- msgid ""
545
- "Whole words means Relevanssi only finds posts that include the whole search "
546
- "term."
547
- msgstr ""
548
-
549
- #: lib/interface.php:1298
550
- msgid ""
551
- "Partial words also includes cases where the word in the index begins or ends "
552
- "with the search term (searching for 'ana' will match 'anaconda' or 'banana', "
553
- "but not 'banal'). See Help, if you want to make Relevanssi match also inside "
554
- "words."
555
- msgstr ""
556
-
557
- #: lib/interface.php:1303
558
- msgid "Weights"
559
- msgstr ""
560
-
561
- #: lib/interface.php:1306
562
- msgid ""
563
- "All the weights in the table are multipliers. To increase the weight of an "
564
- "element, use a higher number. To make an element less significant, use a "
565
- "number lower than 1."
566
- msgstr ""
567
-
568
- #: lib/interface.php:1310
569
- msgid "Element"
570
- msgstr ""
571
-
572
- #: lib/interface.php:1311
573
- msgid "Weight"
574
- msgstr ""
575
-
576
- #: lib/interface.php:1316
577
- msgid "Post content"
578
- msgstr ""
579
-
580
- #: lib/interface.php:1324
581
- msgid "Post titles"
582
- msgstr ""
583
-
584
- #: lib/interface.php:1333
585
- msgid "Comment text"
586
- msgstr ""
587
-
588
- #: lib/interface.php:1353
589
- msgid "WPML"
590
- msgstr ""
591
-
592
- #: lib/interface.php:1357 lib/interface.php:1360
593
- msgid "Limit results to current language."
594
- msgstr ""
595
-
596
- #: lib/interface.php:1363
597
- msgid ""
598
- "Enabling this option will restrict the results to the currently active "
599
- "language. If the option is disabled, results will include posts in all "
600
- "languages."
601
- msgstr ""
602
-
603
- #: lib/interface.php:1370
604
- msgid "Polylang"
605
- msgstr ""
606
-
607
- #: lib/interface.php:1374 lib/interface.php:1377
608
- msgid "Allow results from all languages."
609
- msgstr ""
610
-
611
- #: lib/interface.php:1380
612
- msgid ""
613
- "By default Polylang restricts the search to the current language. Enabling "
614
- "this option will lift this restriction."
615
- msgstr ""
616
-
617
- #: lib/interface.php:1386
618
- msgid "Admin search"
619
- msgstr ""
620
-
621
- #: lib/interface.php:1390 lib/interface.php:1393
622
- msgid "Use Relevanssi for admin searches."
623
- msgstr ""
624
-
625
- #: lib/interface.php:1396
626
- msgid ""
627
- "If checked, Relevanssi will be used for searches in the admin interface. The "
628
- "page search doesn't use Relevanssi, because WordPress works like that."
629
- msgstr ""
630
-
631
- #: lib/interface.php:1401
632
- #, php-format
633
- msgid "Respect %s"
634
- msgstr ""
635
-
636
- #: lib/interface.php:1405
637
- msgid "Respect exclude_from_search for custom post types"
638
- msgstr ""
639
-
640
- #: lib/interface.php:1408
641
- #, php-format
642
- msgid "Respect %s for custom post types"
643
- msgstr ""
644
-
645
- #: lib/interface.php:1410
646
- msgid ""
647
- "If checked, Relevanssi won't display posts of custom post types that have "
648
- "'exclude_from_search' set to true."
649
- msgstr ""
650
-
651
- #: lib/interface.php:1418
652
- msgid ""
653
- "You probably should uncheck this option, because you've set Relevanssi to "
654
- "index the following non-public post types:"
655
- msgstr ""
656
-
657
- #: lib/interface.php:1427
658
- msgid "Throttle searches"
659
- msgstr ""
660
-
661
- #: lib/interface.php:1431
662
- msgid "Throttling the search does not work when sorting the posts by date."
663
- msgstr ""
664
-
665
- #: lib/interface.php:1435 lib/interface.php:1438
666
- msgid "Throttle searches."
667
- msgstr ""
668
-
669
- #: lib/interface.php:1442
670
- msgid "Your database is so small that you don't need to enable this."
671
- msgstr ""
672
-
673
- #: lib/interface.php:1444
674
- msgid ""
675
- "If this option is checked, Relevanssi will limit search results to at most "
676
- "500 results per term. This will improve performance, but may cause some "
677
- "relevant documents to go unfound. See Help for more details."
678
- msgstr ""
679
-
680
- #: lib/interface.php:1450
681
- msgid "Category restriction"
682
- msgstr ""
683
-
684
- #: lib/interface.php:1466
685
- msgid ""
686
- "You can restrict search results to a category for all searches. For "
687
- "restricting on a per-search basis and more options (eg. tag restrictions), "
688
- "see Help."
689
- msgstr ""
690
-
691
- #: lib/interface.php:1471
692
- msgid "Category exclusion"
693
- msgstr ""
694
-
695
- #: lib/interface.php:1487
696
- msgid ""
697
- "Posts in these categories are not included in search results. To exclude the "
698
- "posts completely from the index, see Help."
699
- msgstr ""
700
-
701
- #: lib/interface.php:1492
702
- msgid "Post exclusion"
703
- msgstr ""
704
-
705
- #: lib/interface.php:1496
706
- msgid ""
707
- "Enter a comma-separated list of post or page ID's to exclude those pages "
708
- "from the search results."
709
- msgstr ""
710
-
711
- #: lib/interface.php:1498
712
- msgid ""
713
- "With Relevanssi Premium, it's better to use the check box on post edit "
714
- "pages. That will remove the posts completely from the index, and will work "
715
- "with multisite searches unlike this setting."
716
- msgstr ""
717
-
718
- #: lib/interface.php:1508
719
- msgid "Custom excerpts/snippets"
720
- msgstr ""
721
-
722
- #: lib/interface.php:1513
723
- msgid "Custom search result snippets"
724
- msgstr ""
725
-
726
- #: lib/interface.php:1517
727
- msgid "Create custom search results snippets"
728
- msgstr ""
729
-
730
- #: lib/interface.php:1520
731
- msgid "Create custom search result snippets"
732
- msgstr ""
733
-
734
- #: lib/interface.php:1523
735
- msgid "Only enable this if you actually use the custom excerpts."
736
- msgstr ""
737
-
738
- #: lib/interface.php:1528
739
- msgid "Length of the snippet"
740
- msgstr ""
741
-
742
- #: lib/interface.php:1533
743
- msgid "characters"
744
- msgstr ""
745
-
746
- #: lib/interface.php:1534
747
- msgid "words"
748
- msgstr ""
749
-
750
- #: lib/interface.php:1536
751
- msgid ""
752
- "Using words is much faster than characters. Don't use characters, unless you "
753
- "have a really good reason and your posts are short."
754
- msgstr ""
755
-
756
- #: lib/interface.php:1541
757
- msgid "Allowable tags in excerpts"
758
- msgstr ""
759
-
760
- #: lib/interface.php:1545
761
- msgid ""
762
- "List all tags you want to allow in excerpts. For example: &lt;p&gt;&lt;a&gt;"
763
- "&lt;strong&gt;."
764
- msgstr ""
765
-
766
- #: lib/interface.php:1550
767
- msgid "Use custom fields for excerpts"
768
- msgstr ""
769
-
770
- #: lib/interface.php:1554 lib/interface.php:1557
771
- msgid "Use custom field content for building excerpts"
772
- msgstr ""
773
-
774
- #: lib/interface.php:1560
775
- msgid ""
776
- "Use the custom fields setting for indexing for excerpt-making as well. "
777
- "Enabling this option will show custom field content in Relevanssi-generated "
778
- "excerpts."
779
- msgstr ""
780
-
781
- #: lib/interface.php:1562
782
- msgid "Current custom field setting"
783
- msgstr ""
784
-
785
- #: lib/interface.php:1564
786
- msgid "all visible custom fields"
787
- msgstr ""
788
-
789
- #: lib/interface.php:1565
790
- msgid "all custom fields"
791
- msgstr ""
792
-
793
- #: lib/interface.php:1567
794
- msgid "None selected"
795
- msgstr ""
796
-
797
- #: lib/interface.php:1573
798
- msgid "Search hit highlighting"
799
- msgstr ""
800
-
801
- #: lib/interface.php:1578
802
- msgid "Highlight type"
803
- msgstr ""
804
-
805
- #: lib/interface.php:1582
806
- msgid "No highlighting"
807
- msgstr ""
808
-
809
- #: lib/interface.php:1586 lib/interface.php:1596
810
- msgid "Text color"
811
- msgstr ""
812
-
813
- #: lib/interface.php:1587 lib/interface.php:1604
814
- msgid "Background color"
815
- msgstr ""
816
-
817
- #: lib/interface.php:1588
818
- msgid "CSS Style"
819
- msgstr ""
820
-
821
- #: lib/interface.php:1589
822
- msgid "CSS Class"
823
  msgstr ""
824
 
825
- #: lib/interface.php:1591 lib/interface.php:1706
826
- msgid "Requires custom snippets to work."
827
  msgstr ""
828
 
829
- #: lib/interface.php:1612
830
- msgid "CSS style for highlights"
831
  msgstr ""
832
 
833
- #: lib/interface.php:1616
834
- #, php-format
835
- msgid ""
836
- "The highlights will be wrapped in a %s with this CSS in the style parameter."
837
  msgstr ""
838
 
839
- #: lib/interface.php:1621
840
- msgid "CSS class for highlights"
841
  msgstr ""
842
 
843
- #: lib/interface.php:1625
844
- #, php-format
845
- msgid "The highlights will be wrapped in a %s with this class."
846
  msgstr ""
847
 
848
- #: lib/interface.php:1630
849
- msgid "Highlight in titles"
850
  msgstr ""
851
 
852
- #: lib/interface.php:1634 lib/interface.php:1637
853
- msgid "Highlight query terms in titles"
854
  msgstr ""
855
 
856
- #: lib/interface.php:1640
857
- #, php-format
858
- msgid ""
859
- "Highlights in titles require changes to the search results template. You "
860
- "need to replace %s in the search results template with %s. For more "
861
- "information, see the contextual help."
862
  msgstr ""
863
 
864
- #: lib/interface.php:1645
865
- msgid "Highlight in documents"
866
  msgstr ""
867
 
868
- #: lib/interface.php:1649 lib/interface.php:1652
869
- msgid "Highlight query terms in documents"
870
  msgstr ""
871
 
872
- #: lib/interface.php:1655
873
  #, php-format
874
  msgid ""
875
- "Highlights hits when user opens the post from search results. This requires "
876
- "an extra parameter (%s) to the links from the search results pages so in "
877
- "order to get these highlights, you need to use %s or %s to print out the "
878
- "permalinks on the search results templates."
879
- msgstr ""
880
-
881
- #: lib/interface.php:1661
882
- msgid "Highlight in comments"
883
- msgstr ""
884
-
885
- #: lib/interface.php:1665 lib/interface.php:1668
886
- msgid "Highlight query terms in comments"
887
- msgstr ""
888
-
889
- #: lib/interface.php:1671
890
- msgid ""
891
- "Highlights hits in comments when user opens the post from search results."
892
- msgstr ""
893
-
894
- #: lib/interface.php:1676
895
- msgid "Highlighting problems with non-ASCII alphabet?"
896
- msgstr ""
897
-
898
- #: lib/interface.php:1680 lib/interface.php:1683
899
- msgid "Uncheck this if you use non-ASCII characters"
900
- msgstr ""
901
-
902
- #: lib/interface.php:1686
903
- msgid ""
904
- "If you use non-ASCII characters (like Cyrillic alphabet) and the highlights "
905
- "don't work, unchecking this option may make the highlights work."
906
- msgstr ""
907
-
908
- #: lib/interface.php:1691
909
- msgid "Breakdown of search results"
910
- msgstr ""
911
-
912
- #: lib/interface.php:1696
913
- msgid "Breakdown of search hits in excerpts"
914
- msgstr ""
915
-
916
- #: lib/interface.php:1700
917
- msgid "Show the breakdown of search hits in the excerpts"
918
- msgstr ""
919
-
920
- #: lib/interface.php:1703
921
- msgid "Show the breakdown of search hits in the excerpts."
922
  msgstr ""
923
 
924
- #: lib/interface.php:1711
925
- msgid "The breakdown format"
926
  msgstr ""
927
 
928
- #: lib/interface.php:1715
929
- msgid ""
930
- "Use %body%, %title%, %tags% and %comments% to display the number of hits (in "
931
- "different parts of the post), %total% for total hits, %score% to display the "
932
- "document weight and %terms% to show how many hits each search term got."
933
  msgstr ""
934
 
935
- #: lib/interface.php:1747
936
  #, php-format
937
- msgid "%s empties the existing index and rebuilds it from scratch."
938
- msgstr ""
939
-
940
- #: lib/interface.php:1747
941
- msgid "Build the index"
942
  msgstr ""
943
 
944
- #: lib/interface.php:1748
945
  #, php-format
946
- msgid ""
947
- "%s doesn't empty the index and only indexes those posts that are not "
948
- "indexed. You can use it if you have to interrupt building the index."
949
- msgstr ""
950
-
951
- #: lib/interface.php:1748
952
- msgid "Index unindexed posts"
953
- msgstr ""
954
-
955
- #: lib/interface.php:1749
956
- msgid "This doesn't index any taxonomy terms or users."
957
- msgstr ""
958
-
959
- #: lib/interface.php:1753
960
- msgid "Time elapsed"
961
- msgstr ""
962
-
963
- #: lib/interface.php:1753
964
- msgid "Time remaining"
965
- msgstr ""
966
-
967
- #: lib/interface.php:1753
968
- msgid "some time"
969
- msgstr ""
970
-
971
- #: lib/interface.php:1758
972
- msgid "State of the index"
973
- msgstr ""
974
-
975
- #: lib/interface.php:1766
976
- msgid "is the highest post ID indexed."
977
  msgstr ""
978
 
979
- #: lib/interface.php:1773
980
- msgid ""
981
- "WARNING: You've chosen no post types to index. Nothing will be indexed. "
982
- "Choose some post types to index."
983
- msgstr ""
984
-
985
- #: lib/interface.php:1777
986
- msgid "Indexing options"
987
- msgstr ""
988
-
989
- #: lib/interface.php:1779
990
- msgid ""
991
- "Any changes to the settings on this page require reindexing before they take "
992
- "effect."
993
- msgstr ""
994
-
995
- #: lib/interface.php:1783
996
- msgid "Post types"
997
- msgstr ""
998
-
999
- #: lib/interface.php:1789
1000
- msgid "Type"
1001
- msgstr ""
1002
-
1003
- #: lib/interface.php:1790 lib/interface.php:1852
1004
- msgid "Index"
1005
- msgstr ""
1006
-
1007
- #: lib/interface.php:1791
1008
- msgid "Excluded from search?"
1009
  msgstr ""
1010
 
1011
- #: lib/interface.php:1809 lib/interface.php:1869
1012
- msgid "no"
1013
  msgstr ""
1014
 
1015
- #: lib/interface.php:1809 lib/interface.php:1869
1016
  msgid "yes"
1017
  msgstr ""
1018
 
1019
- #: lib/interface.php:1838
1020
- #, php-format
1021
- msgid ""
1022
- "%s includes all attachment types. If you want to index only some "
1023
- "attachments, see %sControlling attachment types in the Knowledge base%s."
1024
- msgstr ""
1025
-
1026
- #: lib/interface.php:1844
1027
- msgid "Taxonomies"
1028
- msgstr ""
1029
-
1030
- #: lib/interface.php:1851
1031
- msgid "Taxonomy"
1032
- msgstr ""
1033
-
1034
- #: lib/interface.php:1853
1035
- msgid "Public?"
1036
- msgstr ""
1037
-
1038
- #: lib/interface.php:1889
1039
- msgid ""
1040
- "If you check a taxonomy here, the terms for that taxonomy are indexed with "
1041
- "the posts. If you for example choose \"post_tag\", searching for a tag will "
1042
- "find all posts that have the tag."
1043
- msgstr ""
1044
-
1045
- #: lib/interface.php:1896
1046
- msgid "Comments"
1047
- msgstr ""
1048
-
1049
- #: lib/interface.php:1900 lib/interface.php:1914
1050
- msgid "none"
1051
- msgstr ""
1052
-
1053
- #: lib/interface.php:1901
1054
- msgid "comments"
1055
- msgstr ""
1056
-
1057
- #: lib/interface.php:1902
1058
- msgid "comments and pingbacks"
1059
- msgstr ""
1060
-
1061
- #: lib/interface.php:1904
1062
- msgid ""
1063
- "If you choose to index comments, you can choose if you want to index just "
1064
- "comments, or everything including comments and track- and pingbacks."
1065
- msgstr ""
1066
-
1067
- #: lib/interface.php:1910
1068
- msgid "Custom fields"
1069
- msgstr ""
1070
-
1071
- #: lib/interface.php:1915
1072
- msgid "all"
1073
- msgstr ""
1074
-
1075
- #: lib/interface.php:1916
1076
- msgid "visible"
1077
  msgstr ""
1078
 
1079
- #: lib/interface.php:1917 lib/interface.php:1928
1080
  msgid "some"
1081
  msgstr ""
1082
 
1083
- #: lib/interface.php:1919
1084
- msgid "'All' indexes all custom fields for posts."
1085
- msgstr ""
1086
-
1087
- #: lib/interface.php:1920
1088
- msgid ""
1089
- "'Visible' only includes the custom fields that are visible in the user "
1090
- "interface (with names that don't start with an underscore)."
1091
- msgstr ""
1092
-
1093
- #: lib/interface.php:1921
1094
- msgid "'Some' lets you choose individual custom fields to index."
1095
- msgstr ""
1096
-
1097
- #: lib/interface.php:1924
1098
- msgid ""
1099
- "Enter a comma-separated list of custom fields to include in the index. With "
1100
- "Relevanssi Premium, you can also use 'fieldname_%_subfieldname' notation for "
1101
- "ACF repeater fields."
1102
- msgstr ""
1103
-
1104
- #: lib/interface.php:1925
1105
- msgid ""
1106
- "You can use 'relevanssi_index_custom_fields' filter hook to adjust which "
1107
- "custom fields are indexed."
1108
- msgstr ""
1109
-
1110
- #: lib/interface.php:1928
1111
- #, php-format
1112
- msgid ""
1113
- "If you want the SKU included, choose %s and enter %s. Also see the "
1114
- "contextual help for more details."
1115
- msgstr ""
1116
-
1117
- #: lib/interface.php:1937
1118
- msgid "Author display names"
1119
- msgstr ""
1120
-
1121
- #: lib/interface.php:1941 lib/interface.php:1944
1122
- msgid "Index the post author display name"
1123
- msgstr ""
1124
-
1125
- #: lib/interface.php:1946
1126
- msgid ""
1127
- "Searching for the post author display name will return posts by that author."
1128
- msgstr ""
1129
-
1130
- #: lib/interface.php:1953 lib/interface.php:2285
1131
- msgid "Excerpts"
1132
- msgstr ""
1133
-
1134
- #: lib/interface.php:1957 lib/interface.php:1960 lib/interface.php:1981
1135
- msgid "Index the post excerpt"
1136
- msgstr ""
1137
-
1138
- #: lib/interface.php:1962
1139
- msgid "Relevanssi will find posts by the content in the excerpt."
1140
- msgstr ""
1141
-
1142
- #: lib/interface.php:1964
1143
- msgid ""
1144
- "WooCommerce stores the product short description in the excerpt, so it's a "
1145
- "good idea to index excerpts."
1146
- msgstr ""
1147
-
1148
- #: lib/interface.php:1972
1149
- msgid "Shortcodes"
1150
- msgstr ""
1151
-
1152
- #: lib/interface.php:1977
1153
- msgid "Expand shortcodes"
1154
- msgstr ""
1155
-
1156
- #: lib/interface.php:1984
1157
- msgid "Expand shortcodes when indexing"
1158
- msgstr ""
1159
-
1160
- #: lib/interface.php:1987
1161
- msgid ""
1162
- "WooCommerce has shortcodes that don't work well with Relevanssi. With "
1163
- "WooCommerce, make sure the option is disabled."
1164
- msgstr ""
1165
-
1166
- #: lib/interface.php:1989
1167
- msgid ""
1168
- "If checked, Relevanssi will expand shortcodes in post content before "
1169
- "indexing. Otherwise shortcodes will be stripped."
1170
- msgstr ""
1171
-
1172
- #: lib/interface.php:1990
1173
- msgid ""
1174
- "If you use shortcodes to include dynamic content, Relevanssi will not keep "
1175
- "the index updated, the index will reflect the status of the shortcode "
1176
- "content at the moment of indexing."
1177
- msgstr ""
1178
-
1179
- #: lib/interface.php:2007
1180
- msgid "Advanced indexing settings"
1181
- msgstr ""
1182
-
1183
- #: lib/interface.php:2009
1184
- msgid "Show advanced settings"
1185
- msgstr ""
1186
-
1187
- #: lib/interface.php:2014
1188
- msgid "Minimum word length"
1189
- msgstr ""
1190
-
1191
- #: lib/interface.php:2018
1192
- msgid "Words shorter than this many letters will not be indexed."
1193
- msgstr ""
1194
-
1195
- #: lib/interface.php:2022
1196
- msgid "Punctuation control"
1197
- msgstr ""
1198
-
1199
- #: lib/interface.php:2023
1200
- msgid ""
1201
- "Here you can adjust how the punctuation is controlled. For more information, "
1202
- "see help. Remember that any changes here require reindexing, otherwise "
1203
- "searches will fail to find posts they should."
1204
- msgstr ""
1205
-
1206
- #: lib/interface.php:2027
1207
- msgid "Hyphens and dashes"
1208
- msgstr ""
1209
-
1210
- #: lib/interface.php:2031 lib/interface.php:2058
1211
- msgid "Keep"
1212
- msgstr ""
1213
-
1214
- #: lib/interface.php:2032 lib/interface.php:2045 lib/interface.php:2059
1215
- msgid "Replace with spaces"
1216
- msgstr ""
1217
-
1218
- #: lib/interface.php:2033 lib/interface.php:2046 lib/interface.php:2060
1219
- msgid "Remove"
1220
- msgstr ""
1221
-
1222
- #: lib/interface.php:2035
1223
- msgid ""
1224
- "How Relevanssi should handle hyphens and dashes (en and em dashes)? "
1225
- "Replacing with spaces is generally the best option, but in some cases "
1226
- "removing completely is the best option. Keeping them is rarely the best "
1227
- "option."
1228
- msgstr ""
1229
-
1230
- #: lib/interface.php:2041
1231
- msgid "Apostrophes and quotes"
1232
- msgstr ""
1233
-
1234
- #: lib/interface.php:2048
1235
- msgid ""
1236
- "How Relevanssi should handle apostrophes and quotes? It's not possible to "
1237
- "keep them; that would lead to problems. Default behaviour is to replace with "
1238
- "spaces, but sometimes removing makes sense."
1239
- msgstr ""
1240
-
1241
- #: lib/interface.php:2054
1242
- msgid "Ampersands"
1243
- msgstr ""
1244
-
1245
- #: lib/interface.php:2062
1246
- msgid ""
1247
- "How Relevanssi should handle ampersands? Replacing with spaces is generally "
1248
- "the best option, but if you talk a lot about D&amp;D, for example, keeping "
1249
- "the ampersands is useful."
1250
- msgstr ""
1251
-
1252
- #: lib/interface.php:2075
1253
- msgid "Hide advanced settings"
1254
- msgstr ""
1255
-
1256
- #: lib/interface.php:2087
1257
- msgid "Indexing attachment content"
1258
- msgstr ""
1259
-
1260
- #: lib/interface.php:2089
1261
- msgid ""
1262
- "With Relevanssi Premium, you can index the text contents of PDF attachments. "
1263
- "The contents of the attachments are processed on an external service, which "
1264
- "makes the feature reliable and light on your own server performance."
1265
- msgstr ""
1266
-
1267
- #: lib/interface.php:2091
1268
- #, php-format
1269
- msgid ""
1270
- "In order to access this and many other delightful Premium features, %s buy "
1271
- "Relevanssi Premium here%s."
1272
- msgstr ""
1273
-
1274
- #: lib/interface.php:2107
1275
- msgid ""
1276
- "Add synonyms here to make the searches find better results. If you notice "
1277
- "your users frequently misspelling a product name, or for other reasons use "
1278
- "many names for one thing, adding synonyms will make the results better."
1279
- msgstr ""
1280
-
1281
- #: lib/interface.php:2109
1282
- msgid ""
1283
- "Do not go overboard, though, as too many synonyms can make the search "
1284
- "confusing: users understand if a search query doesn't match everything, but "
1285
- "they get confused if the searches match to unexpected things."
1286
- msgstr ""
1287
-
1288
- #: lib/interface.php:2113
1289
- msgid ""
1290
- "The format here is <code>key = value</code>. If you add <code>dog = hound</"
1291
- "code> to the list of synonyms, searches for <code>dog</code> automatically "
1292
- "become a search for <code>dog hound</code> and will thus match to posts that "
1293
- "include either <code>dog</code> or <code>hound</code>. This only works in OR "
1294
- "searches: in AND searches the synonyms only restrict the search, as now the "
1295
- "search only finds posts that contain <strong>both</strong> <code>dog</code> "
1296
- "and <code>hound</code>."
1297
- msgstr ""
1298
-
1299
- #: lib/interface.php:2115
1300
- msgid ""
1301
- "The synonyms are one direction only. If you want both directions, add the "
1302
- "synonym again, reversed: <code>hound = dog</code>."
1303
- msgstr ""
1304
-
1305
- #: lib/interface.php:2117
1306
- msgid ""
1307
- "It's possible to use phrases for the value, but not for the key. <code>dog = "
1308
- "\"great dane\"</code> works, but <code>\"great dane\" = dog</code> doesn't."
1309
- msgstr ""
1310
-
1311
- #: lib/interface.php:2120
1312
- msgid ""
1313
- "If you want to use synonyms in AND searches, enable synonym indexing on the "
1314
- "Indexing tab."
1315
- msgstr ""
1316
-
1317
- #: lib/interface.php:2170
1318
- msgid ""
1319
- "Enter a word here to add it to the list of stopwords. The word will "
1320
- "automatically be removed from the index, so re-indexing is not necessary. "
1321
- "You can enter many words at the same time, separate words with commas."
1322
- msgstr ""
1323
-
1324
- #: lib/interface.php:2177
1325
- msgid "Stopword(s) to add"
1326
- msgstr ""
1327
-
1328
- #: lib/interface.php:2186
1329
- msgid ""
1330
- "Here's a list of stopwords in the database. Click a word to remove it from "
1331
- "stopwords. Removing stopwords won't automatically return them to index, so "
1332
- "you need to re-index all posts after removing stopwords to get those words "
1333
- "back to index."
1334
- msgstr ""
1335
-
1336
- #: lib/interface.php:2192
1337
- msgid "Current stopwords"
1338
- msgstr ""
1339
-
1340
- #: lib/interface.php:2214
1341
- msgid "Exportable list of stopwords"
1342
- msgstr ""
1343
-
1344
- #: lib/interface.php:2218
1345
- msgid ""
1346
- "You can copy the list of stopwords here if you want to back up the list, "
1347
- "copy it to a different blog or otherwise need the list."
1348
  msgstr ""
1349
 
1350
- #: lib/interface.php:2235
1351
  #, php-format
1352
  msgid ""
1353
- "To adjust the post order, you can use the %s query parameter. With %s, you "
1354
- "can use multiple layers of different sorting methods. See <a "
1355
- "href='%s'>WordPress Codex</a> for more details on using arrays for orderby."
1356
  msgstr ""
1357
 
1358
- #: lib/interface.php:2236
1359
  msgid ""
1360
  "Inside-word matching is disabled by default, because it increases garbage "
1361
  "results that don't really match the search term. If you want to enable it, "
1362
  "add the following function to your theme functions.php:"
1363
  msgstr ""
1364
 
1365
- #: lib/interface.php:2241
1366
  #, php-format
1367
  msgid "In order to adjust the throttle limit, you can use the %s filter hook."
1368
  msgstr ""
1369
 
1370
- #: lib/interface.php:2243
1371
  msgid ""
1372
  "It's not usually necessary to adjust the limit from 500, but in some cases "
1373
  "performance gains can be achieved by setting a lower limit. We don't suggest "
1374
  "going under 200, as low values will make the results worse."
1375
  msgstr ""
1376
 
1377
- #: lib/interface.php:2248
1378
  msgid "Restrictions"
1379
  msgstr ""
1380
 
1381
- #: lib/interface.php:2250
1382
  msgid ""
1383
  "If you want the general search to target all posts, but have a single search "
1384
  "form target only certain posts, you can add a hidden input variable to the "
1385
  "search form. "
1386
  msgstr ""
1387
 
1388
- #: lib/interface.php:2251
1389
  msgid ""
1390
  "For example in order to restrict the search to categories 10, 14 and 17, you "
1391
  "could add this to the search form:"
1392
  msgstr ""
1393
 
1394
- #: lib/interface.php:2253
1395
  msgid ""
1396
  "To restrict the search to posts tagged with alfa AND beta, you could add "
1397
  "this to the search form:"
1398
  msgstr ""
1399
 
1400
- #: lib/interface.php:2255
1401
  #, php-format
1402
  msgid "For all the possible options, see the Codex documentation for %s."
1403
  msgstr ""
1404
 
1405
- #: lib/interface.php:2260
1406
  msgid "Exclusions"
1407
  msgstr ""
1408
 
1409
- #: lib/interface.php:2262
1410
  #, php-format
1411
  msgid ""
1412
  "For more exclusion options, see the Codex documentation for %s. For example, "
1413
  "to exclude tag ID 10, use"
1414
  msgstr ""
1415
 
1416
- #: lib/interface.php:2264
1417
  #, php-format
1418
  msgid ""
1419
  "To exclude posts from the index and not just from the search, you can use "
@@ -1421,58 +183,62 @@ msgid ""
1421
  "term:"
1422
  msgstr ""
1423
 
1424
- #: lib/interface.php:2271 lib/interface.php:2310
1425
  #, php-format
1426
  msgid ""
1427
  "For more examples, see <a href='%s'>the related knowledge base posts</a>."
1428
  msgstr ""
1429
 
1430
- #: lib/interface.php:2276
1431
  msgid "Logs"
1432
  msgstr ""
1433
 
1434
- #: lib/interface.php:2278
1435
  #, php-format
1436
  msgid ""
1437
  "By default, the User searches page shows 20 most common keywords. In order "
1438
  "to see more, you can adjust the value with the %s filter hook, like this:"
1439
  msgstr ""
1440
 
1441
- #: lib/interface.php:2280
1442
  #, php-format
1443
  msgid ""
1444
  "The complete logs are stored in the %s database table, where you can access "
1445
  "them if you need more information than what the User searches page provides."
1446
  msgstr ""
1447
 
1448
- #: lib/interface.php:2287
 
 
 
 
1449
  msgid ""
1450
  "Building custom excerpts can be slow. If you are not actually using the "
1451
  "excerpts, make sure you disable the option."
1452
  msgstr ""
1453
 
1454
- #: lib/interface.php:2288
1455
  #, php-format
1456
  msgid ""
1457
  "Custom snippets require that the search results template uses %s to print "
1458
  "out the excerpts."
1459
  msgstr ""
1460
 
1461
- #: lib/interface.php:2289
1462
  msgid ""
1463
  "Generally, Relevanssi generates the excerpts from post content. If you want "
1464
  "to include custom field content in the excerpt-building, this can be done "
1465
  "with a simple setting from the excerpt settings."
1466
  msgstr ""
1467
 
1468
- #: lib/interface.php:2290
1469
  #, php-format
1470
  msgid ""
1471
  "If you want more control over what content Relevanssi uses to create the "
1472
- "excerpts, you can use the %s and %s filter hooks to adjust the content."
1473
  msgstr ""
1474
 
1475
- #: lib/interface.php:2291
1476
  #, php-format
1477
  msgid ""
1478
  "Some shortcode do not work well with Relevanssi excerpt-generation. "
@@ -1480,199 +246,231 @@ msgid ""
1480
  "can be adjusted with the %s filter hook."
1481
  msgstr ""
1482
 
1483
- #: lib/interface.php:2292
1484
  #, php-format
1485
  msgid ""
1486
  "If you want Relevanssi to build excerpts faster and don't mind that they may "
1487
  "be less than perfect in quality, add a filter that returns true on hook %s."
1488
  msgstr ""
1489
 
1490
- #: lib/interface.php:2298
1491
  msgid "Highlights"
1492
  msgstr ""
1493
 
1494
- #: lib/interface.php:2300
1495
  msgid ""
1496
  "Title highlights don't appear automatically, because that led to problems "
1497
  "with highlights appearing in wrong places and messing up navigation menus, "
1498
  "for example."
1499
  msgstr ""
1500
 
1501
- #: lib/interface.php:2301
1502
  #, php-format
1503
  msgid ""
1504
- "In order to see title highlights from Relevanssi, replace %s in the search "
1505
- "results template with %s. It does the same thing, but supports Relevanssi "
1506
  "title highlights."
1507
  msgstr ""
1508
 
1509
- #: lib/interface.php:2306
1510
  msgid "Punctuation"
1511
  msgstr ""
1512
 
1513
- #: lib/interface.php:2308
1514
  msgid ""
1515
  "Relevanssi removes punctuation. Some punctuation is removed, some replaced "
1516
  "with spaces. Advanced indexing settings include some of the more common "
1517
  "settings people want to change."
1518
  msgstr ""
1519
 
1520
- #: lib/interface.php:2309
1521
  #, php-format
1522
  msgid ""
1523
- "For more fine-tuned changes, you can use %s filter hook to adjust what is "
1524
- "replaced with what, and %s filter hook to completely override the default "
1525
  "punctuation control."
1526
  msgstr ""
1527
 
1528
- #: lib/interface.php:2315
1529
  msgid "Helpful shortcodes"
1530
  msgstr ""
1531
 
1532
- #: lib/interface.php:2317
1533
  #, php-format
1534
  msgid ""
1535
  "If you have content that you don't want indexed, you can wrap that content "
1536
  "in a %s shortcode."
1537
  msgstr ""
1538
 
1539
- #: lib/interface.php:2318
1540
  #, php-format
1541
  msgid ""
1542
  "If you need a search form on some page on your site, you can use the %s "
1543
  "shortcode to print out a basic search form."
1544
  msgstr ""
1545
 
1546
- #: lib/interface.php:2323
 
 
 
 
 
 
 
 
 
1547
  msgid "WooCommerce"
1548
  msgstr ""
1549
 
1550
- #: lib/interface.php:2325
1551
  msgid ""
1552
  "If your SKUs include hyphens or other punctuation, do note that Relevanssi "
1553
  "replaces most punctuation with spaces. That's going to cause issues with SKU "
1554
  "searches."
1555
  msgstr ""
1556
 
1557
- #: lib/interface.php:2326
1558
  #, php-format
1559
  msgid ""
1560
  "For more details how to fix that issue, see <a href='%s'>WooCommerce tips in "
1561
  "Relevanssi user manual</a>."
1562
  msgstr ""
1563
 
1564
- #: lib/interface.php:2330
 
 
 
 
 
 
 
 
 
 
 
1565
  msgid "For more information:"
1566
  msgstr ""
1567
 
1568
- #: lib/interface.php:2331
1569
  msgid "Plugin knowledge base"
1570
  msgstr ""
1571
 
1572
- #: lib/interface.php:2332
1573
  msgid "WordPress.org forum"
1574
  msgstr ""
1575
 
1576
- #: lib/interface.php:2356
1577
  msgid "Click OK to copy Relevanssi options to all subsites"
1578
  msgstr ""
1579
 
1580
- #: lib/interface.php:2357
1581
  msgid "Are you sure you want to remove all stopwords?"
1582
  msgstr ""
1583
 
1584
- #: lib/interface.php:2358
1585
  msgid "Wiping out the index..."
1586
  msgstr ""
1587
 
1588
- #: lib/interface.php:2359
1589
  msgid "Done."
1590
  msgstr ""
1591
 
1592
- #: lib/interface.php:2360
1593
  msgid "Indexing users..."
1594
  msgstr ""
1595
 
1596
- #: lib/interface.php:2361
1597
  msgid "Indexing the following taxonomies:"
1598
  msgstr ""
1599
 
1600
- #: lib/interface.php:2362
1601
  msgid "Counting posts..."
1602
  msgstr ""
1603
 
1604
- #: lib/interface.php:2363
1605
  msgid "Counting taxonomy terms..."
1606
  msgstr ""
1607
 
1608
- #: lib/interface.php:2364
 
 
 
 
1609
  msgid "posts found."
1610
  msgstr ""
1611
 
1612
- #: lib/interface.php:2365
1613
  msgid "taxonomy terms found."
1614
  msgstr ""
1615
 
1616
- #: lib/interface.php:2366
 
 
 
 
1617
  msgid "Taxonomy term indexing is disabled."
1618
  msgstr ""
1619
 
1620
- #: lib/interface.php:2367
 
 
 
 
1621
  msgid "Indexing complete."
1622
  msgstr ""
1623
 
1624
- #: lib/interface.php:2368
1625
  msgid "posts excluded."
1626
  msgstr ""
1627
 
1628
- #: lib/interface.php:2369
1629
  msgid "Settings have changed, please save the options before indexing."
1630
  msgstr ""
1631
 
1632
- #: lib/interface.php:2370
1633
  msgid "Reload the page to refresh the state of the index."
1634
  msgstr ""
1635
 
1636
- #: lib/interface.php:2371
1637
- msgid "Are you sure you want to delete all PDF content from the index?"
1638
  msgstr ""
1639
 
1640
- #: lib/interface.php:2372
1641
- msgid "Relevanssi PDF data wiped clean. Removed entries: "
1642
  msgstr ""
1643
 
1644
- #: lib/interface.php:2373
1645
  msgid "hour"
1646
  msgstr ""
1647
 
1648
- #: lib/interface.php:2374
1649
  msgid "hours"
1650
  msgstr ""
1651
 
1652
- #: lib/interface.php:2375
1653
  msgid "about"
1654
  msgstr ""
1655
 
1656
- #: lib/interface.php:2376
1657
  msgid "about an hour"
1658
  msgstr ""
1659
 
1660
- #: lib/interface.php:2377
1661
  msgid "about an hour and a half"
1662
  msgstr ""
1663
 
1664
- #: lib/interface.php:2378
1665
  msgid "minute"
1666
  msgstr ""
1667
 
1668
- #: lib/interface.php:2379
1669
  msgid "minutes"
1670
  msgstr ""
1671
 
1672
- #: lib/interface.php:2380
1673
  msgid "less than a minute"
1674
  msgstr ""
1675
 
1676
- #: lib/interface.php:2381
1677
  msgid "we're done!"
1678
  msgstr ""
2
  msgstr ""
3
  "Project-Id-Version: Relevanssi\n"
4
  "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: 2018-03-26 20:49+0300\n"
6
  "PO-Revision-Date: \n"
7
  "Last-Translator: Mikko Saari <mikko@mikkosaari.fi>\n"
8
  "Language-Team: \n"
10
  "MIME-Version: 1.0\n"
11
  "Content-Type: text/plain; charset=UTF-8\n"
12
  "Content-Transfer-Encoding: 8bit\n"
13
+ "X-Poedit-KeywordsList: _e;__;esc_html__;esc_html_e;_n\n"
14
  "X-Poedit-Basepath: .\n"
15
+ "X-Generator: Poedit 2.0.6\n"
16
  "X-Poedit-SearchPath-0: .\n"
17
+ "X-Poedit-SearchPath-1: lib\n"
18
 
19
+ #: lib/indexing.php:349
20
+ msgid "Indexing complete!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  msgstr ""
22
 
23
+ #: lib/indexing.php:352
24
+ msgid "More to index..."
25
  msgstr ""
26
 
27
+ #: lib/init.php:149 lib/init.php:150
28
+ msgid "User searches"
29
  msgstr ""
30
 
31
+ #: lib/init.php:339
32
+ msgid "Settings"
 
 
33
  msgstr ""
34
 
35
+ #: lib/init.php:342
36
+ msgid "Go Premium!"
37
  msgstr ""
38
 
39
+ #: lib/interface.php:18
40
+ msgid "Relevanssi Search Options"
 
41
  msgstr ""
42
 
43
+ #: lib/interface.php:20
44
+ msgid "Relevanssi Premium Search Options"
45
  msgstr ""
46
 
47
+ #: lib/interface.php:79
48
+ msgid "User Searches"
49
  msgstr ""
50
 
51
+ #: lib/interface.php:81
52
+ msgid "Relevanssi User Searches"
 
 
 
 
53
  msgstr ""
54
 
55
+ #: lib/interface.php:654
56
+ msgid "Add to stopwords"
57
  msgstr ""
58
 
59
+ #: lib/interface.php:684
60
+ msgid "Totals"
61
  msgstr ""
62
 
63
+ #: lib/interface.php:698
64
  #, php-format
65
  msgid ""
66
+ "Here you can see the %d most common user search queries, how many times "
67
+ "those queries were made and how many results were found for those queries."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  msgstr ""
69
 
70
+ #: lib/interface.php:701 lib/interface.php:718 lib/interface.php:759
71
+ msgid "Today and yesterday"
72
  msgstr ""
73
 
74
+ #: lib/interface.php:705 lib/interface.php:722 lib/interface.php:760
75
+ msgid "Last 7 days"
 
 
 
76
  msgstr ""
77
 
78
+ #: lib/interface.php:710 lib/interface.php:727
79
  #, php-format
80
+ msgid "Last %d days"
 
 
 
 
81
  msgstr ""
82
 
83
+ #: lib/interface.php:737
84
  #, php-format
85
+ msgid "To reset the logs, type \"reset\" into the box here %1$s and click %2$s"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  msgstr ""
87
 
88
+ #: lib/interface.php:761
89
+ msgid "Last 30 days"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  msgstr ""
91
 
92
+ #: lib/interface.php:762
93
+ msgid "Forever"
94
  msgstr ""
95
 
96
+ #: lib/interface.php:2187 lib/interface.php:2241
97
  msgid "yes"
98
  msgstr ""
99
 
100
+ #: lib/interface.php:2189 lib/interface.php:2239
101
+ msgid "no"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  msgstr ""
103
 
104
+ #: lib/interface.php:2302
105
  msgid "some"
106
  msgstr ""
107
 
108
+ #: lib/interface.php:2658
109
+ msgid "Searching"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  msgstr ""
111
 
112
+ #: lib/interface.php:2661
113
  #, php-format
114
  msgid ""
115
+ "To adjust the post order, you can use the %1$s query parameter. With %1$s, "
116
+ "you can use multiple layers of different sorting methods. See <a "
117
+ "href='%2$s'>WordPress Codex</a> for more details on using arrays for orderby."
118
  msgstr ""
119
 
120
+ #: lib/interface.php:2662
121
  msgid ""
122
  "Inside-word matching is disabled by default, because it increases garbage "
123
  "results that don't really match the search term. If you want to enable it, "
124
  "add the following function to your theme functions.php:"
125
  msgstr ""
126
 
127
+ #: lib/interface.php:2668
128
  #, php-format
129
  msgid "In order to adjust the throttle limit, you can use the %s filter hook."
130
  msgstr ""
131
 
132
+ #: lib/interface.php:2670
133
  msgid ""
134
  "It's not usually necessary to adjust the limit from 500, but in some cases "
135
  "performance gains can be achieved by setting a lower limit. We don't suggest "
136
  "going under 200, as low values will make the results worse."
137
  msgstr ""
138
 
139
+ #: lib/interface.php:2675
140
  msgid "Restrictions"
141
  msgstr ""
142
 
143
+ #: lib/interface.php:2677
144
  msgid ""
145
  "If you want the general search to target all posts, but have a single search "
146
  "form target only certain posts, you can add a hidden input variable to the "
147
  "search form. "
148
  msgstr ""
149
 
150
+ #: lib/interface.php:2678
151
  msgid ""
152
  "For example in order to restrict the search to categories 10, 14 and 17, you "
153
  "could add this to the search form:"
154
  msgstr ""
155
 
156
+ #: lib/interface.php:2680
157
  msgid ""
158
  "To restrict the search to posts tagged with alfa AND beta, you could add "
159
  "this to the search form:"
160
  msgstr ""
161
 
162
+ #: lib/interface.php:2683
163
  #, php-format
164
  msgid "For all the possible options, see the Codex documentation for %s."
165
  msgstr ""
166
 
167
+ #: lib/interface.php:2688
168
  msgid "Exclusions"
169
  msgstr ""
170
 
171
+ #: lib/interface.php:2691
172
  #, php-format
173
  msgid ""
174
  "For more exclusion options, see the Codex documentation for %s. For example, "
175
  "to exclude tag ID 10, use"
176
  msgstr ""
177
 
178
+ #: lib/interface.php:2694
179
  #, php-format
180
  msgid ""
181
  "To exclude posts from the index and not just from the search, you can use "
183
  "term:"
184
  msgstr ""
185
 
186
+ #: lib/interface.php:2704 lib/interface.php:2752
187
  #, php-format
188
  msgid ""
189
  "For more examples, see <a href='%s'>the related knowledge base posts</a>."
190
  msgstr ""
191
 
192
+ #: lib/interface.php:2709
193
  msgid "Logs"
194
  msgstr ""
195
 
196
+ #: lib/interface.php:2712
197
  #, php-format
198
  msgid ""
199
  "By default, the User searches page shows 20 most common keywords. In order "
200
  "to see more, you can adjust the value with the %s filter hook, like this:"
201
  msgstr ""
202
 
203
+ #: lib/interface.php:2715
204
  #, php-format
205
  msgid ""
206
  "The complete logs are stored in the %s database table, where you can access "
207
  "them if you need more information than what the User searches page provides."
208
  msgstr ""
209
 
210
+ #: lib/interface.php:2720
211
+ msgid "Excerpts"
212
+ msgstr ""
213
+
214
+ #: lib/interface.php:2722
215
  msgid ""
216
  "Building custom excerpts can be slow. If you are not actually using the "
217
  "excerpts, make sure you disable the option."
218
  msgstr ""
219
 
220
+ #: lib/interface.php:2724
221
  #, php-format
222
  msgid ""
223
  "Custom snippets require that the search results template uses %s to print "
224
  "out the excerpts."
225
  msgstr ""
226
 
227
+ #: lib/interface.php:2725
228
  msgid ""
229
  "Generally, Relevanssi generates the excerpts from post content. If you want "
230
  "to include custom field content in the excerpt-building, this can be done "
231
  "with a simple setting from the excerpt settings."
232
  msgstr ""
233
 
234
+ #: lib/interface.php:2727
235
  #, php-format
236
  msgid ""
237
  "If you want more control over what content Relevanssi uses to create the "
238
+ "excerpts, you can use the %1$s and %2$s filter hooks to adjust the content."
239
  msgstr ""
240
 
241
+ #: lib/interface.php:2729
242
  #, php-format
243
  msgid ""
244
  "Some shortcode do not work well with Relevanssi excerpt-generation. "
246
  "can be adjusted with the %s filter hook."
247
  msgstr ""
248
 
249
+ #: lib/interface.php:2731
250
  #, php-format
251
  msgid ""
252
  "If you want Relevanssi to build excerpts faster and don't mind that they may "
253
  "be less than perfect in quality, add a filter that returns true on hook %s."
254
  msgstr ""
255
 
256
+ #: lib/interface.php:2737
257
  msgid "Highlights"
258
  msgstr ""
259
 
260
+ #: lib/interface.php:2739
261
  msgid ""
262
  "Title highlights don't appear automatically, because that led to problems "
263
  "with highlights appearing in wrong places and messing up navigation menus, "
264
  "for example."
265
  msgstr ""
266
 
267
+ #: lib/interface.php:2741
268
  #, php-format
269
  msgid ""
270
+ "In order to see title highlights from Relevanssi, replace %1$s in the search "
271
+ "results template with %2$s. It does the same thing, but supports Relevanssi "
272
  "title highlights."
273
  msgstr ""
274
 
275
+ #: lib/interface.php:2746
276
  msgid "Punctuation"
277
  msgstr ""
278
 
279
+ #: lib/interface.php:2748
280
  msgid ""
281
  "Relevanssi removes punctuation. Some punctuation is removed, some replaced "
282
  "with spaces. Advanced indexing settings include some of the more common "
283
  "settings people want to change."
284
  msgstr ""
285
 
286
+ #: lib/interface.php:2750
287
  #, php-format
288
  msgid ""
289
+ "For more fine-tuned changes, you can use %1$s filter hook to adjust what is "
290
+ "replaced with what, and %2$s filter hook to completely override the default "
291
  "punctuation control."
292
  msgstr ""
293
 
294
+ #: lib/interface.php:2757
295
  msgid "Helpful shortcodes"
296
  msgstr ""
297
 
298
+ #: lib/interface.php:2760
299
  #, php-format
300
  msgid ""
301
  "If you have content that you don't want indexed, you can wrap that content "
302
  "in a %s shortcode."
303
  msgstr ""
304
 
305
+ #: lib/interface.php:2762
306
  #, php-format
307
  msgid ""
308
  "If you need a search form on some page on your site, you can use the %s "
309
  "shortcode to print out a basic search form."
310
  msgstr ""
311
 
312
+ #: lib/interface.php:2764
313
+ #, php-format
314
+ msgid ""
315
+ "If you need to add query variables to the search form, the shortcode takes "
316
+ "parameters, which are then printed out as hidden input fields. To get a "
317
+ "search form with a post type restriction, you can use %1$s. To restrict the "
318
+ "search to categories 10, 14 and 17, you can use %2$s and so on."
319
+ msgstr ""
320
+
321
+ #: lib/interface.php:2769
322
  msgid "WooCommerce"
323
  msgstr ""
324
 
325
+ #: lib/interface.php:2771
326
  msgid ""
327
  "If your SKUs include hyphens or other punctuation, do note that Relevanssi "
328
  "replaces most punctuation with spaces. That's going to cause issues with SKU "
329
  "searches."
330
  msgstr ""
331
 
332
+ #: lib/interface.php:2773
333
  #, php-format
334
  msgid ""
335
  "For more details how to fix that issue, see <a href='%s'>WooCommerce tips in "
336
  "Relevanssi user manual</a>."
337
  msgstr ""
338
 
339
+ #: lib/interface.php:2778
340
+ msgid "Exact match bonus"
341
+ msgstr ""
342
+
343
+ #: lib/interface.php:2781
344
+ #, php-format
345
+ msgid ""
346
+ "To adjust the amount of the exact match bonus, you can use the %s filter "
347
+ "hook. It works like this:"
348
+ msgstr ""
349
+
350
+ #: lib/interface.php:2790
351
  msgid "For more information:"
352
  msgstr ""
353
 
354
+ #: lib/interface.php:2791
355
  msgid "Plugin knowledge base"
356
  msgstr ""
357
 
358
+ #: lib/interface.php:2792
359
  msgid "WordPress.org forum"
360
  msgstr ""
361
 
362
+ #: lib/interface.php:2832
363
  msgid "Click OK to copy Relevanssi options to all subsites"
364
  msgstr ""
365
 
366
+ #: lib/interface.php:2833
367
  msgid "Are you sure you want to remove all stopwords?"
368
  msgstr ""
369
 
370
+ #: lib/interface.php:2834
371
  msgid "Wiping out the index..."
372
  msgstr ""
373
 
374
+ #: lib/interface.php:2835
375
  msgid "Done."
376
  msgstr ""
377
 
378
+ #: lib/interface.php:2836
379
  msgid "Indexing users..."
380
  msgstr ""
381
 
382
+ #: lib/interface.php:2837
383
  msgid "Indexing the following taxonomies:"
384
  msgstr ""
385
 
386
+ #: lib/interface.php:2838
387
  msgid "Counting posts..."
388
  msgstr ""
389
 
390
+ #: lib/interface.php:2839
391
  msgid "Counting taxonomy terms..."
392
  msgstr ""
393
 
394
+ #: lib/interface.php:2840
395
+ msgid "Counting users..."
396
+ msgstr ""
397
+
398
+ #: lib/interface.php:2841
399
  msgid "posts found."
400
  msgstr ""
401
 
402
+ #: lib/interface.php:2842
403
  msgid "taxonomy terms found."
404
  msgstr ""
405
 
406
+ #: lib/interface.php:2843
407
+ msgid "users found."
408
+ msgstr ""
409
+
410
+ #: lib/interface.php:2844
411
  msgid "Taxonomy term indexing is disabled."
412
  msgstr ""
413
 
414
+ #: lib/interface.php:2845
415
+ msgid "User indexing is disabled."
416
+ msgstr ""
417
+
418
+ #: lib/interface.php:2846
419
  msgid "Indexing complete."
420
  msgstr ""
421
 
422
+ #: lib/interface.php:2847
423
  msgid "posts excluded."
424
  msgstr ""
425
 
426
+ #: lib/interface.php:2848
427
  msgid "Settings have changed, please save the options before indexing."
428
  msgstr ""
429
 
430
+ #: lib/interface.php:2849
431
  msgid "Reload the page to refresh the state of the index."
432
  msgstr ""
433
 
434
+ #: lib/interface.php:2850
435
+ msgid "Are you sure you want to delete all attachment content from the index?"
436
  msgstr ""
437
 
438
+ #: lib/interface.php:2851
439
+ msgid "Relevanssi attachment data wiped clean."
440
  msgstr ""
441
 
442
+ #: lib/interface.php:2852
443
  msgid "hour"
444
  msgstr ""
445
 
446
+ #: lib/interface.php:2853
447
  msgid "hours"
448
  msgstr ""
449
 
450
+ #: lib/interface.php:2854
451
  msgid "about"
452
  msgstr ""
453
 
454
+ #: lib/interface.php:2855
455
  msgid "about an hour"
456
  msgstr ""
457
 
458
+ #: lib/interface.php:2856
459
  msgid "about an hour and a half"
460
  msgstr ""
461
 
462
+ #: lib/interface.php:2857
463
  msgid "minute"
464
  msgstr ""
465
 
466
+ #: lib/interface.php:2858
467
  msgid "minutes"
468
  msgstr ""
469
 
470
+ #: lib/interface.php:2859
471
  msgid "less than a minute"
472
  msgstr ""
473
 
474
+ #: lib/interface.php:2860
475
  msgid "we're done!"
476
  msgstr ""
uninstall.php CHANGED
@@ -1,110 +1,29 @@
1
  <?php
 
 
 
 
 
 
 
 
2
 
3
- if (!defined('WP_UNINSTALL_PLUGIN'))
4
  exit();
 
5
 
6
  global $wpdb;
 
 
7
 
8
- if (function_exists('is_multisite') && is_multisite()) {
9
- $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
10
  $old_blogid = $wpdb->blogid;
11
- foreach ($blogids as $blog_id) {
12
- switch_to_blog($blog_id);
13
- _relevanssi_uninstall();
14
  }
15
- switch_to_blog($old_blogid);
16
- }
17
- else {
18
- _relevanssi_uninstall();
19
  }
20
-
21
- function _relevanssi_uninstall() {
22
- delete_option('relevanssi_admin_search');
23
- delete_option('relevanssi_bg_col');
24
- delete_option('relevanssi_cache_seconds');
25
- delete_option('relevanssi_cat');
26
- delete_option('relevanssi_comment_boost');
27
- delete_option('relevanssi_css');
28
- delete_option('relevanssi_class');
29
- delete_option('relevanssi_content_boost');
30
- delete_option('relevanssi_db_version');
31
- delete_option('relevanssi_default_orderby');
32
- delete_option('relevanssi_disable_or_fallback');
33
- delete_option('relevanssi_disable_shortcodes');
34
- delete_option('relevanssi_doc_count');
35
- delete_option('relevanssi_enable_cache');
36
- delete_option('relevanssi_excat');
37
- delete_option('relevanssi_extag');
38
- delete_option('relevanssi_excerpt_length');
39
- delete_option('relevanssi_excerpt_type');
40
- delete_option('relevanssi_excerpt_allowable_tags');
41
- delete_option('relevanssi_excerpt_custom_fields');
42
- delete_option('relevanssi_excerpts');
43
- delete_option('relevanssi_exclude_posts'); //added by OdditY
44
- delete_option('relevanssi_expand_shortcodes');
45
- delete_option('relevanssi_fuzzy');
46
- delete_option('relevanssi_hide_branding');
47
- delete_option('relevanssi_hide_post_controls');
48
- delete_option('relevanssi_highlight_comments');
49
- delete_option('relevanssi_highlight_docs_external');
50
- delete_option('relevanssi_highlight_docs');
51
- delete_option('relevanssi_highlight');
52
- delete_option('relevanssi_hilite_title'); //added by OdditY
53
- delete_option('relevanssi_implicit_operator');
54
- delete_option('relevanssi_index');
55
- delete_option('relevanssi_index_author');
56
- delete_option('relevanssi_index_comments'); //added by OdditY
57
- delete_option('relevanssi_index_drafts');
58
- delete_option('relevanssi_index_excerpt');
59
- delete_option('relevanssi_index_fields');
60
- delete_option('relevanssi_index_limit');
61
- delete_option('relevanssi_index_post_types');
62
- delete_option('relevanssi_index_synonyms');
63
- delete_option('relevanssi_index_taxonomies');
64
- delete_option('relevanssi_index_taxonomies_list');
65
- delete_option('relevanssi_index_terms');
66
- delete_option('relevanssi_indexed');
67
- delete_option('relevanssi_link_boost');
68
- delete_option('relevanssi_log_queries');
69
- delete_option('relevanssi_log_queries_with_ip');
70
- delete_option('relevanssi_min_word_length');
71
- delete_option('relevanssi_mysql_columns');
72
- delete_option('relevanssi_omit_from_logs');
73
- delete_option('relevanssi_polylang_all_languages');
74
- delete_option('relevanssi_post_type_weights');
75
- delete_option('relevanssi_punctuation');
76
- delete_option('relevanssi_recency_bonus');
77
- delete_option('relevanssi_respect_exclude');
78
- delete_option('relevanssi_show_matches_text');
79
- delete_option('relevanssi_show_matches');
80
- delete_option('relevanssi_synonyms');
81
- delete_option('relevanssi_thousand_separator');
82
- delete_option('relevanssi_throttle');
83
- delete_option('relevanssi_throttle_limit');
84
- delete_option('relevanssi_title_boost');
85
- delete_option('relevanssi_txt_col');
86
- delete_option('relevanssi_word_boundaries');
87
- delete_option('relevanssi_wpml_only_current');
88
-
89
- global $wpdb;
90
- $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_hide_post'");
91
- $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_pin'");
92
- $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_unpin'");
93
- $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_pdf_content'");
94
- $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_pdf_error'");
95
-
96
- // Unused options, removed in case they are still left
97
- delete_option('relevanssi_custom_types');
98
- delete_option('relevanssi_hidesponsor');
99
- delete_option('relevanssi_index_attachments');
100
- delete_option('relevanssi_index_type');
101
- delete_option('relevanssi_show_matches_txt');
102
- delete_option('relevanssi_tag_boost');
103
- delete_option('relevanssi_include_cats');
104
- delete_option('relevanssi_include_tags'); //added by OdditY
105
- delete_option('relevanssi_custom_taxonomies');
106
- delete_option('relevanssi_taxonomies_to_index');
107
-
108
- include_once('lib/uninstall.php');
109
- relevanssi_clear_database_tables();
110
- }
1
  <?php
2
+ /**
3
+ * /uninstall.php
4
+ *
5
+ * @package Relevanssi
6
+ * @author Mikko Saari
7
+ * @license https://wordpress.org/about/gpl/ GNU General Public License
8
+ * @see https://www.relevanssi.com/
9
+ */
10
 
11
+ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
12
  exit();
13
+ }
14
 
15
  global $wpdb;
16
+ define( 'RELEVANSSI_PREMIUM', false );
17
+ require_once 'lib/uninstall.php';
18
 
19
+ if ( function_exists( 'is_multisite' ) && is_multisite() ) {
20
+ $blogids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
21
  $old_blogid = $wpdb->blogid;
22
+ foreach ( $blogids as $blog_id ) {
23
+ switch_to_blog( $blog_id );
24
+ relevanssi_uninstall_free();
25
  }
26
+ switch_to_blog( $old_blogid );
27
+ } else {
28
+ relevanssi_uninstall_free();
 
29
  }