3c85cdf4 by Jeff Balicki

qa

Signed-off-by: Jeff <jeff@gotenzing.com>
1 parent f5c15107
Showing 90 changed files with 4898 additions and 0 deletions
This diff could not be displayed because it is too large.
1 jQuery(document).ready(function ($) {
2 $("#build_index").click(function () {
3 $("#relevanssi-progress").show()
4 $("#results").show()
5 $("#relevanssi-timer").show()
6 $("#relevanssi-indexing-instructions").show()
7 $("#stateoftheindex").html(relevanssi.reload_state)
8 $("#indexing_button_instructions").hide()
9 var results = document.getElementById("results")
10 results.value = ""
11
12 var data = {
13 action: "relevanssi_truncate_index",
14 security: nonce.indexing_nonce,
15 }
16
17 intervalID = window.setInterval(relevanssiUpdateClock, 1000)
18
19 console.log("Truncating index.")
20 results.value += relevanssi.truncating_index + " "
21 jQuery.post(ajaxurl, data, function (response) {
22 truncate_response = JSON.parse(response)
23 console.log("Truncate index: " + truncate_response)
24 if (truncate_response == true) {
25 results.value += relevanssi.done + "\n"
26 }
27
28 var data = {
29 action: "relevanssi_count_posts",
30 }
31 console.log("Counting posts.")
32 results.value += relevanssi.counting_posts + " "
33 jQuery.post(ajaxurl, data, function (response) {
34 count_response = JSON.parse(response)
35 console.log("Counted " + count_response + " posts.")
36 var post_total = parseInt(count_response)
37 results.value += count_response + " " + relevanssi.posts_found + "\n"
38
39 var args = {
40 completed: 0,
41 total: post_total,
42 offset: 0,
43 total_seconds: 0,
44 limit: relevanssi_params.indexing_limit,
45 adjust: relevanssi_params.indexing_adjust,
46 extend: false,
47 security: nonce.indexing_nonce,
48 }
49 process_indexing_step(args)
50 })
51 })
52 })
53 })
1 p.important {
2 color: #992000;
3 }
4
5 table.form-table table.widefat th {
6 padding-left: 8px;
7 }
8
9 #relevanssi_min_word_length {
10 width: 3em;
11 }
12
13 #relevanssi_trim_logs, #relevanssi_trim_click_logs {
14 width: 4em;
15 }
16
17 #index_field_input {
18 margin-top: 1em;
19 }
20
21 #indexing_tab #results {
22 display: none;
23 width: 100%;
24 }
25
26 #relevanssi-progress {
27 display: none;
28 margin-bottom: 2em;
29 width: 100%;
30 height: 20px;
31 background-color: white;
32 }
33
34 .rpi-indicator {
35 width: 0;
36 height: 20px;
37 background-color: #afe240;
38 }
39
40 .relevanssi-weights-table {
41 min-width: 400px;
42 }
43
44 .relevanssi-weights-table td {
45 padding: 0;
46 }
47
48 .relevanssi-weights-table td.col-2, .relevanssi-weights-table th.col-2 {
49 width: 25%;
50 }
51
52 .rpi-progress {
53 display: none;
54 margin: 0.5em 0 2em 0;
55 width: 100%;
56 height: 20px;
57 background-color: white;
58 }
59
60 .rpi-progress div {
61 width: 0;
62 height: 20px;
63 background-color: #afe240;
64 }
65
66 #relevanssi_results {
67 display: none;
68 width: 100%;
69 }
70
71 #relevanssi_show_pdf_errors {
72 text-decoration: underline;
73 cursor: pointer;
74 color: #0073aa;
75 }
76
77 #relevanssi_pdf_errors {
78 display: none;
79 }
80
81 .visually_hidden {
82 margin: -1px;
83 padding: 0;
84 width: 1px;
85 height: 1px;
86 overflow: hidden;
87 clip: rect(0 0 0 0);
88 clip: rect(0,0,0,0);
89 position: absolute;
90 }
91
92 .relevanssi_disabled, .relevanssi_disabled td, .relevanssi_disabled th, .relevanssi_disabled p {
93 color: #999;
94 }
95
96 #relevanssi-timer {
97 display: none;
98 }
99
100 #category_inclusion_checklist ul.children, #category_exclusion_checklist ul.children {
101 margin-left: 1.5em;
102 }
103
104 #relevanssi_filter_list {
105 display: none;
106 }
107
108 #redirect_table td {
109 vertical-align: top;
110 }
111
112 #relevanssi_sees_container, #relevanssi_db_view_container {
113 width: 80%;
114 background: white;
115 padding: 5px 20px;
116 border: thin solid black;
117 overflow: scroll;
118 }
...\ No newline at end of file ...\ No newline at end of file
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 $output .= "\n" . '<li' . $class . '>' .
61 '<div class="' . $inner_class . '" data-term-id=' . $category->term_id .
62 ' tabindex="0" role="checkbox" aria-checked="' . $aria_checked . '">' .
63 /** This filter is documented in wp-includes/category-template.php */
64 esc_html( apply_filters( 'the_category', $category->name, '', '' ) ) . '</div>';
65 } else {
66 $output .= "\n<li id='{$taxonomy}-{$category->term_id}'$class>" .
67 '<label class="selectit"><input value="' . $category->term_id . '" type="checkbox" name="' . $name . '[]" id="in-' . $taxonomy . '-' . $category->term_id . '"' .
68 checked( in_array( intval( $category->term_id ), $args['selected_cats'], true ), true, false ) .
69 disabled( empty( $args['disabled'] ), false, false ) . ' /> ' .
70 /** This filter is documented in wp-includes/category-template.php */
71 esc_html( apply_filters( 'the_category', $category->name, '', '' ) ) . '</label>';
72 }
73 }
74 }
1 <?php
2 /**
3 * /lib/compatibility/acf.php
4 *
5 * Advanced Custom Fields compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_action( 'acf/render_field_settings', 'relevanssi_acf_exclude_setting' );
14 add_filter( 'relevanssi_search_ok', 'relevanssi_acf_relationship_fields' );
15 add_filter( 'relevanssi_index_custom_fields', 'relevanssi_acf_exclude_fields', 10, 2 );
16
17 /**
18 * Disables Relevanssi in the ACF Relationship field post search.
19 *
20 * We don't want to use Relevanssi on the ACF Relationship field post searches, so
21 * this function disables it (on the 'relevanssi_search_ok' hook).
22 *
23 * @param boolean $search_ok Block the search or not.
24 *
25 * @return boolean False, if this is an ACF Relationship field search, pass the
26 * parameter unchanged otherwise.
27 */
28 function relevanssi_acf_relationship_fields( $search_ok ) {
29 // phpcs:disable WordPress.Security.NonceVerification
30 if ( isset( $_REQUEST['action'] )
31 && is_string( $_REQUEST['action'] )
32 && 'acf' === substr( $_REQUEST['action'], 0, 3 ) ) {
33 $search_ok = false;
34 }
35 return $search_ok;
36 }
37
38 /**
39 * Indexes the human-readable value of "choice" options list from ACF.
40 *
41 * @author Droz Raphaël
42 *
43 * @param array $insert_data The insert data array.
44 * @param int $post_id The post ID.
45 * @param string $field_name Name of the field.
46 * @param string $field_value The field value.
47 *
48 * @return int Number of tokens indexed.
49 */
50 function relevanssi_index_acf( &$insert_data, $post_id, $field_name, $field_value ) {
51 if ( ! is_admin() ) {
52 include_once ABSPATH . 'wp-admin/includes/plugin.php'; // Otherwise is_plugin_active() will cause a fatal error.
53 }
54 if ( ! function_exists( 'is_plugin_active' ) ) {
55 return 0;
56 }
57 if ( ! is_plugin_active( 'advanced-custom-fields/acf.php' ) && ! is_plugin_active( 'advanced-custom-fields-pro/acf.php' ) ) {
58 return 0;
59 }
60 if ( ! function_exists( 'get_field_object' ) ) {
61 return 0; // ACF is active, but not loaded.
62 }
63
64 $field_object = get_field_object( $field_name, $post_id );
65 if ( ! isset( $field_object['choices'] ) ) {
66 return 0; // Not a "select" field.
67 }
68 if ( is_array( $field_value ) ) {
69 return 0; // Not handled (currently).
70 }
71 if ( ! isset( $field_object['choices'][ $field_value ] ) ) {
72 return 0; // Value does not exist.
73 }
74
75 $n = 0;
76
77 /**
78 * Filters the field value before it is used to save the insert data.
79 *
80 * The value is used as an array key, so it needs to be an integer or a
81 * string. If your custom field values are arrays or objects, use this
82 * filter hook to convert them into strings.
83 *
84 * @param mixed $field_content The ACF field value.
85 * @param string $field_name The ACF field name.
86 * @param int $post_id The post ID.
87 *
88 * @return string|int The field value.
89 */
90 $value = apply_filters(
91 'relevanssi_acf_field_value',
92 $field_object['choices'][ $field_value ],
93 $field_name,
94 $post_id
95 );
96
97 if ( $value && ( is_integer( $value ) || is_string( $value ) ) ) {
98 $min_word_length = get_option( 'relevanssi_min_word_length', 3 );
99
100 /** This filter is documented in lib/indexing.php */
101 $value_tokens = apply_filters( 'relevanssi_indexing_tokens', relevanssi_tokenize( $value, true, $min_word_length, 'indexing' ), 'custom_field' );
102 foreach ( $value_tokens as $token => $count ) {
103 ++$n;
104 if ( ! isset( $insert_data[ $token ]['customfield'] ) ) {
105 $insert_data[ $token ]['customfield'] = 0;
106 }
107 $insert_data[ $token ]['customfield'] += $count;
108
109 // Premium indexes more detail about custom fields.
110 if ( function_exists( 'relevanssi_customfield_detail' ) ) {
111 $insert_data = relevanssi_customfield_detail( $insert_data, $token, $count, $field_name );
112 }
113 }
114 }
115
116 return $n;
117 }
118
119 /**
120 * Adds a Relevanssi exclude setting to ACF fields.
121 *
122 * @param array $field The field object array.
123 */
124 function relevanssi_acf_exclude_setting( $field ) {
125 if ( ! function_exists( 'acf_render_field_setting' ) ) {
126 return;
127 }
128 if ( 'clone' === $field['type'] ) {
129 return;
130 }
131 acf_render_field_setting(
132 $field,
133 array(
134 'label' => __( 'Exclude from Relevanssi index', 'relevanssi' ),
135 'instructions' => __( 'If this setting is enabled, Relevanssi will not index the value of this field for posts.', 'relevanssi' ),
136 'name' => 'relevanssi_exclude',
137 'type' => 'true_false',
138 'ui' => 1,
139 ),
140 true
141 );
142 }
143
144 /**
145 * Excludes ACF fields based on the exclude setting.
146 *
147 * Hooks on to relevanssi_index_custom_fields.
148 *
149 * @param array $fields The list of custom fields to index.
150 * @param int $post_id The post ID.
151 *
152 * @return array Filtered list of custom fields.
153 */
154 function relevanssi_acf_exclude_fields( $fields, $post_id ) {
155 $included_fields = array();
156 $excluded_fields = array();
157
158 /**
159 * Filters the types of ACF fields to exclude from indexing.
160 *
161 * By default, blocks 'repeater', 'flexible_content' and 'group' are
162 * excluded from Relevanssi indexing. You can add other field types here.
163 *
164 * @param array $excluded_field_types The field types to exclude.
165 */
166 $blocked_field_types = apply_filters(
167 'relevanssi_blocked_field_types',
168 array( 'repeater', 'flexible_content', 'group' )
169 );
170
171 global $post;
172 foreach ( $fields as $field ) {
173 $global_post = $post; // ACF fields can change the global $post.
174 $field_object = get_field_object( $field );
175 $post = $global_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
176
177 if ( ! $field_object || ! is_array( $field_object ) ) {
178 $field_id = relevanssi_acf_get_field_id( $field, $post_id );
179 if ( ! $field_id ) {
180 // No field ID -> not an ACF field. Include.
181 $included_fields[] = $field;
182 } else {
183 /*
184 * This field has a field ID, but get_field_object() does not
185 * return a field object. This may be a clone field, in which
186 * case we can try to get the field object from the field ID.
187 * Clone fields have keys like field_xxx_field_yyy, where the
188 * field_yyy is the part we need.
189 */
190 $field_id = preg_replace( '/.*_(field_.*)/', '$1', $field_id );
191 $field_object = get_field_object( $field_id );
192 }
193 }
194 if ( $field_object ) {
195 /**
196 * Filters the ACF field object.
197 *
198 * If the filter returns a false value, Relevanssi will not index
199 * the field.
200 *
201 * @param array $field_object The field object.
202 * @param int $post_id The post ID.
203 *
204 * @return array The filtered field object.
205 */
206 $field_object = apply_filters(
207 'relevanssi_acf_field_object',
208 $field_object,
209 $post_id
210 );
211
212 if ( ! $field_object ) {
213 continue;
214 }
215 if ( isset( $field_object['relevanssi_exclude'] ) && 1 === $field_object['relevanssi_exclude'] ) {
216 continue;
217 }
218 if ( relevanssi_acf_is_parent_excluded( $field_object ) ) {
219 continue;
220 }
221 if ( isset( $field_object['type'] ) && in_array( $field_object['type'], $blocked_field_types, true ) ) {
222 continue;
223 }
224 $included_fields[] = $field;
225 }
226 }
227 return $included_fields;
228 }
229
230 /**
231 * Checks if the field has an excluded parent field.
232 *
233 * If the field has a "parent" value set, this function gets the parent field
234 * post based on the post ID in the "parent" value. This is done recursively
235 * until we reach the top or find an excluded parent.
236 *
237 * @param array $field_object The field object.
238 *
239 * @return bool Returns true if the post has an excluded parent.
240 */
241 function relevanssi_acf_is_parent_excluded( $field_object ) {
242 if ( isset( $field_object['parent'] ) ) {
243 $parent = $field_object['parent'];
244 if ( $parent ) {
245 $parent_field_post = get_post( $parent );
246 if ( $parent_field_post ) {
247 $parent_object = get_field_object( $parent_field_post->post_name );
248 if ( $parent_object ) {
249 if ( isset( $parent_object['relevanssi_exclude'] ) && 1 === $parent_object['relevanssi_exclude'] ) {
250 return true;
251 }
252 return relevanssi_acf_is_parent_excluded( $parent_object );
253 }
254 }
255 }
256 }
257 return false;
258 }
259
260 /**
261 * Gets the field ID from the field name.
262 *
263 * The field ID is stored in the postmeta table with the field name prefixed
264 * with an underscore as the key.
265 *
266 * @param string $field_name The field name.
267 * @param int $post_id The post ID.
268 *
269 * @return string The field ID.
270 */
271 function relevanssi_acf_get_field_id( $field_name, $post_id ) {
272 global $wpdb;
273
274 $field_id = $wpdb->get_var(
275 $wpdb->prepare(
276 "SELECT meta_value FROM $wpdb->postmeta
277 WHERE post_id = %d
278 AND meta_key = %s",
279 $post_id,
280 '_' . $field_name
281 )
282 );
283 return $field_id;
284 }
1 <?php
2 /**
3 * /lib/compatibility/aioseo.php
4 *
5 * All-in-One SEO noindex filtering function.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_do_not_index', 'relevanssi_aioseo_noindex', 10, 2 );
14 add_filter( 'relevanssi_indexing_restriction', 'relevanssi_aioseo_exclude' );
15 add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_aioseo_form', 20 );
16 add_action( 'relevanssi_indexing_options', 'relevanssi_aioseo_options' );
17
18 /**
19 * Blocks indexing of posts marked "noindex" in the All-in-One SEO settings.
20 *
21 * Attaches to the 'relevanssi_do_not_index' filter hook.
22 *
23 * @param boolean $do_not_index True, if the post shouldn't be indexed.
24 * @param integer $post_id The post ID number.
25 *
26 * @return string|boolean If the post shouldn't be indexed, this returns
27 * 'aioseo_seo'. The value may also be a boolean.
28 */
29 function relevanssi_aioseo_noindex( bool $do_not_index, int $post_id ) {
30 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
31 return $do_not_index;
32 }
33 $noindex_posts = relevanssi_aioseo_get_noindex_posts();
34 if ( in_array( $post_id, $noindex_posts, true ) ) {
35 $do_not_index = 'All-in-One SEO';
36 }
37 return $do_not_index;
38 }
39
40 /**
41 * Excludes the "noindex" posts from Relevanssi indexing.
42 *
43 * Adds a MySQL query restriction that blocks posts that have the aioseo SEO
44 * "noindex" setting set to "1" from indexing.
45 *
46 * @param array $restriction An array with two values: 'mysql' for the MySQL
47 * query restriction to modify, 'reason' for the reason of restriction.
48 */
49 function relevanssi_aioseo_exclude( array $restriction ) {
50 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
51 return $restriction;
52 }
53
54 global $wpdb;
55
56 $restriction['mysql'] .= " AND post.ID NOT IN (SELECT post_id FROM
57 {$wpdb->prefix}aioseo_posts WHERE robots_noindex = '1' ) ";
58 $restriction['reason'] .= ' All-in-One SEO';
59
60 return $restriction;
61 }
62
63 /**
64 * Fetches the post IDs where robots_noindex is set to 1 in the aioseo_posts
65 * table.
66 *
67 * @return array An array of post IDs.
68 */
69 function relevanssi_aioseo_get_noindex_posts() {
70 global $wpdb, $relevanssi_aioseo_noindex_cache;
71 if ( ! empty( $relevanssi_aioseo_noindex_cache ) ) {
72 return $relevanssi_aioseo_noindex_cache;
73 }
74 $relevanssi_aioseo_noindex_cache = $wpdb->get_col( "SELECT post_id FROM {$wpdb->prefix}aioseo_posts WHERE 'robots_noindex' = '1'" );
75 return $relevanssi_aioseo_noindex_cache;
76 }
77
78 /**
79 * Prints out the form fields for disabling the feature.
80 */
81 function relevanssi_aioseo_form() {
82 $seo_noindex = get_option( 'relevanssi_seo_noindex' );
83 $seo_noindex = relevanssi_check( $seo_noindex );
84
85 ?>
86 <tr>
87 <th scope="row">
88 <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use All-in-One SEO noindex', 'relevanssi' ); ?></label>
89 </th>
90 <td>
91 <label for='relevanssi_seo_noindex'>
92 <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
93 <?php esc_html_e( 'Use All-in-One SEO noindex.', 'relevanssi' ); ?>
94 </label>
95 <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in All-in-One SEO settings.', 'relevanssi' ); ?></p>
96 </td>
97 </tr>
98 <?php
99 }
100
101 /**
102 * Saves the SEO No index option.
103 *
104 * @param array $request An array of option values from the request.
105 */
106 function relevanssi_aioseo_options( array $request ) {
107 relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
108 }
1 <?php
2 /**
3 * /lib/compatibility/avada.php
4 *
5 * Avada theme compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter(
14 'fusion_live_search_query_args',
15 function ( $args ) {
16 $args['relevanssi'] = true;
17 return $args;
18 }
19 );
1 <?php
2 /**
3 * /lib/compatibility/bricks.php
4 *
5 * Bricks theme compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'bricks/posts/query_vars', 'relevanssi_bricks_enable', 10 );
14 add_filter( 'relevanssi_custom_field_value', 'relevanssi_bricks_values', 10, 2 );
15 add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_bricks' );
16 add_filter( 'option_relevanssi_index_fields', 'relevanssi_bricks_fix_none_setting' );
17 add_action( 'save_post', 'relevanssi_insert_edit', 99, 1 );
18
19 /**
20 * Enables Relevanssi in the query when the 's' query var is set.
21 *
22 * @param array $query_vars The query variables.
23 *
24 * @return array The query variables with the Relevanssi toggle enabled.
25 */
26 function relevanssi_bricks_enable( $query_vars ) {
27 if ( isset( $query_vars['s'] ) ) {
28 $query_vars['relevanssi'] = true;
29 }
30 return $query_vars;
31 }
32
33 /**
34 * Adds the `_bricks_page_content_2` to the list of indexed custom fields.
35 *
36 * @param array|boolean $fields An array of custom fields to index, or false.
37 *
38 * @return array An array of custom fields, including `_bricks_page_content_2`.
39 */
40 function relevanssi_add_bricks( $fields ) {
41 if ( ! is_array( $fields ) ) {
42 $fields = array();
43 }
44 if ( ! in_array( '_bricks_page_content_2', $fields, true ) ) {
45 $fields[] = '_bricks_page_content_2';
46 }
47
48 return $fields;
49 }
50
51 /**
52 * Includes only text from _bricks_page_content_2 custom field.
53 *
54 * This function goes through the multilevel array of _bricks_page_content_2
55 * and only picks up the "text" elements inside it, discarding everything else.
56 *
57 * @param array $value An array of custom field values.
58 * @param string $field The name of the custom field.
59 *
60 * @return array An array containing a string with all the values concatenated
61 * together.
62 */
63 function relevanssi_bricks_values( $value, $field ) {
64 if ( '_bricks_page_content_2' !== $field ) {
65 return $value;
66 }
67
68 $content = '';
69 array_walk_recursive(
70 $value,
71 function ( $text, $key ) use ( &$content ) {
72 if ( 'text' === $key ) {
73 $content .= ' ' . $text;
74 }
75 }
76 );
77
78 return array( $content );
79 }
80
81 /**
82 * Makes sure the Bricks builder shortcode is included in the index, even when
83 * the custom field setting is set to 'none'.
84 *
85 * @param string $value The custom field indexing setting value. The parameter
86 * is ignored, Relevanssi disables this filter and then checks the option to
87 * see what the value is.
88 *
89 * @return string If value is undefined, it's set to '_bricks_page_content_2'.
90 */
91 function relevanssi_bricks_fix_none_setting( $value ) {
92 if ( ! $value ) {
93 $value = '_bricks_page_content_2';
94 }
95
96 return $value;
97 }
1 <?php
2 /**
3 * /lib/compatibility/elementor.php
4 *
5 * Elementor page builder compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_search_ok', 'relevanssi_block_elementor_library', 10, 2 );
14
15 /**
16 * Blocks Relevanssi from interfering with the Elementor Library searches.
17 *
18 * @param bool $ok Should Relevanssi be allowed to process the query.
19 * @param WP_Query $query The WP_Query object.
20 *
21 * @return bool Returns false, if this is an Elementor library search.
22 */
23 function relevanssi_block_elementor_library( bool $ok, WP_Query $query ): bool {
24 if ( 'elementor_library' === $query->query_vars['post_type'] ) {
25 $ok = false;
26 }
27 return $ok;
28 }
1 <?php
2 /**
3 * /lib/compatibility/fibosearch.php
4 *
5 * Fibo Search compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'dgwt/wcas/search_query/args', 'relevanssi_enable_relevanssi_in_fibo' );
14
15 /**
16 * Adds the 'relevanssi' parameter to the Fibo Search.
17 *
18 * Uses the dgwt/wcas/search_query_args filter hook to modify the search query.
19 *
20 * @param array $args The search arguments.
21 *
22 * @return array
23 */
24 function relevanssi_enable_relevanssi_in_fibo( $args ) {
25 $args['relevanssi'] = true;
26 return $args;
27 }
1 <?php
2 /**
3 * /lib/compatibility/groups.php
4 *
5 * Groups compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_groups_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the user is allowed to see the post.
17 *
18 * Only applies to published posts.
19 *
20 * @param boolean $post_ok Can the post be shown to the user.
21 * @param int $post_id The post ID.
22 *
23 * @return boolean $post_ok True if the user is allowed to see the post,
24 * otherwise false.
25 */
26 function relevanssi_groups_compatibility( $post_ok, $post_id ) {
27 $status = relevanssi_get_post_status( $post_id );
28
29 if ( 'publish' === $status ) {
30 // Only apply to published posts, don't apply to drafts.
31 $current_user = wp_get_current_user();
32 $post_ok = Groups_Post_Access::user_can_read_post( $post_id, $current_user->ID );
33 }
34
35 return $post_ok;
36 }
1 <?php
2 /**
3 * /lib/compatibility/gutenberg.php
4 *
5 * Gutenberg compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_content', 'relevanssi_gutenberg_block_rendering', 10, 2 );
14
15 /**
16 * Registers rest_after_insert_{post_type} actions for all indexed post types.
17 *
18 * Runs on `admin_init` action hook and registers the function
19 * `relevanssi_save_gutenberg_postdata` for all indexed post types.
20 *
21 * @see relevanssi_save_gutenberg_postdata
22 */
23 function relevanssi_register_gutenberg_actions() {
24 if ( ! RELEVANSSI_PREMIUM ) {
25 return;
26 }
27 $index_post_types = get_option( 'relevanssi_index_post_types', array() );
28 array_walk(
29 $index_post_types,
30 function ( $post_type ) {
31 if ( 'bogus' !== $post_type ) {
32 add_action(
33 'rest_after_insert_' . $post_type,
34 'relevanssi_save_gutenberg_postdata'
35 );
36 }
37 }
38 );
39 }
40
41 /**
42 * Renders Gutenberg blocks.
43 *
44 * Renders all sorts of Gutenberg blocks, including reusable blocks and ACF
45 * blocks. Also enables basic Gutenberg deindexing: you can add an extra CSS
46 * class 'relevanssi_noindex' to a block to stop it from being indexed by
47 * Relevanssi. This function is essentially the same as core do_blocks().
48 *
49 * @see do_blocks()
50 *
51 * @param string $content The post content.
52 * @param object $post_object The post object.
53 *
54 * @return string The post content with the rendered content added.
55 */
56 function relevanssi_gutenberg_block_rendering( $content, $post_object ) {
57 /**
58 * Filters whether the blocks are rendered or not.
59 *
60 * If this filter returns false, the blocks in this post are not rendered,
61 * and the post content is returned as such.
62 *
63 * @param boolean If true, render the blocks. Default true.
64 * @param object The post object.
65 */
66 if ( ! apply_filters( 'relevanssi_render_blocks', true, $post_object ) ) {
67 return $content;
68 }
69 $blocks = parse_blocks( $content );
70 $output = '';
71
72 foreach ( $blocks as $block ) {
73 /**
74 * Filters the Gutenberg block before it is rendered.
75 *
76 * If the block is non-empty after the filter and it's className
77 * parameter is not 'relevanssi_noindex', it will be passed on to the
78 * render_block() function for rendering.
79 *
80 * @see render_block
81 *
82 * @param array $block The Gutenberg block element.
83 */
84 $block = apply_filters( 'relevanssi_block_to_render', $block );
85
86 if ( ! $block ) {
87 continue;
88 }
89
90 if (
91 isset( $block['attrs']['className'] )
92 && false !== strstr( $block['attrs']['className'], 'relevanssi_noindex' )
93 ) {
94 continue;
95 }
96
97 $block = relevanssi_process_inner_blocks( $block );
98
99 /**
100 * Filters the Gutenberg block after it is rendered.
101 *
102 * The value is the output from render_block( $block ). Feel free to
103 * modify it as you wish.
104 *
105 * @see render_block
106 *
107 * @param string The rendered block content.
108 * @param array $block The Gutenberg block being rendered.
109 *
110 * @return string The filtered block content.
111 */
112 $output .= apply_filters( 'relevanssi_rendered_block', render_block( $block ), $block );
113
114 }
115
116 // If there are blocks in this content, we shouldn't run wpautop() on it later.
117 $priority = has_filter( 'the_content', 'wpautop' );
118 if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) {
119 remove_filter( 'the_content', 'wpautop', $priority );
120 add_filter( 'the_content', '_restore_wpautop_hook', $priority + 1 );
121 }
122
123 return $output;
124 }
125
126 /**
127 * Runs recursively through inner blocks to filter them.
128 *
129 * Runs relevanssi_block_to_render and the relevanssi_noindex CSS class check
130 * on all inner blocks. If inner blocks are filtered out, they will be removed
131 * with empty blocks of the type "core/fake". Removing the inner blocks causes
132 * problems; that's why they are replaced. The blocks are rendered here;
133 * everything will be rendered once at the top level.
134 *
135 * @param array $block A Gutenberg block.
136 *
137 * @return array The filtered block.
138 */
139 function relevanssi_process_inner_blocks( $block ) {
140 $innerblocks_to_keep = array();
141
142 $empty_block = array(
143 'blockName' => 'core/fake',
144 'attrs' => array(),
145 'innerHTML' => '',
146 'innerBlocks' => array(),
147 );
148
149 foreach ( $block['innerBlocks'] as $inner_block ) {
150 /* Filter documented in /lib/compatibility/gutenberg.php. */
151 $inner_block = apply_filters( 'relevanssi_block_to_render', $inner_block );
152
153 if ( ! $inner_block ) {
154 $innerblocks_to_keep[] = $empty_block;
155 continue;
156 }
157
158 if (
159 isset( $inner_block['attrs']['className'] )
160 && false !== strstr( $inner_block['attrs']['className'], 'relevanssi_noindex' )
161 ) {
162 $innerblocks_to_keep[] = $empty_block;
163 continue;
164 }
165
166 $inner_block = relevanssi_process_inner_blocks( $inner_block );
167
168 $innerblocks_to_keep[] = $inner_block;
169 }
170
171 $block['innerBlocks'] = $innerblocks_to_keep;
172 return $block;
173 }
1 <?php
2 /**
3 * /lib/compatibility/jetsmartfilters.php
4 *
5 * JetSmartFilters compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_action( 'pre_get_posts', 'relevanssi_jetsmartfilters', 9999 );
14
15 /**
16 * Makes JetSmartFilters use posts from Relevanssi.
17 *
18 * @param WP_Query $wp_query The wp_query object.
19 */
20 function relevanssi_jetsmartfilters( $wp_query ) {
21 if (
22 ! isset( $wp_query->query['jet_smart_filters'] )
23 || empty( $wp_query->query['s'] )
24 ) {
25 return;
26 }
27
28 $args = array(
29 's' => $wp_query->query['s'],
30 'fields' => 'ids',
31 'posts_per_page' => -1,
32 'relevanssi' => true,
33 );
34
35 $relevanssi_query = new WP_Query( $args );
36
37 $results = ! empty( $relevanssi_query->posts )
38 ? $relevanssi_query->posts
39 : array( 0 );
40
41 $wp_query->set( 'post__in', $results );
42 $wp_query->set( 'post_type', 'any' );
43 $wp_query->set( 'post_status', 'any' );
44 $wp_query->set( 'orderby', 'post__in' );
45 $wp_query->set( 'order', 'DESC' );
46 $wp_query->set( 's', false );
47 }
1 <?php
2 /**
3 * /lib/compatibility/memberpress.php
4 *
5 * Memberpress compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_memberpress_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the user is allowed to see the post.
17 *
18 * @param boolean $post_ok Can the post be shown to the user.
19 * @param int $post_id The post ID.
20 *
21 * @return boolean $post_ok True if the user is allowed to see the post,
22 * otherwise false.
23 */
24 function relevanssi_memberpress_compatibility( $post_ok, $post_id ) {
25 $post = get_post( $post_id );
26 if ( MeprRule::is_locked( $post ) ) {
27 $post_ok = false;
28 }
29
30 return $post_ok;
31 }
1 <?php
2 /**
3 * /lib/compatibility/members.php
4 *
5 * Members compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_members_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the user is allowed to see the post.
17 *
18 * Only applies to private posts and only if the "content permissions" feature
19 * is enabled.
20 *
21 * @param boolean $post_ok Can the post be shown to the user.
22 * @param int $post_id The post ID.
23 *
24 * @return boolean $post_ok True if the user is allowed to see the post,
25 * otherwise false.
26 */
27 function relevanssi_members_compatibility( $post_ok, $post_id ) {
28 $status = relevanssi_get_post_status( $post_id );
29
30 if ( 'private' === $status ) {
31 if ( members_content_permissions_enabled() ) {
32 $post_ok = members_can_current_user_view_post( $post_id );
33 }
34 }
35
36 return $post_ok;
37 }
1 <?php
2 /**
3 * /lib/compatibility/ninjatables.php
4 *
5 * Ninja Tables compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_content', 'relevanssi_index_ninja_tables' );
14
15 /**
16 * Indexes Ninja Tables table contents.
17 *
18 * Uses regular expression matching to find all the Ninja Tables shortcodes in
19 * the post content and then uses relevanssi_index_ninja_table() to convert the
20 * tables into strings.
21 *
22 * @uses $wpdb WordPress database abstraction.
23 * @see relevanssi_index_ninja_table()
24 *
25 * @param string $content The post content.
26 *
27 * @return string Post content with the Ninja Tables data.
28 */
29 function relevanssi_index_ninja_tables( $content ) {
30 $m = preg_match_all(
31 '/.*\[ninja_tables.*?id=["\'](\d+)["\'].*?\]/im',
32 $content,
33 $matches,
34 PREG_PATTERN_ORDER
35 );
36 if ( ! $m ) {
37 return $content;
38 }
39 foreach ( $matches[1] as $table_id ) {
40 $content .= ' ' . relevanssi_index_ninja_table( $table_id );
41 }
42
43 return $content;
44 }
45
46 /**
47 * Creates a string containing a Ninja Table table contents.
48 *
49 * The string contains the caption and the values from each row. The table
50 * title and description are also included, if they are set visible on the
51 * frontend.
52 *
53 * @uses $wpdb WordPress database abstraction.
54 *
55 * @param int $table_id The table ID.
56 *
57 * @return string The table content as a string.
58 */
59 function relevanssi_index_ninja_table( $table_id ) {
60 global $wpdb;
61 $table_post = get_post( $table_id );
62 $table_settings = get_post_meta( $table_id, '_ninja_table_settings', true );
63 $table_contents = '';
64
65 if ( isset( $table_settings['show_description'] ) && '1' === $table_settings['show_description'] ) {
66 $table_contents .= ' ' . $table_post->post_content;
67 }
68 if ( isset( $table_settings['show_title'] ) && '1' === $table_settings['show_title'] ) {
69 $table_contents .= ' ' . $table_post->post_title;
70 }
71 $table_contents .= ' ' . get_post_meta( $table_id, '_ninja_table_caption', true );
72
73 $rows = $wpdb->get_results(
74 $wpdb->prepare(
75 "SELECT value FROM {$wpdb->prefix}ninja_table_items WHERE table_id=%d",
76 $table_id
77 )
78 );
79 foreach ( $rows as $row ) {
80 $array_values = array_map(
81 function ( $value ) {
82 if ( is_object( $value ) ) {
83 return '';
84 }
85 return strval( $value );
86 },
87 array_values( get_object_vars( json_decode( $row->value ) ) )
88 );
89
90 $table_contents .= ' ' . implode( ' ', $array_values );
91 }
92
93 return $table_contents;
94 }
1 <?php
2 /**
3 * /lib/compatibility/oxygen.php
4 *
5 * Oxygen Builder compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_custom_field_value', 'relevanssi_oxygen_compatibility', 10, 3 );
14 add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_oxygen' );
15 add_filter( 'option_relevanssi_index_fields', 'relevanssi_oxygen_fix_none_setting' );
16 add_filter( 'relevanssi_oxygen_section_content', 'relevanssi_oxygen_code_block' );
17 add_filter( 'relevanssi_oxygen_section_content', 'relevanssi_oxygen_rich_text' );
18 add_action( 'save_post', 'relevanssi_insert_edit', 99, 1 );
19
20 /**
21 * Cleans up the Oxygen Builder custom field for Relevanssi consumption.
22 *
23 * Splits up the big custom field content from ct_builder_shortcodes into
24 * sections ([ct_section] tags). Each section can be processed with filters
25 * defined with `relevanssi_oxygen_section_filters`, for example to remove
26 * sections based on their "nicename" or "ct_category" values. After that the
27 * section is passed through the `relevanssi_oxygen_section_content` filter.
28 * Finally all shortcode tags are removed, leaving just the content.
29 *
30 * @param array $value An array of custom field values.
31 * @param string $field The name of the custom field. This function only looks
32 * at `ct_builder_shortcodes` fields.
33 * @param int $post_id The post ID.
34 *
35 * @return array|null An array of custom field values, null if no value exists.
36 */
37 function relevanssi_oxygen_compatibility( $value, $field, $post_id ) {
38 if ( 'ct_builder_json' === $field ) {
39 $json = array();
40 foreach ( $value as $row ) {
41 $json[] = json_decode( $row );
42 }
43
44 $content = '';
45 if ( isset( $json[0]->children ) ) {
46 foreach ( $json[0]->children as $child ) {
47 $content .= relevanssi_process_oxygen_child( $child );
48 }
49 }
50
51 $value[0] = $content;
52 return $value;
53 }
54
55 if ( 'ct_builder_shortcodes_revisions_dates' === $field ) {
56 return '';
57 }
58 if ( 'ct_builder_shortcodes_revisions' === $field ) {
59 return '';
60 }
61 if ( 'ct_builder_shortcodes' === $field ) {
62 if ( version_compare( CT_VERSION, '4.0', '>=' ) ) {
63 return null;
64 }
65 if ( empty( $value ) ) {
66 return null;
67 }
68
69 $content_tags = explode( '[ct_section', $value[0] );
70 $page_content = '';
71
72 foreach ( $content_tags as $content ) {
73 if ( empty( $content ) ) {
74 continue;
75 }
76 if ( '[' !== substr( $content, 0, 1 ) ) {
77 $content = '[ct_section' . $content;
78 }
79 /**
80 * Allows defining filters to remove Oxygen Builder sections.
81 *
82 * The filters are arrays, with the array key defining the key and
83 * the value defining the value. If the filter array is
84 * array( 'nicename' => 'Hero BG' ), Relevanssi will look for
85 * sections that have "nicename":"Hero BG" in their settings and
86 * will remove those.
87 *
88 * @param array An array of filtering rules, defaults empty.
89 *
90 * @return array
91 */
92 $filters = apply_filters(
93 'relevanssi_oxygen_section_filters',
94 array()
95 );
96 array_walk(
97 $filters,
98 function ( $filter ) use ( &$content ) {
99 foreach ( $filter as $key => $value ) {
100 if ( stristr( $content, '"' . $key . '":"' . $value . '"' ) !== false ) {
101 $content = '';
102 }
103 }
104 }
105 );
106
107 $content = preg_replace(
108 array(
109 '/\[oxygen.*?\]/',
110 '/\[\/?ct_.*?\]/',
111 '/\[\/?oxy_.*?\]/',
112 ),
113 ' ',
114 /**
115 * Filters the Oxygen Builder section content before the
116 * Oxygen Builder shortcode tags are removed.
117 *
118 * @param string $content The single section content.
119 * @param int $post_id The post ID.
120 *
121 * @return string
122 */
123 apply_filters(
124 'relevanssi_oxygen_section_content',
125 $content,
126 $post_id
127 )
128 );
129
130 $page_content .= $content;
131 }
132
133 $page_content = relevanssi_do_shortcode( $page_content );
134
135 $value[0] = $page_content;
136 }
137 return $value;
138 }
139
140 /**
141 * Recursively processes the Oxygen JSON data.
142 *
143 * This function extracts all the ct_content data from the JSON. All elements
144 * are run through the relevanssi_oxygen_element filter hook. You can use that
145 * filter hook to modify or to eliminate elements from the JSON.
146 *
147 * @param array $child The child element array.
148 *
149 * @return string The content from the child and the grandchildren.
150 */
151 function relevanssi_process_oxygen_child( $child ): string {
152 /**
153 * Filters the Oxygen JSON child element.
154 *
155 * If the filter returns an empty value, the child element and all its
156 * children will be ignored.
157 *
158 * @param array $child The JSON child element.
159 */
160 $child = apply_filters( 'relevanssi_oxygen_element', $child );
161 if ( empty( $child ) ) {
162 return '';
163 }
164
165 $child_content = ' ';
166 if ( isset( $child->options->ct_content ) ) {
167 $child_content .= $child->options->ct_content;
168 }
169
170 if ( isset( $child->options->original->{'code-php'} ) ) {
171 // For code and HTML blocks, strip all tags.
172 $child_content .= wp_strip_all_tags( $child->options->original->{'code-php'} );
173 }
174
175 if ( isset( $child->children ) ) {
176 foreach ( $child->children as $grandchild ) {
177 $child_content .= relevanssi_process_oxygen_child( $grandchild );
178 }
179 }
180
181 return $child_content;
182 }
183
184 /**
185 * Adds the Oxygen custom field to the list of indexed custom fields.
186 *
187 * @param array|boolean $fields An array of custom fields to index, or false.
188 *
189 * @return array An array of custom fields, including `ct_builder_json` or
190 * `ct_builder_shortcodes`.
191 */
192 function relevanssi_add_oxygen( $fields ) {
193 $oxygen_field = version_compare( CT_VERSION, '4.0', '>=' )
194 ? 'ct_builder_json'
195 : 'ct_builder_shortcodes';
196 if ( ! is_array( $fields ) ) {
197 $fields = array();
198 }
199 if ( ! in_array( $oxygen_field, $fields, true ) ) {
200 $fields[] = $oxygen_field;
201 }
202
203 return $fields;
204 }
205
206 /**
207 * Makes sure the Oxygen builder shortcode is included in the index, even when
208 * the custom field setting is set to 'none'.
209 *
210 * @param string $value The custom field indexing setting value. The parameter
211 * is ignored, Relevanssi disables this filter and then checks the option to
212 * see what the value is.
213 *
214 * @return string If value is undefined, it's set to 'ct_builder_json' or
215 * 'ct_builder_shortcodes'.
216 */
217 function relevanssi_oxygen_fix_none_setting( $value ) {
218 if ( ! $value ) {
219 $value = version_compare( CT_VERSION, '4.0', '>=' )
220 ? 'ct_builder_json'
221 : 'ct_builder_shortcodes';
222 }
223
224 return $value;
225 }
226
227 /**
228 * Indexes the Base64 encoded PHP & HTML code block contents.
229 *
230 * @param string $content The section content from the
231 * relevanssi_oxygen_section_content filter hook.
232 *
233 * @return string $content The content with the decoded code block content
234 * added to the end.
235 */
236 function relevanssi_oxygen_code_block( $content ) {
237 if ( preg_match_all( '/\[ct_code_block.*?ct_code_block\]/', $content, $matches ) ) {
238 foreach ( $matches[0] as $match ) {
239 if ( preg_match_all( '/"code-php":"(.*?)"/', $match, $block_matches ) ) {
240 foreach ( $block_matches[1] as $encoded_text ) {
241 $content .= ' ' . base64_decode( $encoded_text ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions
242 }
243 }
244 }
245 }
246 return $content;
247 }
248
249 /**
250 * Removes the Oxygen rich text shortcode.
251 *
252 * @param string $content The content of the Oxygen section.
253 *
254 * @return string The content with the oxy_rich_text shortcodes removed.
255 */
256 function relevanssi_oxygen_rich_text( $content ) {
257 $content = preg_replace( '/\[\/?oxy_rich_text.*?\]/im', '', $content );
258 return $content;
259 }
1 <?php
2 /**
3 * /lib/compatibility/paidmembershippro.php
4 *
5 * Paid Membership Pro compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_paidmembershippro_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the user is allowed to see the post.
17 *
18 * @param boolean $post_ok Can the post be shown to the user.
19 * @param int $post_id The post ID.
20 *
21 * @return boolean $post_ok True if the user is allowed to see the post,
22 * otherwise false.
23 */
24 function relevanssi_paidmembershippro_compatibility( $post_ok, $post_id ) {
25 $pmpro_active = get_option( 'pmpro_filterqueries', 0 );
26
27 if ( $pmpro_active ) {
28 $status = relevanssi_get_post_status( $post_id );
29
30 if ( 'publish' === $status ) {
31 // Only apply to published posts, don't apply to drafts.
32 $current_user = wp_get_current_user();
33 $post_ok = pmpro_has_membership_access( $post_id, $current_user->ID );
34 }
35 }
36
37 return $post_ok;
38 }
1 <?php
2 /**
3 * /lib/compatibility/polylang.php
4 *
5 * Polylang compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_modify_wp_query', 'relevanssi_polylang_filter' );
14 add_filter( 'relevanssi_where', 'relevanssi_polylang_where_include_terms' );
15 add_filter( 'relevanssi_hits_filter', 'relevanssi_polylang_term_filter' );
16
17 /**
18 * Removes the Polylang language filters.
19 *
20 * If the Polylang allow all option ('relevanssi_polylang_all_languages') is
21 * enabled this removes the Polylang language filter. By default Polylang
22 * filters the languages using a taxonomy query.
23 *
24 * @param object $query WP_Query object we need to clean up.
25 */
26 function relevanssi_polylang_filter( $query ) {
27 $polylang_allow_all = get_option( 'relevanssi_polylang_all_languages' );
28 if ( 'on' === $polylang_allow_all ) {
29 $ok_queries = array();
30
31 if ( ! isset( $query->tax_query ) ) {
32 // No tax query set, backing off.
33 return;
34 }
35
36 if ( ! isset( $query->tax_query->queries ) || ! is_array( $query->tax_query->queries ) ) {
37 // No tax query set, backing off.
38 return;
39 }
40
41 foreach ( $query->tax_query->queries as $tax_query ) {
42 if ( isset( $tax_query['taxonomy'] ) && 'language' !== $tax_query['taxonomy'] ) {
43 // Not a language tax query.
44 $ok_queries[] = $tax_query;
45 }
46 }
47 $query->tax_query->queries = $ok_queries;
48
49 if ( isset( $query->query_vars['tax_query'] ) ) {
50 // Tax queries can be here as well, so let's sweep this one too.
51 $ok_queries = array();
52 foreach ( $query->query_vars['tax_query'] as $tax_query ) {
53 if ( isset( $tax_query['taxonomy'] ) ) {
54 if ( 'language' !== $tax_query['taxonomy'] ) {
55 $ok_queries[] = $tax_query;
56 }
57 } else {
58 // Relation parameter most likely.
59 $ok_queries[] = $tax_query;
60 }
61 }
62 $query->query_vars['tax_query'] = $ok_queries;
63 }
64
65 if ( isset( $query->query_vars['taxonomy'] ) && 'language' === $query->query_vars['taxonomy'] ) {
66 // Another way to set the taxonomy.
67 unset( $query->query_vars['taxonomy'] );
68 unset( $query->query_vars['term'] );
69 }
70 }
71
72 return $query;
73 }
74
75 /**
76 * Allows taxonomy terms in language-restricted searches.
77 *
78 * This is a bit of a hack, where the language taxonomy WHERE clause is modified
79 * on the go to allow all posts with the post ID -1 (which means taxonomy terms
80 * and users). This may break suddenly in updates, but I haven't come up with a
81 * better way so far.
82 *
83 * @param string $where The WHERE clause to modify.
84 *
85 * @return string The WHERE clause with additional filtering included.
86 *
87 * @since 2.1.6
88 */
89 function relevanssi_polylang_where_include_terms( $where ) {
90 global $wpdb;
91
92 $current_language = substr( get_locale(), 0, 2 );
93 if ( function_exists( 'pll_current_language' ) ) {
94 $current_language = pll_current_language();
95 }
96 $languages = get_terms( array( 'taxonomy' => 'language' ) );
97 $language_id = 0;
98 foreach ( $languages as $language ) {
99 if (
100 ! is_wp_error( $language ) &&
101 $language instanceof WP_Term &&
102 $language->slug === $current_language
103 ) {
104 $language_id = intval( $language->term_id );
105 break;
106 }
107 }
108 // Language ID should now have current language ID.
109 if ( 0 !== $language_id ) {
110 // Do a simple search-and-replace to modify the query.
111 $where = preg_replace( '/\s+/', ' ', $where );
112 $where = preg_replace( '/\(\s/', '(', $where );
113 $where = str_replace(
114 "AND relevanssi.doc IN (SELECT DISTINCT(tr.object_id) FROM {$wpdb->prefix}term_relationships AS tr WHERE tr.term_taxonomy_id IN ($language_id))",
115 "AND (relevanssi.doc IN ( SELECT DISTINCT(tr.object_id) FROM {$wpdb->prefix}term_relationships AS tr WHERE tr.term_taxonomy_id IN ($language_id)) OR (relevanssi.doc = -1))",
116 $where
117 );
118 }
119 return $where;
120 }
121
122 /**
123 * Filters out taxonomy terms in the wrong language.
124 *
125 * If all languages are not allowed, this filter goes through the results and
126 * removes the taxonomy terms in the wrong language. This can't be done in the
127 * original query because the term language information is slightly hard to
128 * find.
129 *
130 * @param array $hits The found posts are in $hits[0].
131 *
132 * @return array The $hits array with the unwanted posts removed.
133 *
134 * @since 2.1.6
135 */
136 function relevanssi_polylang_term_filter( $hits ) {
137 $polylang_allow_all = get_option( 'relevanssi_polylang_all_languages' );
138 if ( 'on' !== $polylang_allow_all ) {
139 $current_language = substr( get_locale(), 0, 2 );
140 if ( function_exists( 'pll_current_language' ) ) {
141 $current_language = pll_current_language();
142 }
143 $accepted_hits = array();
144 foreach ( $hits[0] as $hit ) {
145 $original_hit = $hit;
146 if ( is_numeric( $hit ) ) {
147 // In case "fields" is set to "ids", fetch the post object we need.
148 $original_hit = $hit;
149 $hit = get_post( $hit );
150 }
151 if ( ! isset( $hit->post_content ) && isset( $hit->ID ) ) {
152 // The "fields" is set to "id=>parent".
153 $original_hit = $hit;
154 $hit = get_post( $hit->ID );
155 }
156
157 if ( isset( $hit->ID ) && -1 === $hit->ID && isset( $hit->term_id ) ) {
158 $term_id = intval( $hit->term_id );
159 $translations = pll_get_term_translations( $term_id );
160 if (
161 isset( $translations[ $current_language ] ) &&
162 $translations[ $current_language ] === $term_id
163 ) {
164 $accepted_hits[] = $original_hit;
165 }
166 } else {
167 $accepted_hits[] = $original_hit;
168 }
169 }
170 $hits[0] = $accepted_hits;
171 }
172 return $hits;
173 }
174
175 /**
176 * Returns the term_taxonomy_id matching the Polylang language based on locale.
177 *
178 * @param string $locale The locale string for the language.
179 *
180 * @return int The term_taxonomy_id for the language; 0 if nothing is found.
181 */
182 function relevanssi_get_language_term_taxonomy_id( $locale ) {
183 global $wpdb, $relevanssi_language_term_ids;
184
185 if ( isset( $relevanssi_language_term_ids[ $locale ] ) ) {
186 return $relevanssi_language_term_ids[ $locale ];
187 }
188
189 $languages = $wpdb->get_results(
190 "SELECT term_taxonomy_id, description FROM $wpdb->term_taxonomy " .
191 "WHERE taxonomy = 'language'"
192 );
193 $term_id = 0;
194 foreach ( $languages as $row ) {
195 $description = unserialize( $row->description ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions
196 if ( $description['locale'] === $locale ) {
197 $term_id = $row->term_taxonomy_id;
198 break;
199 }
200 }
201
202 $relevanssi_language_term_ids[ $locale ] = $term_id;
203
204 return $term_id;
205 }
1 <?php
2 /**
3 * /lib/compatibility/pretty-links.php
4 *
5 * Pretty Links compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_admin_search_ok', 'relevanssi_pretty_links_ok', 10, 2 );
14 add_filter( 'relevanssi_prevent_default_request', 'relevanssi_pretty_links_ok', 10, 2 );
15 add_filter( 'relevanssi_search_ok', 'relevanssi_pretty_links_ok', 10, 2 );
16
17 /**
18 * Returns false if the query post type is set to 'pretty-link'.
19 *
20 * @param boolean $ok Whether to allow the query.
21 * @param WP_Query $query The WP_Query object.
22 *
23 * @return boolean False if this is a Pretty Links query.
24 */
25 function relevanssi_pretty_links_ok( $ok, $query ) {
26 if ( isset( $query->query['post_type'] ) && 'pretty-link' === $query->query['post_type'] ) {
27 $ok = false;
28 }
29 return $ok;
30 }
1 <?php
2 /**
3 * /lib/compatibility/product-gtin-ean-upc-isbn-for-woocommerce.php.php
4 *
5 * Adds Product GTIN (EAN, UPC, ISBN) for WooCommerce support for Relevanssi.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_action( 'pre_option_wpm_pgw_search_by_code', 'relevanssi_disable_gtin_code' );
14 add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_wpm_gtin_code' );
15 add_filter( 'option_relevanssi_index_fields', 'relevanssi_wpm_pgw_fix_none_setting' );
16
17 /**
18 * Disables the 'wpm_pgw_search_by_code' option.
19 *
20 * If this option is enabled, it will break Relevanssi search when there's a
21 * match for the code.
22 *
23 * @return string 'no'.
24 */
25 function relevanssi_disable_gtin_code() {
26 return 'no';
27 }
28
29 /**
30 * Adds the `_wpm_gtin_code` to the list of indexed custom fields.
31 *
32 * @param array|boolean $fields An array of custom fields to index, or false.
33 *
34 * @return array An array of custom fields, including `_wpm_gtin_code`.
35 */
36 function relevanssi_add_wpm_gtin_code( $fields ) {
37 if ( ! is_array( $fields ) ) {
38 $fields = array();
39 }
40 if ( ! in_array( '_wpm_gtin_code', $fields, true ) ) {
41 $fields[] = '_wpm_gtin_code';
42 }
43 return $fields;
44 }
45
46 /**
47 * Makes sure the GTIN code is included in the index, even when the custom field
48 * setting is set to 'none'.
49 *
50 * @param string $value The custom field indexing setting value. The parameter
51 * is ignored, Relevanssi disables this filter and then checks the option to
52 * see what the value is.
53 *
54 * @return string If value is undefined, it's set to '_wpm_gtin_code'.
55 */
56 function relevanssi_wpm_pgw_fix_none_setting( $value ) {
57 if ( ! $value ) {
58 $value = '_wpm_gtin_code';
59 }
60
61 return $value;
62 }
1 <?php
2 /**
3 * /lib/compatibility/rankmath.php
4 *
5 * Rank Math noindex filtering function.
6 *
7 * @package Relevanssi
8 * @license https://wordpress.org/about/gpl/ GNU General Public License
9 * @see https://www.relevanssi.com/
10 */
11
12 add_filter( 'relevanssi_do_not_index', 'relevanssi_rankmath_noindex', 10, 2 );
13 add_filter( 'relevanssi_indexing_restriction', 'relevanssi_rankmath_exclude' );
14 add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_rankmath_form', 20 );
15 add_action( 'relevanssi_indexing_options', 'relevanssi_rankmath_options' );
16
17 /**
18 * Blocks indexing of posts marked "noindex" in the Rank Math settings.
19 *
20 * Attaches to the 'relevanssi_do_not_index' filter hook.
21 *
22 * @param boolean $do_not_index True, if the post shouldn't be indexed.
23 * @param integer $post_id The post ID number.
24 *
25 * @return string|boolean If the post shouldn't be indexed, this returns
26 * 'RankMath'. The value may also be a boolean.
27 */
28 function relevanssi_rankmath_noindex( $do_not_index, $post_id ) {
29 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
30 return $do_not_index;
31 }
32 $noindex = get_post_meta( $post_id, 'rank_math_robots', true );
33 if ( is_array( $noindex ) && in_array( 'noindex', $noindex, true ) ) {
34 $do_not_index = 'RankMath';
35 }
36 return $do_not_index;
37 }
38
39 /**
40 * Excludes the "noindex" posts from Relevanssi indexing.
41 *
42 * Adds a MySQL query restriction that blocks posts that have the Rank Math
43 * "rank_math_robots" setting set to something that includes "noindex".
44 *
45 * @param array $restriction An array with two values: 'mysql' for the MySQL
46 * query restriction to modify, 'reason' for the reason of restriction.
47 */
48 function relevanssi_rankmath_exclude( $restriction ) {
49 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
50 return $restriction;
51 }
52
53 global $wpdb;
54
55 // Backwards compatibility code for 2.8.0, remove at some point.
56 if ( is_string( $restriction ) ) {
57 $restriction = array(
58 'mysql' => $restriction,
59 'reason' => '',
60 );
61 }
62
63 $restriction['mysql'] .= " AND post.ID NOT IN (SELECT post_id FROM
64 $wpdb->postmeta WHERE meta_key = 'rank_math_robots'
65 AND meta_value LIKE '%noindex%' ) ";
66 $restriction['reason'] .= ' Rank Math';
67 return $restriction;
68 }
69
70 /**
71 * Prints out the form fields for disabling the feature.
72 */
73 function relevanssi_rankmath_form() {
74 $seo_noindex = get_option( 'relevanssi_seo_noindex' );
75 $seo_noindex = relevanssi_check( $seo_noindex );
76
77 ?>
78 <tr>
79 <th scope="row">
80 <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use Rank Math SEO noindex', 'relevanssi' ); ?></label>
81 </th>
82 <td>
83 <label for='relevanssi_seo_noindex'>
84 <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
85 <?php esc_html_e( 'Use Rank Math SEO noindex.', 'relevanssi' ); ?>
86 </label>
87 <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in Rank Math SEO settings.', 'relevanssi' ); ?></p>
88 </td>
89 </tr>
90 <?php
91 }
92
93 /**
94 * Saves the SEO No index option.
95 *
96 * @param array $request An array of option values from the request.
97 */
98 function relevanssi_rankmath_options( array $request ) {
99 relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
100 }
1 <?php
2 /**
3 * /lib/compatibility/restrictcontentpro.php
4 *
5 * Restrict Content Pro compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_restrictcontentpro_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the user is allowed to see the post.
17 *
18 * @param boolean $post_ok Can the post be shown to the user.
19 * @param int $post_id The post ID.
20 *
21 * @return boolean $post_ok True if the user is allowed to see the post,
22 * otherwise false.
23 */
24 function relevanssi_restrictcontentpro_compatibility( $post_ok, $post_id ) {
25 if ( ! $post_ok ) {
26 return $post_ok;
27 }
28
29 $post_ok = rcp_user_can_access( get_current_user_id(), $post_id );
30
31 return $post_ok;
32 }
1 <?php
2 /**
3 * /lib/compatibility/seoframework.php
4 *
5 * The SEO Framework noindex filtering function.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_do_not_index', 'relevanssi_seoframework_noindex', 10, 2 );
14 add_filter( 'relevanssi_indexing_restriction', 'relevanssi_seoframework_exclude' );
15 add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_seoframework_form', 20 );
16 add_action( 'relevanssi_indexing_options', 'relevanssi_seoframework_options' );
17
18 /**
19 * Blocks indexing of posts marked "Exclude this page from all search queries
20 * on this site." in the SEO Framework settings.
21 *
22 * Attaches to the 'relevanssi_do_not_index' filter hook.
23 *
24 * @param boolean $do_not_index True, if the post shouldn't be indexed.
25 * @param integer $post_id The post ID number.
26 *
27 * @return string|boolean If the post shouldn't be indexed, this returns
28 * 'SEO Framework'. The value may also be a boolean.
29 */
30 function relevanssi_seoframework_noindex( $do_not_index, $post_id ) {
31 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
32 return $do_not_index;
33 }
34
35 $noindex = get_post_meta( $post_id, 'exclude_local_search', true );
36 if ( '1' === $noindex ) {
37 $do_not_index = 'SEO Framework';
38 }
39 return $do_not_index;
40 }
41
42 /**
43 * Excludes the "noindex" posts from Relevanssi indexing.
44 *
45 * Adds a MySQL query restriction that blocks posts that have the SEO Framework
46 * "Exclude this page from all search queries on this site" setting set to "1"
47 * from indexing.
48 *
49 * @param array $restriction An array with two values: 'mysql' for the MySQL
50 * query restriction to modify, 'reason' for the reason of restriction.
51 */
52 function relevanssi_seoframework_exclude( $restriction ) {
53 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
54 return $restriction;
55 }
56
57 global $wpdb;
58
59 $restriction['mysql'] .= " AND post.ID NOT IN (SELECT post_id FROM
60 $wpdb->postmeta WHERE meta_key = 'exclude_local_search'
61 AND meta_value = '1' ) ";
62 $restriction['reason'] .= ' SEO Framework';
63 return $restriction;
64 }
65
66 /**
67 * Prints out the form fields for disabling the feature.
68 */
69 function relevanssi_seoframework_form() {
70 $seo_noindex = get_option( 'relevanssi_seo_noindex' );
71 $seo_noindex = relevanssi_check( $seo_noindex );
72
73 ?>
74 <tr>
75 <th scope="row">
76 <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use SEO Framework noindex', 'relevanssi' ); ?></label>
77 </th>
78 <td>
79 <label for='relevanssi_seo_noindex'>
80 <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
81 <?php esc_html_e( 'Use SEO Framework noindex.', 'relevanssi' ); ?>
82 </label>
83 <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in SEO Framework settings.', 'relevanssi' ); ?></p>
84 </td>
85 </tr>
86 <?php
87 }
88
89 /**
90 * Saves the SEO No index option.
91 *
92 * @param array $request An array of option values from the request.
93 */
94 function relevanssi_seoframework_options( array $request ) {
95 relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
96 }
1 <?php
2 /**
3 * /lib/compatibility/seopress.php
4 *
5 * SEOPress noindex filtering function.
6 *
7 * @package Relevanssi
8 * @author Benjamin Denis
9 * @source ./yoast-seo.php (Mikko Saari)
10 * @license https://wordpress.org/about/gpl/ GNU General Public License
11 * @see https://www.relevanssi.com/
12 */
13
14 add_filter( 'relevanssi_do_not_index', 'relevanssi_seopress_noindex', 10, 2 );
15 add_filter( 'relevanssi_indexing_restriction', 'relevanssi_seopress_exclude' );
16 add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_seopress_form', 20 );
17 add_action( 'relevanssi_indexing_options', 'relevanssi_seopress_options' );
18
19 /**
20 * Blocks indexing of posts marked "noindex" in the SEOPress settings.
21 *
22 * Attaches to the 'relevanssi_do_not_index' filter hook.
23 *
24 * @param boolean $do_not_index True, if the post shouldn't be indexed.
25 * @param integer $post_id The post ID number.
26 *
27 * @return string|boolean If the post shouldn't be indexed, this returns
28 * 'seopress'. The value may also be a boolean.
29 */
30 function relevanssi_seopress_noindex( $do_not_index, $post_id ) {
31 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
32 return $do_not_index;
33 }
34
35 $noindex = get_post_meta( $post_id, '_seopress_robots_index', true );
36 if ( 'yes' === $noindex ) {
37 $do_not_index = 'SEOPress';
38 }
39 return $do_not_index;
40 }
41
42 /**
43 * Excludes the "noindex" posts from Relevanssi indexing.
44 *
45 * Adds a MySQL query restriction that blocks posts that have the SEOPress
46 * "noindex" setting set to "1" from indexing.
47 *
48 * @param array $restriction An array with two values: 'mysql' for the MySQL
49 * query restriction to modify, 'reason' for the reason of restriction.
50 */
51 function relevanssi_seopress_exclude( $restriction ) {
52 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
53 return $restriction;
54 }
55
56 global $wpdb;
57 // Backwards compatibility code for 2.8.0, remove at some point.
58 if ( is_string( $restriction ) ) {
59 $restriction = array(
60 'mysql' => $restriction,
61 'reason' => '',
62 );
63 }
64
65 $restriction['mysql'] .= " AND post.ID NOT IN (SELECT post_id FROM
66 $wpdb->postmeta WHERE meta_key = '_seopress_robots_index'
67 AND meta_value = 'yes' ) ";
68 $restriction['reason'] .= 'SEOPress';
69 return $restriction;
70 }
71
72 /**
73 * Prints out the form fields for disabling the feature.
74 */
75 function relevanssi_seopress_form() {
76 $seo_noindex = get_option( 'relevanssi_seo_noindex' );
77 $seo_noindex = relevanssi_check( $seo_noindex );
78
79 ?>
80 <tr>
81 <th scope="row">
82 <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use SEOPress noindex', 'relevanssi' ); ?></label>
83 </th>
84 <td>
85 <label for='relevanssi_seo_noindex'>
86 <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
87 <?php esc_html_e( 'Use SEOPress noindex.', 'relevanssi' ); ?>
88 </label>
89 <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in SEOPress settings.', 'relevanssi' ); ?></p>
90 </td>
91 </tr>
92 <?php
93 }
94
95 /**
96 * Saves the SEO No index option.
97 *
98 * @param array $request An array of option values from the request.
99 */
100 function relevanssi_seopress_options( array $request ) {
101 relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
102 }
1 <?php
2 /**
3 * /lib/compatibility/simplemembership.php
4 *
5 * Simple Membership compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_simplemembership_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the user is allowed to see the post.
17 *
18 * @param boolean $post_ok Can the post be shown to the user.
19 * @param int $post_id The post ID.
20 *
21 * @return boolean $post_ok True if the user is allowed to see the post,
22 * otherwise false.
23 */
24 function relevanssi_simplemembership_compatibility( $post_ok, $post_id ) {
25 $access_ctrl = SwpmAccessControl::get_instance();
26 $post = get_post( $post_id );
27 $post_ok = $access_ctrl->can_i_read_post( $post );
28
29 return $post_ok;
30 }
1 <?php
2 /**
3 * /lib/compatibility/tablepress.php
4 *
5 * TablePress compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 /**
14 * Enables TablePress shortcodes for Relevanssi indexing.
15 *
16 * @return null|object The TablePress controller.
17 */
18 function relevanssi_enable_tablepress_shortcodes() {
19 $my_tablepress_controller = null;
20 if ( defined( 'TABLEPRESS_ABSPATH' ) ) {
21 if ( ! isset( TablePress::$model_options ) ) {
22 include_once TABLEPRESS_ABSPATH . 'classes/class-model.php';
23 include_once TABLEPRESS_ABSPATH . 'models/model-options.php';
24 TablePress::$model_options = new TablePress_Options_Model();
25 }
26 $my_tablepress_controller = TablePress::load_controller( 'frontend' );
27 $my_tablepress_controller->init_shortcodes();
28 }
29 return $my_tablepress_controller;
30 }
31
32 add_filter( 'relevanssi_post_content', 'relevanssi_table_filter' );
33
34 /**
35 * Replaces the [table_filter] shortcodes with [table].
36 *
37 * The shortcode filter extension adds a [table_filter] shortcode which is not
38 * compatible with Relevanssi. This function switches those to the normal
39 * [table] shortcode which works better.
40 *
41 * @param string $content The post content.
42 *
43 * @return string The fixed post content.
44 */
45 function relevanssi_table_filter( $content ) {
46 $content = str_replace( '[table_filter', '[table', $content );
47 return $content;
48 }
1 <?php
2 /**
3 * /lib/compatibility/useraccessmanager.php
4 *
5 * User Access Manager compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_useraccessmanager_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the user is allowed to see the post.
17 *
18 * @param boolean $post_ok Can the post be shown to the user.
19 * @param int $post_id The post ID.
20 *
21 * @return boolean $post_ok True if the user is allowed to see the post,
22 * otherwise false.
23 */
24 function relevanssi_useraccessmanager_compatibility( $post_ok, $post_id ) {
25 $status = relevanssi_get_post_status( $post_id );
26
27 if ( 'publish' === $status ) {
28 // Only apply to published posts, don't apply to drafts.
29
30 // phpcs:disable WordPress.NamingConventions.ValidVariableName
31 global $userAccessManager;
32 $type = relevanssi_get_post_type( $post_id );
33 $post_ok = $userAccessManager->getAccessHandler()->checkObjectAccess( $type, $post_id );
34 // phpcs:enable WordPress.NamingConventions.ValidVariableName
35 }
36
37 return $post_ok;
38 }
1 <?php
2 /**
3 * /lib/compatibility/woocommerce.php
4 *
5 * WooCommerce compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_indexing_restriction', 'relevanssi_woocommerce_restriction' );
14 add_filter( 'relevanssi_admin_search_blocked_post_types', 'relevanssi_woocommerce_admin_search_blocked_post_types' );
15 add_filter( 'relevanssi_modify_wp_query', 'relevanssi_woocommerce_filters' );
16
17 /**
18 * This action solves the problems introduced by adjust_posts_count() in
19 * WooCommerce version 4.4.0.
20 */
21 add_action( 'woocommerce_before_shop_loop', 'relevanssi_wc_reset_loop' );
22
23 RELEVANSSI_PREMIUM && add_filter( 'relevanssi_match', 'relevanssi_sku_boost' );
24
25 /**
26 * Resets the WC post loop in search queries.
27 *
28 * Hooks on to woocommerce_before_shop_loop.
29 */
30 function relevanssi_wc_reset_loop() {
31 global $wp_query;
32 if ( $wp_query->is_search ) {
33 wc_reset_loop();
34 }
35 }
36 /**
37 * Applies the WooCommerce product visibility filter.
38 *
39 * @param array $restriction An array with two values: 'mysql' for the MySQL
40 * query restriction to modify, 'reason' for the reason of restriction.
41 */
42 function relevanssi_woocommerce_restriction( $restriction ) {
43 // Backwards compatibility code for 2.8.0, remove at some point.
44 if ( is_string( $restriction ) ) {
45 $restriction = array(
46 'mysql' => $restriction,
47 'reason' => '',
48 );
49 }
50
51 $restriction['mysql'] .= relevanssi_woocommerce_indexing_filter();
52 $restriction['reason'] .= 'WooCommerce';
53 return $restriction;
54 }
55
56 /**
57 * WooCommerce product visibility filtering for indexing.
58 *
59 * This filter is applied before the posts are selected for indexing, so this will
60 * skip all the excluded posts right away.
61 *
62 * @since 4.0.9 (2.1.5)
63 * @global $wpdb The WordPress database interface.
64 *
65 * @return string $restriction The query restriction for the WooCommerce filtering.
66 */
67 function relevanssi_woocommerce_indexing_filter() {
68 global $wpdb;
69
70 $restriction = '';
71 $woocommerce_blocks = array(
72 'outofstock' => false,
73 'exclude-from-catalog' => false,
74 'exclude-from-search' => true,
75 );
76 /**
77 * Controls the WooCommerce product visibility filtering.
78 *
79 * @param array $woocommerce_blocks Has three keys: 'outofstock',
80 * 'exclude-from-catalog' and 'exclude-from-search', matching three different
81 * product visibility settings. If the filter sets some of these to 'true',
82 * those posts will be filtered in the indexing.
83 */
84 $woocommerce_blocks = apply_filters( 'relevanssi_woocommerce_indexing', $woocommerce_blocks );
85 $term_taxonomy_id_array = array();
86 if ( $woocommerce_blocks['outofstock'] ) {
87 $out_of_stock = get_term_by( 'slug', 'outofstock', 'product_visibility', OBJECT );
88 if ( $out_of_stock && isset( $out_of_stock->term_taxonomy_id ) ) {
89 $term_taxonomy_id_array[] = $out_of_stock->term_taxonomy_id;
90 }
91 }
92 if ( $woocommerce_blocks['exclude-from-catalog'] ) {
93 $exclude_from_catalog = get_term_by( 'slug', 'exclude-from-catalog', 'product_visibility', OBJECT );
94 if ( $exclude_from_catalog && isset( $exclude_from_catalog->term_taxonomy_id ) ) {
95 $term_taxonomy_id_array[] = $exclude_from_catalog->term_taxonomy_id;
96 }
97 }
98 if ( $woocommerce_blocks['exclude-from-search'] ) {
99 $exclude_from_search = get_term_by( 'slug', 'exclude-from-search', 'product_visibility', OBJECT );
100 if ( $exclude_from_search && isset( $exclude_from_search->term_taxonomy_id ) ) {
101 $term_taxonomy_id_array[] = $exclude_from_search->term_taxonomy_id;
102 }
103 }
104 if ( ! empty( $term_taxonomy_id_array ) ) {
105 $term_taxonomy_id_string = implode( ',', $term_taxonomy_id_array );
106 $restriction .= " AND post.ID NOT IN (SELECT object_id FROM $wpdb->term_relationships WHERE object_id = post.ID AND term_taxonomy_id IN ($term_taxonomy_id_string)) ";
107 }
108 return $restriction;
109 }
110
111 /**
112 * SKU weight boost.
113 *
114 * Increases the weight for matches in the _sku custom field. The amount of
115 * boost can be adjusted with the `relevanssi_sku_boost` filter hook. The
116 * default is 2.
117 *
118 * @param object $match_object The match object.
119 *
120 * @return object The match object.
121 */
122 function relevanssi_sku_boost( $match_object ) {
123 $custom_field_detail = json_decode( $match_object->customfield_detail );
124 if ( null !== $custom_field_detail && isset( $custom_field_detail->_sku ) ) {
125 /**
126 * Filters the SKU boost value.
127 *
128 * @param float The boost multiplier, default 2.
129 */
130 $match_object->weight *= apply_filters( 'relevanssi_sku_boost', 2 );
131 }
132 return $match_object;
133 }
134
135 /**
136 * Adds blocked WooCommerce post types to the list of blocked post types.
137 *
138 * Stops Relevanssi from taking over the admin search for the WooCommerce
139 * blocked post types using the relevanssi_admin_search_blocked_post_types
140 * filter hook.
141 *
142 * @param array $post_types The list of blocked post types.
143 * @return array
144 */
145 function relevanssi_woocommerce_admin_search_blocked_post_types( array $post_types ): array {
146 $woo_post_types = array(
147 'shop_coupon',
148 'shop_order',
149 'shop_order_refund',
150 'wc_order_status',
151 'wc_order_email',
152 'shop_webhook',
153 );
154 return array_merge( $post_types, $woo_post_types );
155 }
156
157 /**
158 * Relevanssi support for WooCommerce filtering.
159 *
160 * @param WP_Query $query The WP_Query object.
161 * @return WP_Query The WP_Query object.
162 */
163 function relevanssi_woocommerce_filters( $query ) {
164 // phpcs:disable WordPress.Security.NonceVerification.Recommended
165
166 $min_price = isset( $_REQUEST['min_price'] ) ? intval( $_REQUEST['min_price'] ) : false;
167 $max_price = isset( $_REQUEST['max_price'] ) ? intval( $_REQUEST['max_price'] ) : false;
168
169 $meta_query = $query->get( 'meta_query' );
170 if ( $min_price ) {
171 $meta_query[] = array(
172 'key' => '_price',
173 'value' => $min_price,
174 'compare' => '>=',
175 'type' => 'NUMERIC',
176 );
177 }
178 if ( $max_price ) {
179 $meta_query[] = array(
180 'key' => '_price',
181 'value' => $max_price,
182 'compare' => '<=',
183 'type' => 'NUMERIC',
184 );
185 }
186 if ( $meta_query ) {
187 $query->set( 'meta_query', $meta_query );
188 }
189
190 foreach ( array( 'product_tag', 'product_cat', 'product_brand' ) as $taxonomy ) {
191 $value = isset( $_REQUEST[ $taxonomy ] ) ? intval( $_REQUEST[ $taxonomy ] ) : false;
192 if ( $value ) {
193 $tax_query = $query->get( 'tax_query' );
194 if ( ! is_array( $tax_query ) ) {
195 $tax_query = array();
196 }
197 $tax_query[] = array(
198 'taxonomy' => $taxonomy,
199 'field' => 'term_id',
200 'terms' => $value,
201 );
202 $query->set( 'tax_query', $tax_query );
203 }
204 }
205
206 if ( 'no' === get_option( 'woocommerce_attribute_lookup_enabled' ) ) {
207 return $query;
208 }
209
210 $chosen_attributes = array();
211
212 if ( ! empty( $_GET ) ) {
213 foreach ( $_GET as $key => $value ) {
214 if ( 0 === strpos( $key, 'filter_' ) ) {
215 $attribute = wc_sanitize_taxonomy_name( str_replace( 'filter_', '', $key ) );
216 $taxonomy = wc_attribute_taxonomy_name( $attribute );
217 $filter_terms = ! empty( $value ) ? explode( ',', wc_clean( wp_unslash( $value ) ) ) : array();
218
219 if ( empty( $filter_terms ) || ! taxonomy_exists( $taxonomy ) || ! wc_attribute_taxonomy_id_by_name( $attribute ) ) {
220 continue;
221 }
222
223 $query_type = ! empty( $_GET[ 'query_type_' . $attribute ] ) && in_array( $_GET[ 'query_type_' . $attribute ], array( 'and', 'or' ), true )
224 ? wc_clean( wp_unslash( $_GET[ 'query_type_' . $attribute ] ) )
225 : '';
226
227 $chosen_attributes[ $taxonomy ]['terms'] = array_map( 'sanitize_title', $filter_terms );
228 $chosen_attributes[ $taxonomy ]['query_type'] = $query_type ? $query_type : apply_filters( 'woocommerce_layered_nav_default_query_type', 'and' );
229 }
230 }
231 }
232
233 $tax_query = $query->get( 'tax_query' );
234 if ( ! is_array( $tax_query ) ) {
235 $tax_query = array();
236 }
237 foreach ( $chosen_attributes as $taxonomy => $data ) {
238 $tax_query[] = array(
239 'taxonomy' => $taxonomy,
240 'field' => 'slug',
241 'terms' => $data['terms'],
242 'operator' => 'and' === $data['query_type'] ? 'AND' : 'IN',
243 'include_children' => false,
244 );
245 }
246 $query->set( 'tax_query', $tax_query );
247
248 return $query;
249 }
250
251 /**
252 * Provides layered navigation term counts based on Relevanssi searches.
253 *
254 * Hooks onto woocommerce_get_filtered_term_product_counts_query to provide
255 * accurate term counts.
256 *
257 * @param array $query The MySQL query parts.
258 *
259 * @return array The modified query.
260 */
261 function relevanssi_filtered_term_product_counts_query( $query ) {
262 if ( defined( 'BeRocket_AJAX_filters_version' ) ) {
263 return $query;
264 }
265
266 global $relevanssi_variables, $wpdb;
267
268 if ( false !== stripos( $query['select'], 'product_or_parent_id' ) ) {
269 $query['from'] = str_replace( 'FROM ', "FROM {$relevanssi_variables['relevanssi_table']} AS relevanssi, ", $query['from'] );
270 $query['where'] = str_replace( 'WHERE ', " WHERE relevanssi.doc = $wpdb->posts.ID AND ", $query['where'] );
271 $query['where'] = preg_replace( '/\(\w+posts.post_title LIKE(.*?)\)\)/', 'relevanssi.term LIKE\1)', $query['where'] );
272 $query['where'] = preg_replace( array( '/OR \(\w+posts.post_excerpt LIKE .*?\)/', '/OR \(\w+posts.post_content LIKE .*?\)/' ), '', $query['where'] );
273 } else {
274 $query['select'] = 'SELECT COUNT( DISTINCT( relevanssi.doc ) ) AS term_count, terms.term_id AS term_count_id';
275 $query['from'] = "FROM {$relevanssi_variables['relevanssi_table']} AS relevanssi, $wpdb->posts";
276 $query['where'] = str_replace( 'WHERE ', " WHERE relevanssi.doc = $wpdb->posts.ID AND ", $query['where'] );
277 $query['where'] = preg_replace( '/\(\w+posts.post_title LIKE(.*?)\)\)/', 'relevanssi.term LIKE\1)', $query['where'] );
278 $query['where'] = preg_replace( array( '/OR \(\w+posts.post_excerpt LIKE .*?\)/', '/OR \(\w+posts.post_content LIKE .*?\)/' ), '', $query['where'] );
279 }
280
281 return $query;
282 }
1 <?php
2 /**
3 * /lib/compatibility/wp-file-download.php
4 *
5 * WP File Download compatibility features. Compatibility with WPFD checked for
6 * version 4.5.4.
7 *
8 * @package Relevanssi
9 * @author Mikko Saari
10 * @license https://wordpress.org/about/gpl/ GNU General Public License
11 * @see https://www.relevanssi.com/
12 */
13
14 add_filter( 'relevanssi_content_to_index', 'relevanssi_wpfd_content', 10, 2 );
15 add_action( 'wpfd_file_indexed', 'relevanssi_wpfd_index' );
16
17 /**
18 * Adds the WPFD indexed content to wpfd_file posts.
19 *
20 * Fetches the words from wpfd_words. wpfd_index.tid is the post ID, wpfd_index.id is
21 * then used to get the wpfd_docs.id, that is used to get the wpfd_vectors.did which
22 * can then be used to fetch the correct words from wpfd_words. This function is
23 * hooked onto relevanssi_content_to_index filter hook.
24 *
25 * @param string $content The post content as a string.
26 * @param object $post The post object.
27 *
28 * @return string The post content with the words added to the end.
29 */
30 function relevanssi_wpfd_content( $content, $post ) {
31 $wpfd_search_config = get_option( '_wpfd_global_search_config', null );
32 if ( 'wpfd_file' === $post->post_type ) {
33 if ( $wpfd_search_config && isset( $wpfd_search_config['plain_text_search'] ) && $wpfd_search_config['plain_text_search'] ) {
34 global $wpdb;
35 $words = $wpdb->get_col(
36 "SELECT word
37 FROM {$wpdb->prefix}wpfd_words, {$wpdb->prefix}wpfd_docs, {$wpdb->prefix}wpfd_index, {$wpdb->prefix}wpfd_vectors " .
38 "WHERE {$wpdb->prefix}wpfd_index.tid = {$post->ID} " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
39 " AND {$wpdb->prefix}wpfd_docs.index_id = {$wpdb->prefix}wpfd_index.id
40 AND {$wpdb->prefix}wpfd_docs.id = {$wpdb->prefix}wpfd_vectors.did
41 AND {$wpdb->prefix}wpfd_vectors.wid = {$wpdb->prefix}wpfd_words.id"
42 );
43 $content .= implode( ' ', $words );
44 }
45 }
46 return $content;
47 }
48
49 /**
50 * Runs Relevanssi indexing after WPFD indexing is done.
51 *
52 * @param int $wpfd_id The WPFD post index.
53 */
54 function relevanssi_wpfd_index( $wpfd_id ) {
55 global $wpdb;
56 $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT tid FROM {$wpdb->prefix}wpfd_index WHERE id=%d", $wpfd_id ) );
57 relevanssi_insert_edit( $post_id );
58 }
1 <?php
2 /**
3 * /lib/compatibility/wp-members.php
4 *
5 * WP-Members compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_wpmembers_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the post type is blocked.
17 *
18 * Allows all logged-in users to see posts. For non-logged-in users, checks if
19 * the post is blocked by the _wpmem_block custom field, or if the post type is
20 * blocked in the $wpmem global.
21 *
22 * @param bool $post_ok Whether the user is allowed to see the post.
23 * @param int|string $post_id The post ID.
24 *
25 * @return bool
26 */
27 function relevanssi_wpmembers_compatibility( bool $post_ok, $post_id ): bool {
28 global $wpmem;
29
30 if ( is_user_logged_in() ) {
31 return $post_ok;
32 }
33
34 $post_meta = get_post_meta( $post_id, '_wpmem_block', true );
35 $post_type = isset( $wpmem->block[ relevanssi_get_post_type( $post_id ) ] )
36 ? $wpmem->block[ relevanssi_get_post_type( $post_id ) ]
37 : 0;
38
39 if ( '1' === $post_meta ) {
40 $post_ok = false;
41 } elseif ( '1' === $post_type && '0' !== $post_meta ) {
42 $post_ok = false;
43 }
44
45 return $post_ok;
46 }
1 <?php
2 /**
3 * /lib/compatibility/wp-search-suggest.php
4 *
5 * WP Search Suggest compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'wpss_search_results', 'relevanssi_wpss_support', 10, 2 );
14 /**
15 * Adds Relevanssi results to WP Search Suggest dropdown.
16 *
17 * @param array $title_list List of post titles.
18 * @param object $query The WP_Query object.
19 *
20 * @return array List of post titles.
21 */
22 function relevanssi_wpss_support( $title_list, $query ) {
23 $query = relevanssi_do_query( $query );
24 return wp_list_pluck( $query->posts, 'post_title' );
25 }
1 <?php
2 /**
3 * /lib/compatibility/wpjvpostreadinggroups.php
4 *
5 * WP JV Post Reading Groups compatibility features.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_post_ok', 'relevanssi_wpjvpostreadinggroups_compatibility', 10, 2 );
14
15 /**
16 * Checks whether the user is allowed to see the post.
17 *
18 * @param boolean $post_ok Can the post be shown to the user.
19 * @param int $post_id The post ID.
20 *
21 * @return boolean $post_ok True if the user is allowed to see the post,
22 * otherwise false.
23 */
24 function relevanssi_wpjvpostreadinggroups_compatibility( $post_ok, $post_id ) {
25 $post_ok = wp_jv_prg_user_can_see_a_post( get_current_user_id(), $post_id );
26
27 return $post_ok;
28 }
1 <?php
2 /**
3 * /lib/compatibility/wpml.php
4 *
5 * WPML filtering function.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_hits_filter', 'relevanssi_wpml_filter', 9 );
14 add_filter( 'relevanssi_tag_before_tokenize', 'relevanssi_wpml_term_fix', 10, 4 );
15 add_action( 'relevanssi_pre_index_taxonomies', 'relevanssi_disable_wpml_terms' );
16 add_action( 'relevanssi_post_index_taxonomies', 'relevanssi_enable_wpml_terms' );
17
18 /**
19 * Filters posts based on WPML language.
20 *
21 * Attaches to 'relevanssi_hits_filter' to restrict WPML searches to the current
22 * language. Whether this filter is used or not depends on the option
23 * 'relevanssi_wpml_only_current'. Thanks to rvencu for the initial code.
24 *
25 * @global object $sitepress The WPML global object.
26 *
27 * @param array $data Index 0 has the array of results, index 1 has the search query.
28 *
29 * @return array $data The whole parameter array, with the filtered posts in the index 0.
30 */
31 function relevanssi_wpml_filter( $data ) {
32 $filter_enabled = get_option( 'relevanssi_wpml_only_current' );
33 if ( 'on' === $filter_enabled ) {
34 $wpml_post_type_setting = apply_filters( 'wpml_setting', false, 'custom_posts_sync_option' );
35 $wpml_taxonomy_setting = apply_filters( 'wpml_setting', false, 'taxonomies_sync_option' );
36
37 $current_blog_language = get_bloginfo( 'language' );
38 $filtered_hits = array();
39 foreach ( $data[0] as $hit ) {
40 $original_hit = $hit;
41
42 $object_array = relevanssi_get_an_object( $hit );
43 $hit = $object_array['object'];
44
45 if ( isset( $hit->blog_id ) && function_exists( 'switch_to_blog' ) ) {
46 // This is a multisite search.
47 switch_to_blog( $hit->blog_id );
48 if ( function_exists( 'icl_object_id' ) ) {
49 // Reset the WPML cache when blog is switched, otherwise WPML
50 // will be confused.
51 global $wpml_post_translations;
52 $wpml_post_translations->reload();
53 }
54 }
55
56 global $sitepress;
57
58 // Check if WPML is used.
59 if ( function_exists( 'icl_object_id' ) && ! function_exists( 'pll_is_translated_post_type' ) ) {
60 if ( $sitepress->is_translated_post_type( $hit->post_type ) ) {
61 $fallback_to_default = false;
62 if ( isset( $wpml_post_type_setting[ $hit->post_type ] ) && '2' === $wpml_post_type_setting[ $hit->post_type ] ) {
63 $fallback_to_default = true;
64 }
65 $id = apply_filters( 'wpml_object_id', $hit->ID, $hit->post_type, $fallback_to_default );
66 // This is a post in a translated post type.
67 if ( intval( $hit->ID ) === intval( $id ) ) {
68 // The post exists in the current language, and can be included.
69 $filtered_hits[] = $original_hit;
70 }
71 } elseif ( isset( $hit->term_id ) ) {
72 $fallback_to_default = false;
73 if ( isset( $wpml_taxonomy_setting[ $hit->post_type ] ) && '2' === $wpml_taxonomy_setting[ $hit->post_type ] ) {
74 $fallback_to_default = true;
75 }
76 if ( ! isset( $hit->post_type ) ) {
77 // This is a term object, not a Relevanssi-generated post object.
78 $hit->post_type = $hit->taxonomy;
79 }
80 $id = apply_filters( 'wpml_object_id', $hit->term_id, $hit->post_type, $fallback_to_default );
81 if ( intval( $hit->term_id ) === intval( $id ) ) {
82 // The post exists in the current language, and can be included.
83 $filtered_hits[] = $original_hit;
84 }
85 } else {
86 // This is not a translated post type, so include all posts.
87 $filtered_hits[] = $original_hit;
88 }
89 } elseif ( get_bloginfo( 'language' ) === $current_blog_language ) {
90 // If there is no WPML but the target blog has identical language with current blog,
91 // we use the hits. Note en-US is not identical to en-GB!
92 $filtered_hits[] = $original_hit;
93 }
94
95 if ( isset( $hit->blog_id ) && function_exists( 'restore_current_blog' ) ) {
96 restore_current_blog();
97 }
98 }
99
100 // A bit of foolproofing, avoid a warning if someone passes this filter bad data.
101 $query = '';
102 if ( isset( $data[1] ) ) {
103 $query = $data[1];
104 }
105 return array( $filtered_hits, $query );
106 }
107
108 return $data;
109 }
110
111 /**
112 * Fixes translated term indexing for WPML.
113 *
114 * WPML indexed translated terms based on current admin language, not the post
115 * language. This filter changes the term indexing to match the post language.
116 *
117 * @param string $term_content All terms in the taxonomy as a string.
118 * @param array $terms All the term objects in the current taxonomy.
119 * @param string $taxonomy The taxonomy name.
120 * @param int $post_id The post ID.
121 *
122 * @return string The term names as a string.
123 */
124 function relevanssi_wpml_term_fix( string $term_content, array $terms, string $taxonomy, int $post_id ) {
125 $post_language = apply_filters( 'wpml_post_language_details', null, $post_id );
126 if ( ! is_wp_error( $post_language ) ) {
127 $term_content = '';
128
129 global $sitepress;
130 remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
131
132 foreach ( $terms as $term ) {
133 $term = get_term(
134 apply_filters(
135 'wpml_object_id',
136 $term->term_id,
137 $taxonomy,
138 true,
139 $post_language['language_code']
140 ),
141 $taxonomy
142 );
143
144 $term_content .= ' ' . $term->name;
145 }
146
147 add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
148 }
149
150 return $term_content;
151 }
152
153 /**
154 * Disables WPML term filtering.
155 *
156 * This function disables the WPML term filtering, so that Relevanssi can index
157 * the terms in the correct language.
158 */
159 function relevanssi_disable_wpml_terms() {
160 global $sitepress;
161 remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1 );
162 }
163
164 /**
165 * Enables WPML term filtering.
166 *
167 * This function enables the WPML term filtering.
168 */
169 function relevanssi_enable_wpml_terms() {
170 global $sitepress;
171 add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
172 }
1 <?php
2 /**
3 * /lib/compatibility/yoast-seo.php
4 *
5 * Yoast SEO noindex filtering function.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 add_filter( 'relevanssi_do_not_index', 'relevanssi_yoast_noindex', 10, 2 );
14 add_filter( 'relevanssi_indexing_restriction', 'relevanssi_yoast_exclude' );
15 add_action( 'relevanssi_indexing_tab_advanced', 'relevanssi_yoast_form', 20 );
16 add_action( 'relevanssi_indexing_options', 'relevanssi_yoast_options' );
17
18 /**
19 * Blocks indexing of posts marked "noindex" in the Yoast SEO settings.
20 *
21 * Attaches to the 'relevanssi_do_not_index' filter hook.
22 *
23 * @param boolean $do_not_index True, if the post shouldn't be indexed.
24 * @param integer $post_id The post ID number.
25 *
26 * @return string|boolean If the post shouldn't be indexed, this returns
27 * 'yoast_seo'. The value may also be a boolean.
28 */
29 function relevanssi_yoast_noindex( $do_not_index, $post_id ) {
30 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
31 return $do_not_index;
32 }
33
34 $noindex = get_post_meta( $post_id, '_yoast_wpseo_meta-robots-noindex', true );
35 if ( '1' === $noindex ) {
36 $do_not_index = 'Yoast SEO';
37 }
38 return $do_not_index;
39 }
40
41 /**
42 * Excludes the "noindex" posts from Relevanssi indexing.
43 *
44 * Adds a MySQL query restriction that blocks posts that have the Yoast SEO
45 * "noindex" setting set to "1" from indexing.
46 *
47 * @param array $restriction An array with two values: 'mysql' for the MySQL
48 * query restriction to modify, 'reason' for the reason of restriction.
49 */
50 function relevanssi_yoast_exclude( $restriction ) {
51 if ( 'on' !== get_option( 'relevanssi_seo_noindex' ) ) {
52 return $restriction;
53 }
54
55 global $wpdb;
56
57 // Backwards compatibility code for 2.8.0, remove at some point.
58 if ( is_string( $restriction ) ) {
59 $restriction = array(
60 'mysql' => $restriction,
61 'reason' => '',
62 );
63 }
64
65 $restriction['mysql'] .= " AND post.ID NOT IN (SELECT post_id FROM
66 $wpdb->postmeta WHERE meta_key = '_yoast_wpseo_meta-robots-noindex'
67 AND meta_value = '1' ) ";
68 $restriction['reason'] .= ' Yoast SEO';
69 return $restriction;
70 }
71
72 /**
73 * Prints out the form fields for disabling the feature.
74 */
75 function relevanssi_yoast_form() {
76 $seo_noindex = get_option( 'relevanssi_seo_noindex' );
77 $seo_noindex = relevanssi_check( $seo_noindex );
78
79 ?>
80 <tr>
81 <th scope="row">
82 <label for='relevanssi_seo_noindex'><?php esc_html_e( 'Use Yoast SEO noindex', 'relevanssi' ); ?></label>
83 </th>
84 <td>
85 <label for='relevanssi_seo_noindex'>
86 <input type='checkbox' name='relevanssi_seo_noindex' id='relevanssi_seo_noindex' <?php echo esc_attr( $seo_noindex ); ?> />
87 <?php esc_html_e( 'Use Yoast SEO noindex.', 'relevanssi' ); ?>
88 </label>
89 <p class="description"><?php esc_html_e( 'If checked, Relevanssi will not index posts marked as "No index" in Yoast SEO settings.', 'relevanssi' ); ?></p>
90 </td>
91 </tr>
92 <?php
93 }
94
95 /**
96 * Saves the SEO No index option.
97 *
98 * @param array $request An array of option values from the request.
99 */
100 function relevanssi_yoast_options( array $request ) {
101 relevanssi_update_off_or_on( $request, 'relevanssi_seo_noindex', true );
102 }
1 <?php
2 /**
3 * /lib/debug.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', 'relevanssi_debug_post' );
12
13 /**
14 * Checks if Relevanssi debug mode is enabled.
15 *
16 * Debug mode is enabled by setting RELEVANSSI_DEBUG to true or with the
17 * 'relevanssi_debug' query parameter if the debug mode is allowed from the
18 * settings.
19 *
20 * @return boolean True if debug mode is enabled, false if not.
21 */
22 function relevanssi_is_debug(): bool {
23 $debug = false;
24 if ( defined( 'RELEVANSSI_DEBUG' ) && RELEVANSSI_DEBUG ) {
25 $debug = true;
26 }
27 if ( isset( $_REQUEST['relevanssi_debug'] ) && 'on' === get_option( 'relevanssi_debugging_mode' ) ) { // phpcs:ignore WordPress.Security.NonceVerification
28 $debug = true;
29 }
30 return $debug;
31 }
32
33 /**
34 * Adds the debug information to the search results.
35 *
36 * Displays the found posts.
37 *
38 * @param array $posts The search results.
39 */
40 function relevanssi_debug_posts( $posts ) {
41 // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
42 echo '<h2>Posts</h2>';
43 foreach ( $posts as $post ) {
44 if ( ! is_object( $post ) ) {
45 echo "$post\n";
46 } else {
47 echo "<p>$post->ID: $post->post_title<br />";
48 echo "$post->post_type$post->post_status$post->relevance_score<br />";
49 property_exists( $post, 'relevanssi_link' ) && print( "relevanssi_link: $post->relevanssi_link<br />" );
50 echo 'the_permalink(): ';
51 the_permalink( $post->ID );
52 echo '<br />get_permalink(): ' . get_permalink( $post );
53 echo '</p>';
54 }
55 }
56 // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
57 }
58
59 /**
60 * Prints out an array in a preformatted block.
61 *
62 * @param array $array_value The array to print.
63 * @param string $title The title for the array.
64 */
65 function relevanssi_debug_array( $array_value, $title ) {
66 echo '<h2>' . esc_html( $title ) . '</h2>';
67 echo '<pre>';
68 print_r( $array_value ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
69 echo '</pre>';
70 }
71
72 /**
73 * Prints out a string in a preformatted block.
74 *
75 * @param string $str The string to print.
76 * @param string $title The title for the string.
77 */
78 function relevanssi_debug_string( $str, $title ) {
79 echo '<h2>' . esc_html( $title ) . '</h2>';
80 echo '<pre>' . esc_html( $str ) . '</pre>';
81 }
82
83 /**
84 * Prints out the Relevanssi debug information for a post.
85 *
86 * This function is called by the 'wp' action, so it's executed on every page
87 * load.
88 */
89 function relevanssi_debug_post() {
90 if ( ! is_singular() || ! relevanssi_is_debug() ) {
91 return;
92 }
93 global $post;
94 echo '<h1>' . esc_html( $post->post_title ) . ' (' . intval( $post->ID ) . ')</h1>';
95
96 echo '<h2>Index</h2>';
97 echo relevanssi_generate_how_relevanssi_sees( $post->ID, true, 'post' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
98
99 echo '<h2>Database</h2>';
100 echo '<pre>' . relevanssi_generate_db_post_view( $post->ID ) . '</pre>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
101
102 relevanssi_debug_array( get_post_meta( $post->ID ), 'Post meta' );
103
104 exit();
105 }
106
107 /**
108 * Generates the debugging view for a post.
109 *
110 * @param int $post_id ID of the post.
111 *
112 * @return string The debugging view in a div container.
113 */
114 function relevanssi_generate_db_post_view( int $post_id ) {
115 global $wpdb;
116
117 $element = '<div id="relevanssi_db_view_container">';
118
119 $post_object = get_post( $post_id );
120
121 if ( ! $post_object ) {
122 $element .= '<p>' . esc_html__( 'Post not found', 'relevanssi' ) . '</p>';
123 $element .= '</div>';
124 return $element;
125 }
126
127 $element .= '<p>' . esc_html( $post_object->post_content ) . '</p>';
128
129 $element .= '</div>';
130 return $element;
131 }
132
133 /**
134 * Prints out the Relevanssi debug information for search settings.
135 */
136 function relevanssi_debug_search_settings() {
137 // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
138 echo '<h2>Relevanssi searching settings</h2>';
139 echo '<p>';
140
141 $value = get_option( 'relevanssi_fuzzy' );
142 echo "relevanssi_fuzzy: $value<br />";
143
144 $value = get_option( 'relevanssi_implicit_operator' );
145 echo "relevanssi_implicit_operator: $value<br />";
146
147 $value = get_option( 'relevanssi_disable_or_fallback' );
148 echo "relevanssi_disable_or_fallback: $value<br />";
149
150 $value = get_option( 'relevanssi_throttle' );
151 echo "relevanssi_throttle: $value<br />";
152
153 $value = get_option( 'relevanssi_throttle_limit' );
154 echo "relevanssi_throttle_limit: $value<br />";
155
156 $value = get_option( 'relevanssi_default_orderby' );
157 echo "relevanssi_default_orderby: $value<br />";
158
159 echo '</p>';
160 // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
161 }
162
163 /**
164 * Returns true if RELEVANSSI_DEBUG, WP_DEBUG and WP_DEBUG_DISPLAY are true.
165 *
166 * @return bool True if debug mode is on.
167 */
168 function relevanssi_log_debug(): bool {
169 return defined( 'RELEVANSSI_DEBUG' ) && RELEVANSSI_DEBUG
170 && defined( 'WP_DEBUG' ) && WP_DEBUG
171 && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY;
172 }
1 <?php
2 /**
3 * /lib/didyoumean.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 * Generates the Did you mean suggestions.
13 *
14 * A wrapper function that prints out the Did you mean suggestions. If Premium
15 * is available, will use relevanssi_premium_didyoumean(), otherwise the
16 * relevanssi_simple_didyoumean() is used.
17 *
18 * @param string $query The query.
19 * @param string $pre Printed out before the suggestion.
20 * @param string $post Printed out after the suggestion.
21 * @param int $n Maximum number of search results found for the
22 * suggestions to show up. Default 5.
23 * @param boolean $echoed If true, echo out. Default true.
24 *
25 * @return string|null The suggestion HTML element.
26 */
27 function relevanssi_didyoumean( $query, $pre, $post, $n = 5, $echoed = true ) {
28 if ( function_exists( 'relevanssi_premium_didyoumean' ) ) {
29 $result = relevanssi_premium_didyoumean( $query, $pre, $post, $n );
30 } else {
31 $result = relevanssi_simple_didyoumean( $query, $pre, $post, $n );
32 }
33
34 if ( $echoed ) {
35 echo $result; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
36 }
37
38 return $result;
39 }
40
41 /**
42 * Generates the Did you mean suggestions HTML code.
43 *
44 * Uses relevanssi_simple_generate_suggestion() to come up with a suggestion,
45 * then wraps that up with HTML code.
46 *
47 * @global object $wpdb The WordPress database interface.
48 * @global array $relevanssi_variables The Relevanssi global variables.
49 * @global object $wp_query The WP_Query object.
50 *
51 * @param string $query The query.
52 * @param string $pre Printed out before the suggestion.
53 * @param string $post Printed out after the suggestion.
54 * @param int $n Maximum number of search results found for the
55 * suggestions to show up. Default 5.
56 *
57 * @return string|null The suggestion HTML code, null if nothing found.
58 */
59 function relevanssi_simple_didyoumean( $query, $pre, $post, $n = 5 ) {
60 global $wp_query;
61
62 $total_results = $wp_query->found_posts;
63
64 if ( $total_results > $n ) {
65 return null;
66 }
67
68 $suggestion = relevanssi_simple_generate_suggestion( $query );
69
70 $result = null;
71 if ( $suggestion ) {
72 $url = trailingslashit( get_bloginfo( 'url' ) );
73 $url = esc_attr(
74 add_query_arg(
75 array( 's' => rawurlencode( $suggestion ) ),
76 $url
77 )
78 );
79
80 /**
81 * Filters the 'Did you mean' suggestion URL.
82 *
83 * @param string $url The URL for the suggested search query.
84 * @param string $query The search query.
85 * @param string $suggestion The suggestion.
86 */
87 $url = apply_filters(
88 'relevanssi_didyoumean_url',
89 $url,
90 $query,
91 $suggestion
92 );
93
94 // Escape the suggestion to avoid XSS attacks.
95 $suggestion = htmlspecialchars( $suggestion );
96
97 /**
98 * Filters the complete 'Did you mean' suggestion.
99 *
100 * @param string The suggestion HTML code.
101 */
102 $result = apply_filters(
103 'relevanssi_didyoumean_suggestion',
104 "$pre<a href='$url'>$suggestion</a>$post"
105 );
106 }
107
108 return $result;
109 }
110
111 /**
112 * Generates the 'Did you mean' suggestions. Can be used to correct any queries.
113 *
114 * Uses the Relevanssi search logs as source material for corrections. If there
115 * are no logged search queries, can't do anything.
116 *
117 * @global object $wpdb The WordPress database interface.
118 * @global array $relevanssi_variables The Relevanssi global variables, used
119 * for table names.
120 *
121 * @param string $query The query to correct.
122 *
123 * @return string Corrected query, empty if nothing found.
124 */
125 function relevanssi_simple_generate_suggestion( $query ) {
126 global $wpdb, $relevanssi_variables;
127
128 /**
129 * The minimum limit of occurrances to include a word.
130 *
131 * To save resources, only words with more than this many occurrances are
132 * fed for the spelling corrector. If there are problems with the spelling
133 * corrector, increasing this value may fix those problems.
134 *
135 * @param int $number The number of occurrances must be more than this
136 * value, default 2.
137 */
138 $count = apply_filters( 'relevanssi_get_words_having', 2 );
139 if ( ! is_numeric( $count ) ) {
140 $count = 2;
141 }
142 $q = 'SELECT query, count(query) as c, AVG(hits) as a FROM '
143 . $relevanssi_variables['log_table'] . ' WHERE hits > ' . $count
144 . ' GROUP BY query ORDER BY count(query) DESC';
145 /**
146 * Filters the MySQL query used to fetch potential suggestions from the log.
147 *
148 * @param string $q MySQL query for fetching the suggestions.
149 */
150 $q = apply_filters( 'relevanssi_didyoumean_query', $q );
151
152 $data = get_transient( 'relevanssi_didyoumean_query' );
153 if ( empty( $data ) ) {
154 $data = $wpdb->get_results( $q ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
155 set_transient( 'relevanssi_didyoumean_query', $data, MONTH_IN_SECONDS );
156 }
157
158 $query = htmlspecialchars_decode( $query, ENT_QUOTES );
159 $tokens = relevanssi_tokenize( $query, true, -1, 'search_query' );
160 $suggestions_made = false;
161 $suggestion = '';
162
163 foreach ( $tokens as $token => $count ) {
164 /**
165 * Filters the tokens for Did you mean suggestions.
166 *
167 * You can use this filter hook to modify the tokens before Relevanssi
168 * tries to come up with Did you mean suggestions for them. If you
169 * return an empty string, the token will be skipped and no suggestion
170 * will be made for the token.
171 *
172 * @param string $token An individual word from the search query.
173 *
174 * @return string The token.
175 */
176 $token = apply_filters( 'relevanssi_didyoumean_token', trim( $token ) );
177 if ( ! $token ) {
178 continue;
179 }
180 $closest = '';
181 $distance = -1;
182 foreach ( $data as $row ) {
183 if ( $row->c < 2 ) {
184 break;
185 }
186
187 if ( $token === $row->query ) {
188 $closest = '';
189 break;
190 } elseif ( strlen( $token ) < 255 && strlen( $row->query ) < 255 ) {
191 // The levenshtein() function has a max length of 255
192 // characters. The function uses strlen(), so we must use
193 // too, instead of relevanssi_strlen().
194 $lev = levenshtein( $token, $row->query );
195 if ( $lev < 3 && ( $lev < $distance || $distance < 0 ) ) {
196 if ( $row->a > 0 ) {
197 $distance = $lev;
198 $closest = $row->query;
199 if ( $lev < 2 ) {
200 break; // get the first with distance of 1 and go.
201 }
202 }
203 }
204 }
205 }
206 if ( ! empty( $closest ) ) {
207 $query = str_ireplace( $token, $closest, $query, $replacement_count );
208 if ( $replacement_count > 0 ) {
209 $suggestions_made = true;
210 }
211 }
212 }
213
214 if ( $suggestions_made ) {
215 $suggestion = $query;
216 }
217
218 return $suggestion;
219 }
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' and 'wp_initialize_site' action hooks and runs
15 * '_relevanssi_install' on the new blog.
16 *
17 * @param int|object $blog Either the blog ID (if 'wpmu_new_blog') or new site
18 * object (if 'wp_initialize_site').
19 */
20 function relevanssi_new_blog( $blog ) {
21 if ( is_int( $blog ) ) {
22 $blog_id = $blog;
23 } else {
24 $blog_id = $blog->id;
25 }
26
27 if ( is_plugin_active_for_network( 'relevanssi-premium/relevanssi.php' ) || is_plugin_active_for_network( 'relevanssi/relevanssi.php' ) ) {
28 switch_to_blog( $blog_id );
29 _relevanssi_install();
30 restore_current_blog();
31 }
32 }
33
34 /**
35 * Runs _relevanssi_install() on one blog or for the whole network.
36 *
37 * If Relevanssi is network active, this installs Relevanssi on all blogs in the
38 * network, running the _relevanssi_install() function.
39 *
40 * @param boolean $network_wide If true, install on all sites. Default false.
41 */
42 function relevanssi_install( $network_wide = false ) {
43 if ( $network_wide ) {
44 $args = array(
45 'spam' => 0,
46 'deleted' => 0,
47 'archived' => 0,
48 'fields' => 'ids',
49 );
50 $blog_ids = get_sites( $args );
51
52 foreach ( $blog_ids as $blog_id ) {
53 switch_to_blog( $blog_id );
54 _relevanssi_install();
55 restore_current_blog();
56 }
57 } else {
58 _relevanssi_install();
59 }
60 }
61
62 /**
63 * Installs Relevanssi on the blog.
64 *
65 * Adds Relevanssi options and sets their default values and generates the
66 * database tables.
67 *
68 * @global array $relevanssi_variables The global Relevanssi variables array.
69 */
70 function _relevanssi_install() {
71 global $relevanssi_variables;
72
73 add_option( 'relevanssi_admin_search', 'off' );
74 add_option( 'relevanssi_bg_col', '#ffaf75' );
75 add_option( 'relevanssi_cat', '0' );
76 add_option( 'relevanssi_class', 'relevanssi-query-term' );
77 add_option( 'relevanssi_comment_boost', $relevanssi_variables['comment_boost_default'] );
78 add_option( 'relevanssi_content_boost', $relevanssi_variables['content_boost_default'] );
79 add_option( 'relevanssi_css', 'text-decoration: underline; color: #ff0000' );
80 add_option( 'relevanssi_db_version', '0' );
81 add_option( 'relevanssi_debugging_mode', 'off' );
82 add_option( 'relevanssi_default_orderby', 'relevance' );
83 add_option( 'relevanssi_disable_or_fallback', 'off' );
84 add_option( 'relevanssi_exact_match_bonus', 'on' );
85 add_option( 'relevanssi_excat', '0' );
86 add_option( 'relevanssi_excerpt_allowable_tags', '' );
87 add_option( 'relevanssi_excerpt_custom_fields', 'off' );
88 add_option( 'relevanssi_excerpt_length', '30' );
89 add_option( 'relevanssi_excerpt_specific_fields', 'off' );
90 add_option( 'relevanssi_excerpt_type', 'words' );
91 add_option( 'relevanssi_excerpts', 'on' );
92 add_option( 'relevanssi_exclude_posts', '' );
93 add_option( 'relevanssi_expand_highlights', 'off' );
94 add_option( 'relevanssi_expand_shortcodes', 'on' );
95 add_option( 'relevanssi_extag', '0' );
96 add_option( 'relevanssi_fuzzy', 'always' );
97 add_option( 'relevanssi_highlight', 'strong' );
98 add_option( 'relevanssi_highlight_comments', 'off' );
99 add_option( 'relevanssi_highlight_docs', 'off' );
100 add_option( 'relevanssi_hilite_title', '' );
101 add_option( 'relevanssi_implicit_operator', 'OR' );
102 add_option( 'relevanssi_index_author', '' );
103 add_option( 'relevanssi_index_comments', 'none' );
104 add_option( 'relevanssi_index_excerpt', 'off' );
105 add_option( 'relevanssi_index_fields', '' );
106 add_option( 'relevanssi_index_image_files', 'on' );
107 add_option( 'relevanssi_index_post_types', array( 'post', 'page' ) );
108 add_option( 'relevanssi_index_taxonomies_list', array() );
109 add_option( 'relevanssi_indexed', '' );
110 add_option( 'relevanssi_log_queries', 'off' );
111 add_option( 'relevanssi_log_queries_with_ip', 'off' );
112 add_option( 'relevanssi_min_word_length', '3' );
113 add_option( 'relevanssi_omit_from_logs', '' );
114 add_option( 'relevanssi_polylang_all_languages', 'off' );
115 add_option(
116 'relevanssi_punctuation',
117 array(
118 'quotes' => 'replace',
119 'hyphens' => 'replace',
120 'ampersands' => 'replace',
121 )
122 );
123 add_option( 'relevanssi_respect_exclude', 'on' );
124 add_option( 'relevanssi_seo_noindex', 'on' );
125 add_option( 'relevanssi_show_matches', '' );
126 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%)' );
127 add_option( 'relevanssi_stopwords', array() );
128 add_option( 'relevanssi_synonyms', array() );
129 add_option( 'relevanssi_throttle', 'on' );
130 add_option( 'relevanssi_throttle_limit', '500' );
131 add_option( 'relevanssi_title_boost', $relevanssi_variables['title_boost_default'] );
132 add_option( 'relevanssi_txt_col', '#ff0000' );
133 add_option( 'relevanssi_wpml_only_current', 'on' );
134
135 if ( function_exists( 'relevanssi_premium_install' ) ) {
136 // Do some Relevanssi Premium additions.
137 relevanssi_premium_install();
138 }
139
140 /**
141 * Runs after Relevanssi options are added in the installation process.
142 *
143 * This action hook can be used to adjust the options to set your own default
144 * settings, for example.
145 */
146 do_action( 'relevanssi_update_options' );
147
148 relevanssi_create_database_tables( $relevanssi_variables['database_version'] );
149 }
1 <?php
2 /**
3 * /lib/phrases.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 * Extracts phrases from the search query.
13 *
14 * Finds all phrases wrapped in quotes (curly or straight) from the search
15 * query.
16 *
17 * @param string $query The search query.
18 *
19 * @return array An array of phrases (strings).
20 */
21 function relevanssi_extract_phrases( string $query ) {
22 // iOS uses “” or „“ as the default quotes, so Relevanssi needs to
23 // understand those as well.
24 $normalized_query = str_replace( array( '”', '“', '„' ), '"', $query );
25 $pos = relevanssi_stripos( $normalized_query, '"' );
26
27 $phrases = array();
28 while ( false !== $pos ) {
29 if ( $pos + 2 > relevanssi_strlen( $normalized_query ) ) {
30 $pos = false;
31 continue;
32 }
33 $start = relevanssi_stripos( $normalized_query, '"', $pos );
34 $end = false;
35 if ( false !== $start ) {
36 $end = relevanssi_stripos( $normalized_query, '"', $start + 2 );
37 }
38 if ( false === $end ) {
39 // Just one " in the query.
40 $pos = $end;
41 continue;
42 }
43 $phrase = relevanssi_substr(
44 $normalized_query,
45 $start + 1,
46 $end - $start - 1
47 );
48 $phrase = trim( $phrase );
49
50 // Do not count single-word phrases as phrases.
51 if ( relevanssi_is_multiple_words( $phrase ) ) {
52 $phrases[] = $phrase;
53 }
54 $pos = $end + 1;
55 }
56
57 return $phrases;
58 }
59
60 /**
61 * Generates the MySQL code for restricting the search to phrase hits.
62 *
63 * This function uses relevanssi_extract_phrases() to figure out the phrases in
64 * the search query, then generates MySQL queries to restrict the search to the
65 * posts containing those phrases in the title, content, taxonomy terms or meta
66 * fields.
67 *
68 * @global array $relevanssi_variables The global Relevanssi variables.
69 *
70 * @param string $search_query The search query.
71 * @param string $operator The search operator (AND or OR).
72 *
73 * @return string $queries If not phrase hits are found, an empty string;
74 * otherwise MySQL queries to restrict the search.
75 */
76 function relevanssi_recognize_phrases( $search_query, $operator = 'AND' ) {
77 global $relevanssi_variables;
78
79 $phrases = relevanssi_extract_phrases( $search_query );
80
81 $all_queries = array();
82 if ( 0 === count( $phrases ) ) {
83 return $all_queries;
84 }
85
86 /**
87 * Filters the custom fields for phrase matching.
88 *
89 * If you don't want the phrase matching to target custom fields, you can
90 * have this filter hook return an empty array.
91 *
92 * @param array $custom_fields An array of custom field names.
93 */
94 $custom_fields = apply_filters( 'relevanssi_phrase_custom_fields', relevanssi_get_custom_fields() );
95
96 /**
97 * Filters the taxonomies for phrase matching.
98 *
99 * If you don't want the phrase matching to target taxonomies, you can have
100 * this filter hook return an empty array.
101 *
102 * @param array $taxonomies An array of taxonomy names.
103 */
104 $taxonomies = apply_filters( 'relevanssi_phrase_taxonomies', get_option( 'relevanssi_index_taxonomies_list', array() ) );
105 $excerpts = get_option( 'relevanssi_index_excerpt', 'off' );
106 $phrase_queries = array();
107 $queries = array();
108
109 if (
110 isset( $relevanssi_variables['phrase_targets'] ) &&
111 is_array( $relevanssi_variables['phrase_targets'] )
112 ) {
113 $non_targeted_phrases = array();
114 foreach ( $phrases as $phrase ) {
115 if (
116 isset( $relevanssi_variables['phrase_targets'][ $phrase ] ) &&
117 function_exists( 'relevanssi_targeted_phrases' )
118 ) {
119 $queries = relevanssi_targeted_phrases( $phrase );
120 } else {
121 $non_targeted_phrases[] = $phrase;
122 }
123 }
124 $phrases = $non_targeted_phrases;
125 }
126
127 $queries = array_merge(
128 $queries,
129 relevanssi_generate_phrase_queries(
130 $phrases,
131 $taxonomies,
132 $custom_fields,
133 $excerpts
134 )
135 );
136
137 $phrase_queries = array();
138
139 foreach ( $queries as $phrase => $p_queries ) {
140 $pq_array = array();
141 foreach ( $p_queries as $query ) {
142 $pq_array[] = "relevanssi.{$query['target']} IN {$query['query']}";
143 }
144 $p_queries = implode( ' OR ', $pq_array );
145 $all_queries[] = "($p_queries)";
146
147 $phrase_queries[ $phrase ] = $p_queries;
148 }
149
150 $operator = strtoupper( $operator );
151 if ( 'AND' !== $operator && 'OR' !== $operator ) {
152 $operator = 'AND';
153 }
154
155 if ( ! empty( $all_queries ) ) {
156 $all_queries = ' AND ( ' . implode( ' ' . $operator . ' ', $all_queries ) . ' ) ';
157 }
158
159 return array(
160 'and' => $all_queries,
161 'or' => $phrase_queries,
162 );
163 }
164
165 /**
166 * Generates the phrase queries from phrases.
167 *
168 * Takes in phrases and a bunch of parameters and generates the MySQL queries
169 * that restrict the main search query to only posts that have the phrase.
170 *
171 * @param array $phrases A list of phrases to handle.
172 * @param array $taxonomies An array of taxonomy names to use.
173 * @param array|string $custom_fields A list of custom field names to use,
174 * "visible", or "all".
175 * @param string $excerpts If 'on', include excerpts.
176 *
177 * @global object $wpdb The WordPress database interface.
178 *
179 * @return array An array of queries sorted by phrase.
180 */
181 function relevanssi_generate_phrase_queries(
182 array $phrases,
183 array $taxonomies,
184 $custom_fields,
185 string $excerpts
186 ): array {
187 global $wpdb;
188
189 $status = relevanssi_valid_status_array();
190
191 // Add "inherit" to the list of allowed statuses to include attachments.
192 if ( ! strstr( $status, 'inherit' ) ) {
193 $status .= ",'inherit'";
194 }
195
196 $phrase_queries = array();
197
198 foreach ( $phrases as $phrase ) {
199 $queries = array();
200 $phrase = $wpdb->esc_like( $phrase );
201 $phrase = str_replace( array( '‘', '’', "'", '"', '”', '“', '“', '„', '´' ), '_', $phrase );
202 $title_phrase = $phrase;
203 $phrase = htmlspecialchars( $phrase );
204
205 /**
206 * Filters each phrase before it's passed through esc_sql() and used in
207 * the MySQL query. You can use this filter hook to for example run
208 * htmlentities() on the phrase in case your database needs that.
209 *
210 * @param string $phrase The phrase after quotes are replaced with a
211 * MySQL wild card and the phrase has been passed through esc_like() and
212 * htmlspecialchars().
213 */
214 $phrase = esc_sql( apply_filters( 'relevanssi_phrase', $phrase ) );
215
216 $excerpt = '';
217 if ( 'on' === $excerpts ) {
218 $excerpt = "OR post_excerpt LIKE '%$phrase%'";
219 }
220
221 $query = "(SELECT ID FROM $wpdb->posts
222 WHERE (post_content LIKE '%$phrase%'
223 OR post_title LIKE '%$title_phrase%' $excerpt)
224 AND post_status IN ($status))";
225
226 $queries[] = array(
227 'query' => $query,
228 'target' => 'doc',
229 );
230
231 if ( ! empty( $taxonomies ) ) {
232 $taxonomies_escaped = implode( "','", array_map( 'esc_sql', $taxonomies ) );
233 $taxonomies_sql = "AND s.taxonomy IN ('$taxonomies_escaped')";
234
235 $query = "(SELECT ID FROM
236 $wpdb->posts as p,
237 $wpdb->term_relationships as r,
238 $wpdb->term_taxonomy as s, $wpdb->terms as t
239 WHERE r.term_taxonomy_id = s.term_taxonomy_id
240 AND s.term_id = t.term_id AND p.ID = r.object_id
241 $taxonomies_sql
242 AND t.name LIKE '%$phrase%' AND p.post_status IN ($status))";
243
244 $queries[] = array(
245 'query' => $query,
246 'target' => 'doc',
247 );
248 }
249
250 if ( ! empty( $custom_fields ) ) {
251 $keys = '';
252
253 if ( is_array( $custom_fields ) ) {
254 if ( ! in_array( '_relevanssi_pdf_content', $custom_fields, true ) ) {
255 array_push( $custom_fields, '_relevanssi_pdf_content' );
256 }
257
258 if ( strpos( implode( ' ', $custom_fields ), '%' ) ) {
259 // ACF repeater fields involved.
260 $custom_fields_regexp = str_replace( '%', '.+', implode( '|', $custom_fields ) );
261 $keys = "AND m.meta_key REGEXP ('$custom_fields_regexp')";
262 } else {
263 $custom_fields_escaped = implode(
264 "','",
265 array_map(
266 'esc_sql',
267 $custom_fields
268 )
269 );
270 $keys = "AND m.meta_key IN ('$custom_fields_escaped')";
271 }
272 }
273
274 if ( 'visible' === $custom_fields ) {
275 $keys = "AND (m.meta_key NOT LIKE '\_%' OR m.meta_key = '_relevanssi_pdf_content')";
276 }
277
278 $query = "(SELECT ID
279 FROM $wpdb->posts AS p, $wpdb->postmeta AS m
280 WHERE p.ID = m.post_id
281 $keys
282 AND m.meta_value LIKE '%$phrase%'
283 AND p.post_status IN ($status))";
284
285 $queries[] = array(
286 'query' => $query,
287 'target' => 'doc',
288 );
289 }
290
291 /**
292 * Filters the phrase queries.
293 *
294 * Relevanssi Premium uses this filter hook to add Premium-specific
295 * phrase queries.
296 *
297 * @param array $queries The MySQL queries for phrase matching.
298 * @param string $phrase The current phrase.
299 * @param string $status A string containing post statuses.
300 *
301 * @return array An array of phrase queries, where each query is an
302 * array that has the actual MySQL query in 'query' and the target
303 * column ('doc' or 'item') in the Relevanssi index table in 'target'.
304 */
305 $queries = apply_filters( 'relevanssi_phrase_queries', $queries, $phrase, $status );
306
307 $phrase_queries[ $phrase ] = $queries;
308 }
309
310 return $phrase_queries;
311 }
1 <?php
2 /**
3 * /lib/privacy.php
4 *
5 * Privacy policy features.
6 *
7 * @since 4.0.10
8 *
9 * @package Relevanssi
10 * @author Mikko Saari
11 * @license https://wordpress.org/about/gpl/ GNU General Public License
12 * @see https://www.relevanssi.com/
13 */
14
15 add_action( 'admin_init', 'relevanssi_register_privacy_policy' );
16 add_filter( 'wp_privacy_personal_data_exporters', 'relevanssi_register_exporter', 10 );
17 add_filter( 'wp_privacy_personal_data_erasers', 'relevanssi_register_eraser', 10 );
18
19 /**
20 * Registers the Relevanssi privacy policy information.
21 *
22 * @since 4.0.10
23 */
24 function relevanssi_register_privacy_policy() {
25 if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
26 return;
27 }
28 $name = 'Relevanssi';
29 if ( RELEVANSSI_PREMIUM ) {
30 $name .= ' Premium';
31 }
32 $content = '';
33 if ( 'on' === get_option( 'relevanssi_log_queries' ) ) {
34 $content = '<h2>' . __( 'What personal data we collect and why we collect it' ) . '</h2>';
35 if ( 'on' === get_option( 'relevanssi_log_queries_with_ip' ) ) {
36 $content .= '<h3>' . __( 'IP address for searches', 'relevanssi' ) . '</h3>';
37 $content .= '<p>' . __( 'All searches performed using the internal site search are logged in the database, including the following information: the search query, the number of hits found, user ID for users who are logged in, date and time and the IP address. The IP address is stored for security and auditing purposes.', 'relevanssi' ) . '</p>';
38 } else {
39 $content .= '<p>' . __( 'All searches performed using the internal site search are logged in the database, including the following information: the search query, the number of hits found, user ID for users who are logged in and date and time.', 'relevanssi' ) . '</p>';
40 }
41 $interval = intval( get_option( 'relevanssi_trim_logs' ) );
42 $content .= '<h2>' . __( 'How long we retain your data' ) . '</h2>';
43 if ( $interval > 0 ) {
44 // Translators: %d is the number of days.
45 $content .= '<p>' . sprintf( __( 'The search logs are stored for %d days before they are automatically removed.', 'relevanssi' ), $interval ) . '</p>';
46 } else {
47 $content .= '<p>' . __( 'The search logs are stored indefinitely.', 'relevanssi' ) . '</p>';
48 }
49 }
50 wp_add_privacy_policy_content( $name, $content );
51 }
52
53 /**
54 * Registers the Relevanssi data exporter.
55 *
56 * @since 4.0.10
57 *
58 * @param array $exporters The exporters array.
59 *
60 * @return array The exporters array, with Relevanssi added.
61 */
62 function relevanssi_register_exporter( $exporters ) {
63 $exporters['relevanssi'] = array(
64 'exporter_friendly_name' => __( 'Relevanssi Search Logs' ),
65 'callback' => 'relevanssi_privacy_exporter',
66 );
67 return $exporters;
68 }
69
70 /**
71 * Registers the Relevanssi data eraser.
72 *
73 * @since 4.0.10
74 *
75 * @param array $erasers The erasers array.
76 *
77 * @return array The erasers array, with Relevanssi added.
78 */
79 function relevanssi_register_eraser( $erasers ) {
80 $erasers['relevanssi'] = array(
81 'eraser_friendly_name' => __( 'Relevanssi Search Logs' ),
82 'callback' => 'relevanssi_privacy_eraser',
83 );
84 return $erasers;
85 }
86
87 /**
88 * Exports the log entries based on user email.
89 *
90 * @since 4.0.10
91 *
92 * @param string $email_address The user email address.
93 * @param int $page The page number, default 1.
94 *
95 * @return array Two-item array: 'done' is a Boolean that tells if the exporter is
96 * done, 'data' contains the actual data.
97 */
98 function relevanssi_privacy_exporter( $email_address, $page = 1 ) {
99 $user = get_user_by( 'email', $email_address );
100 if ( ! $user ) {
101 // No user found.
102 return array(
103 'done' => true,
104 'data' => array(),
105 );
106 } else {
107 $result = relevanssi_export_log_data( $user->ID, $page );
108 return array(
109 'done' => $result['done'],
110 'data' => $result['data'],
111 );
112 }
113 }
114
115 /**
116 * Erases the log entries based on user email.
117 *
118 * @since 4.0.10
119 *
120 * @param string $email_address The user email address.
121 * @param int $page The page number, default 1.
122 *
123 * @return array Four-item array: 'items_removed' is a Boolean that tells if
124 * something was removed, 'done' is a Boolean that tells if the eraser is done,
125 * 'items_retained' is always false, 'messages' is always an empty array.
126 */
127 function relevanssi_privacy_eraser( $email_address, $page = 1 ) {
128 $user = get_user_by( 'email', $email_address );
129 if ( ! $user ) {
130 // No user found.
131 return array(
132 'items_removed' => false,
133 'done' => true,
134 'items_retained' => false,
135 'messages' => array(),
136 );
137 } else {
138 $result = relevanssi_erase_log_data( $user->ID, $page );
139 return array(
140 'items_removed' => $result['items_removed'],
141 'done' => $result['done'],
142 'items_retained' => false,
143 'messages' => array(),
144 );
145 }
146 }
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
19 * it if you 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
29 * it as 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 $attributes = shortcode_atts(
36 array(
37 'term' => false,
38 'phrase' => 'not',
39 ),
40 $atts
41 );
42
43 $term = $attributes['term'];
44 $phrase = $attributes['phrase'];
45
46 if ( false !== $term ) {
47 $term = rawurlencode( relevanssi_strtolower( $term ) );
48 } else {
49 $term = rawurlencode( wp_strip_all_tags( relevanssi_strtolower( $content ) ) );
50 }
51
52 if ( 'not' !== $phrase ) {
53 $term = '%22' . $term . '%22';
54 }
55
56 $link = get_bloginfo( 'url' ) . "/?s=$term";
57 $pre = "<a rel='nofollow' href='$link'>"; // rel='nofollow' for Google.
58 $post = '</a>';
59
60 return $pre . do_shortcode( $content ) . $post;
61 }
62
63 /**
64 * Does nothing.
65 *
66 * In normal use, the [noindex] shortcode does nothing.
67 *
68 * @param array $atts The shortcode attributes. Not used.
69 * @param string $content The content inside the shortcode tags.
70 *
71 * @return string The shortcode content.
72 */
73 function relevanssi_noindex_shortcode( $atts, $content ) {
74 return do_shortcode( $content );
75 }
76
77 /**
78 * Returns nothing.
79 *
80 * During indexing, the [noindex] shortcode returns nothing.
81 *
82 * @return string An empty string.
83 */
84 function relevanssi_noindex_shortcode_indexing() {
85 return '';
86 }
87
88 /**
89 * Returns a search form.
90 *
91 * Returns a search form generated by get_search_form(). Any attributes passed to the
92 * shortcode will be passed onto the search form, for example like this:
93 *
94 * [searchform post_types='post,product']
95 *
96 * This would add a
97 *
98 * <input type="hidden" name="post_types" value="post,product" />
99 *
100 * to the search form.
101 *
102 * @param array $atts The shortcode attributes.
103 *
104 * @return string A search form.
105 */
106 function relevanssi_search_form( $atts ) {
107 $form = get_search_form( false );
108 if ( is_array( $atts ) ) {
109 $additional_fields = array();
110 foreach ( $atts as $key => $value ) {
111 if ( 'dropdown' === substr( $key, 0, 8 ) ) {
112 $key = 'dropdown';
113 }
114 if ( 'checklist' === substr( $key, 0, 9 ) ) {
115 $key = 'checklist';
116 }
117 if ( 'post_type_boxes' === $key ) {
118 $post_types = explode( ',', $value );
119 if ( is_array( $post_types ) ) {
120 $post_type_objects = get_post_types( array(), 'objects' );
121 $additional_fields[] = '<div class="post_types"><strong>Post types</strong>: ';
122 foreach ( $post_types as $post_type ) {
123 $checked = '';
124 if ( '*' === substr( $post_type, 0, 1 ) ) {
125 $post_type = substr( $post_type, 1 );
126 $checked = ' checked="checked" ';
127 }
128 if ( isset( $post_type_objects[ $post_type ] ) ) {
129 $additional_fields[] = '<span class="post_type post_type_' . $post_type . '">'
130 . '<input type="checkbox" name="post_types[]" value="' . $post_type . '"' . $checked . '/> '
131 . $post_type_objects[ $post_type ]->name . '</span>';
132 }
133 }
134 $additional_fields[] = '</div>';
135 }
136 } elseif ( 'dropdown' === $key && 'post_type' === $value ) {
137 $field = '<select name="post_type">';
138 $types = get_option( 'relevanssi_index_post_types', array() );
139 if ( ! is_array( $types ) ) {
140 $types = array();
141 }
142 foreach ( $types as $type ) {
143 if ( post_type_exists( $type ) ) {
144 $object = get_post_type_object( $type );
145 $field .= '<option value="' . $type . '">' . $object->labels->singular_name . '</option>';
146 }
147 }
148 $field .= '</select>';
149 $additional_fields[] = $field;
150
151 } elseif ( 'dropdown' === $key && 'post_type' !== $value ) {
152 $name = $value;
153 if ( 'category' === $value ) {
154 $name = 'cat';
155 }
156 if ( 'post_tag' === $value ) {
157 $name = 'tag';
158 }
159 $args = array(
160 'taxonomy' => $value,
161 'echo' => 0,
162 'hide_if_empty' => true,
163 'show_option_none' => __( 'None' ),
164 'name' => $name,
165 'option_none_value' => 0,
166 );
167 $additional_fields[] = wp_dropdown_categories( $args );
168 } elseif ( 'checklist' === $key && 'post_type' !== $value ) {
169 $name = $value;
170 if ( 'category' === $value ) {
171 $name = 'cat';
172 }
173 if ( 'post_tag' === $value ) {
174 $name = 'tag';
175 }
176 $args = array(
177 'taxonomy' => $value,
178 'echo' => 0,
179 );
180 if ( ! function_exists( 'wp_terms_checklist' ) ) {
181 include ABSPATH . 'wp-admin/includes/template.php';
182 }
183 $checklist = wp_terms_checklist( 0, $args );
184 $checklist = str_replace( 'post_category', 'cats', $checklist );
185 $checklist = str_replace( 'tax_input[post_tag]', 'tags', $checklist );
186 $checklist = str_replace( "disabled='disabled'", '', $checklist );
187 $checklist = preg_replace( '/tax_input\[(.*?)\]/', '\1', $checklist );
188 $additional_fields[] = $checklist;
189 } else {
190 $key = esc_attr( $key );
191 $value = esc_attr( $value );
192
193 $additional_fields[] = "<input type='hidden' name='$key' value='$value' />";
194 }
195 }
196 $form = str_replace( '</form>', implode( "\n", $additional_fields ) . '</form>', $form );
197 }
198 /**
199 * Filters the Relevanssi shortcode search form before it's used.
200 *
201 * @param string $form The form HTML code.
202 * @param array $atts The shortcode attributes.
203 */
204 return apply_filters( 'relevanssi_search_form', $form, $atts );
205 }
1 <?php
2 /**
3 * /lib/tabs/attachments-tab.php
4 *
5 * Prints out the Attachments tab in Relevanssi settings.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 /**
14 * Prints out the attachments tab in Relevanssi settings.
15 */
16 function relevanssi_attachments_tab() {
17 ?>
18 <h2><?php esc_html_e( 'Indexing attachment content', 'relevanssi' ); ?></h2>
19
20 <p><?php esc_html_e( 'With Relevanssi Premium, you can index the text contents of attachments (PDFs, Word documents, Open Office documents and many other types). 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>
21 <?php // Translators: %1$s starts the link, %2$s closes it. ?>
22 <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>
23 <?php
24 }
1 <?php
2 /**
3 * /lib/tabs/debugging-tab.php
4 *
5 * Prints out the Debugging tab in Relevanssi settings.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 /**
14 * Prints out the debugging tab in Relevanssi settings.
15 */
16 function relevanssi_debugging_tab() {
17 $how_relevanssi_sees = '';
18 $db_post_view = '';
19 $current_post_id = 0;
20 $current_db_post_id = 0;
21 $selected = 'post';
22 if ( isset( $_REQUEST['post_id'] ) ) {
23 wp_verify_nonce( '_relevanssi_nonce', 'relevanssi_how_relevanssi_sees' );
24 $type = 'post';
25 if ( isset( $_REQUEST['type'] ) ) {
26 if ( 'term' === $_REQUEST['type'] ) {
27 $type = 'term';
28 $selected = 'term';
29 }
30 if ( 'user' === $_REQUEST['type'] ) {
31 $type = 'user';
32 $selected = 'user';
33 }
34 }
35 if ( intval( $_REQUEST['post_id'] ) > 0 ) {
36 $current_post_id = intval( $_REQUEST['post_id'] );
37 $how_relevanssi_sees = relevanssi_generate_how_relevanssi_sees(
38 intval( $current_post_id ),
39 true,
40 $type
41 );
42 }
43 }
44
45 if ( isset( $_REQUEST['db_post_id'] ) ) {
46 wp_verify_nonce( '_relevanssi_nonce', 'relevanssi_how_relevanssi_sees' );
47 if ( intval( $_REQUEST['db_post_id'] ) > 0 ) {
48 $current_db_post_id = intval( $_REQUEST['db_post_id'] );
49 $db_post_view = relevanssi_generate_db_post_view( $current_db_post_id );
50 }
51 }
52 wp_nonce_field( 'relevanssi_how_relevanssi_sees', '_relevanssi_nonce', true, true );
53 ?>
54 <h2><?php esc_html_e( 'Debugging', 'relevanssi' ); ?></h2>
55
56 <p><?php esc_html_e( 'In order to figure out problems with indexing posts, you can test how Relevanssi sees the post by entering the post ID number in the field below.', 'relevanssi' ); ?></p>
57 <?php
58 if ( RELEVANSSI_PREMIUM ) {
59 ?>
60 <p><?php esc_html_e( 'You can also check user profiles and taxonomy terms by choosing the type from the dropdown.', 'relevanssi' ); ?></p>
61 <?php
62 }
63 if ( ! RELEVANSSI_PREMIUM ) {
64 // Translators: %1$s starts the link, %2$s closes it.
65 printf( '<p>' . esc_html__( 'In Relevanssi Premium, you can find this feature for each post on the post edit page. %1$sBuy Relevanssi Premium here%2$s.', 'relevanssi' ) . '</p>', '<a href="https://www.relevanssi.com/buy-premium/">', '</a>' );
66 }
67 ?>
68 <p><label for="post_id"><?php esc_html_e( 'The ID', 'relevanssi' ); ?></label>:
69 <input type="text" name="post_id" id="post_id"
70 <?php
71 if ( $current_post_id > 0 ) {
72 echo 'value="' . esc_attr( $current_post_id ) . '"';
73 }
74 ?>
75 />
76 <?php
77 if ( RELEVANSSI_PREMIUM ) {
78 ?>
79 <select name="type">
80 <option value="post"
81 <?php if ( 'post' === $selected ) { ?>
82 selected="selected"
83 <?php } ?>><?php esc_html_e( 'Post', 'relevanssi' ); ?></option>
84 <option value="term"
85 <?php if ( 'term' === $selected ) { ?>
86 selected="selected"
87 <?php } ?>><?php esc_html_e( 'Taxonomy term', 'relevanssi' ); ?></option>
88 <option value="user"
89 <?php if ( 'user' === $selected ) { ?>
90 selected="selected"
91 <?php } ?>><?php esc_html_e( 'User', 'relevanssi' ); ?></option>
92 </select>
93 <?php
94 }
95 ?>
96 </p>
97 <p>
98 <input
99 type='submit' name='submit'
100 value='<?php esc_attr_e( 'Check the post', 'relevanssi' ); ?>'
101 class='button button-primary' />
102 </p>
103
104 <?php echo $how_relevanssi_sees; // phpcs:ignore WordPress.Security.EscapeOutput ?>
105
106 <h2><?php esc_html_e( 'What does the post look like in the database?', 'relevanssi' ); ?></h2>
107
108 <p><?php esc_html_e( "This feature will show you how the post looks like in the database. It can sometimes be very helpful for debugging why a post isn't indexed the way you expect it to be.", 'relevanssi' ); ?></p>
109
110 <p><label for="db_post_id"><?php esc_html_e( 'The ID', 'relevanssi' ); ?></label>:
111 <input type="text" name="db_post_id" id="db_post_id"
112 <?php
113 if ( $current_db_post_id > 0 ) {
114 echo 'value="' . esc_attr( $current_db_post_id ) . '"';
115 }
116 ?>
117 />
118 </p>
119 <p>
120 <input
121 type='submit' name='submit'
122 value='<?php esc_attr_e( 'Check the post', 'relevanssi' ); ?>'
123 class='button button-primary' />
124 </p>
125
126 <?php echo $db_post_view; // phpcs:ignore WordPress.Security.EscapeOutput ?>
127
128 <h2><?php esc_html_e( 'Debugging information', 'relevanssi' ); ?></h2>
129
130 <?php
131 global $wpdb;
132 $max_allowed_packet = $wpdb->get_var( 'SELECT @@global.max_allowed_packet' );
133 $max_allowed_packet = round( $max_allowed_packet / 1024 / 1024, 2 );
134 echo '<p>max_allowed_packet: ' . $max_allowed_packet . 'M</p>'; // phpcs:ignore WordPress.Security.EscapeOutput
135
136 $indexing_query = relevanssi_generate_indexing_query(
137 relevanssi_valid_status_array(),
138 false,
139 relevanssi_post_type_restriction(),
140 'LIMIT 0'
141 );
142 ?>
143 <p><?php esc_html_e( 'Indexing query', 'relevanssi' ); ?>:</p>
144 <?php
145 echo '<code>' . $indexing_query . '</code>'; // phpcs:ignore WordPress.Security.EscapeOutput
146 ?>
147
148 <?php do_action( 'relevanssi_debugging_tab' ); ?>
149
150 <h2><?php esc_html_e( 'Debugging mode', 'relevanssi' ); ?></h2>
151
152 <?php
153 $enable_debugging_mode = relevanssi_check( get_option( 'relevanssi_debugging_mode' ) );
154 ?>
155
156 <fieldset>
157 <legend class="screen-reader-text"><?php esc_html_e( 'Enable the debugging mode.', 'relevanssi' ); ?></legend>
158 <label for='relevanssi_debugging_mode'>
159 <input type='checkbox' name='relevanssi_debugging_mode' id='relevanssi_debugging_mode' <?php echo esc_html( $enable_debugging_mode ); ?> />
160 <?php esc_html_e( 'Enable the debugging mode.', 'relevanssi' ); ?>
161 </label>
162 <p class="description"><?php esc_html_e( "Relevanssi support may ask you to enable the debugging mode. When you check this box, it's possible to see debugging information from the front-end.", 'relevanssi' ); ?></p>
163 </fieldset>
164
165 <?php
166 }
1 <?php
2 /**
3 * /lib/tabs/logging-tab.php
4 *
5 * Prints out the Logging tab in Relevanssi settings.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 /**
14 * Prints out the logging tab in Relevanssi settings.
15 *
16 * @global $wpdb The WordPress database interface.
17 */
18 function relevanssi_logging_tab() {
19 global $wpdb;
20
21 $log_queries = get_option( 'relevanssi_log_queries' );
22 $log_queries = relevanssi_check( $log_queries );
23 $log_queries_with_ip = get_option( 'relevanssi_log_queries_with_ip' );
24 $log_queries_with_ip = relevanssi_check( $log_queries_with_ip );
25 $omit_from_logs = get_option( 'relevanssi_omit_from_logs' );
26 $trim_logs = get_option( 'relevanssi_trim_logs' );
27
28 ?>
29 <table class="form-table" role="presentation">
30 <tr>
31 <th scope="row">
32 <?php esc_html_e( 'Enable logs', 'relevanssi' ); ?>
33 </th>
34 <td>
35 <fieldset>
36 <legend class="screen-reader-text"><?php esc_html_e( 'Keep a log of user queries.', 'relevanssi' ); ?></legend>
37 <label for='relevanssi_log_queries'>
38 <input type='checkbox' name='relevanssi_log_queries' id='relevanssi_log_queries' <?php echo esc_html( $log_queries ); ?> />
39 <?php esc_html_e( 'Keep a log of user queries.', 'relevanssi' ); ?>
40 </label>
41 </fieldset>
42 <p class="description">
43 <?php
44 // Translators: %1$s is the name of the "User searches" page, %2$s is the name of the database table.
45 printf(
46 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' ),
47 esc_html__( 'User searches', 'relevanssi' ),
48 esc_html( $wpdb->prefix . 'relevanssi_log' )
49 );
50 ?>
51 </p>
52 </td>
53 </tr>
54 <tr>
55 <th scope="row">
56 <?php esc_html_e( 'Log user IP', 'relevanssi' ); ?>
57 </th>
58 <td>
59 <fieldset>
60 <legend class="screen-reader-text"><?php esc_html_e( "Log the user's IP with the queries.", 'relevanssi' ); ?></legend>
61 <label for='relevanssi_log_queries_with_ip'>
62 <input type='checkbox' name='relevanssi_log_queries_with_ip' id='relevanssi_log_queries_with_ip' <?php echo esc_html( $log_queries_with_ip ); ?> />
63 <?php esc_html_e( "Log the user's IP with the queries.", 'relevanssi' ); ?>
64 </label>
65 </fieldset>
66 <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>
67 </td>
68 </tr>
69 <tr>
70 <th scope="row">
71 <label for='relevanssi_omit_from_logs'><?php esc_html_e( 'Exclude users', 'relevanssi' ); ?></label>
72 </th>
73 <td>
74 <input type='text' name='relevanssi_omit_from_logs' id='relevanssi_omit_from_logs' size='60' value='<?php echo esc_attr( $omit_from_logs ); ?>' />
75 <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>
76 </td>
77 </tr>
78 <?php
79 if ( function_exists( 'relevanssi_form_hide_branding' ) ) {
80 relevanssi_form_hide_branding();
81 }
82 ?>
83 <tr>
84 <th scope="row">
85 <label for='relevanssi_trim_logs'><?php esc_html_e( 'Trim logs', 'relevanssi' ); ?></label>
86 </th>
87 <td>
88 <input type='number' name='relevanssi_trim_logs' id='relevanssi_trim_logs' value='<?php echo esc_attr( $trim_logs ); ?>' />
89 <?php esc_html_e( 'How many days of logs to keep in the database.', 'relevanssi' ); ?>
90 <?php
91 if ( '0' === $trim_logs ) {
92 echo '<p class="description">';
93 esc_html_e( "Big log database table will eventually start to slow down the search, so it's a good idea to use some level of automatic log trimming.", 'relevanssi' );
94 echo '</p>';
95 } else {
96 echo '<p class="description">';
97 // Translators: %d is the setting for no trim (probably 0).
98 printf( esc_html__( 'Set to %d for no trimming.', 'relevanssi' ), 0 );
99 echo '</p>';
100 }
101 ?>
102 </td>
103 </tr>
104
105 <tr>
106 <th scope="row">
107 <?php esc_html_e( 'Export logs', 'relevanssi' ); ?>
108 </th>
109 <td>
110 <?php submit_button( __( 'Export the log as a CSV file', 'relevanssi' ), 'secondary', 'relevanssi_export' ); ?>
111 <p class="description"><?php esc_html_e( 'Push the button to export the search log as a CSV file.', 'relevanssi' ); ?></p>
112 </td>
113 </tr>
114
115 </table>
116 <?php
117
118 if ( function_exists( 'relevanssi_click_tracking_interface' ) ) {
119 relevanssi_click_tracking_interface();
120 } else {
121 ?>
122 <h3><?php esc_html_e( 'Click tracking', 'relevanssi' ); ?></h3>
123 <p><?php esc_html_e( 'Relevanssi Premium has a click tracking feature where you can track which posts are clicked from the search results. That way you can tell what is your most interesting content and how the search is actually used to access posts.', 'relevanssi' ); ?></p>
124 <?php
125 }
126 }
1 <?php
2 /**
3 * /lib/tabs/overview-tab.php
4 *
5 * Prints out the Overview tab in Relevanssi settings.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 /**
14 * Prints out the overview tab in Relevanssi settings.
15 *
16 * @global array $relevanssi_variables The global Relevanssi variables array.
17 */
18 function relevanssi_overview_tab() {
19 global $relevanssi_variables;
20 $this_page = '?page=' . plugin_basename( $relevanssi_variables['file'] );
21 ?>
22 <h2><?php esc_html_e( 'Welcome to Relevanssi!', 'relevanssi' ); ?></h2>
23
24 <table class="form-table" role="presentation">
25
26 <?php
27 if ( ! is_plugin_active_for_network( plugin_basename( $relevanssi_variables['file'] ) ) && function_exists( 'relevanssi_form_api_key' ) ) {
28 relevanssi_form_api_key();
29 }
30 if ( function_exists( 'relevanssi_form_hide_post_controls' ) ) {
31 relevanssi_form_hide_post_controls();
32 }
33 if ( function_exists( 'relevanssi_form_do_not_call_home' ) ) {
34 relevanssi_form_do_not_call_home();
35 }
36 if ( function_exists( 'relevanssi_form_update_translations' ) ) {
37 relevanssi_form_update_translations();
38 }
39 ?>
40 <tr>
41 <th scope="row"><?php esc_html_e( 'Getting started', 'relevanssi' ); ?></th>
42 <td>
43 <p><?php esc_html_e( "You've already installed Relevanssi. That's a great first step towards good search experience!", 'relevanssi' ); ?></p>
44 <ol>
45 <?php if ( 'done' !== get_option( 'relevanssi_indexed' ) ) : ?>
46 <?php // Translators: %1$s opens the link, %2$s is the anchor text, %3$s closes the link. ?>
47 <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>
48 <p><?php esc_html_e( 'You need to check at least the following options:', 'relevanssi' ); ?><br />
49 &ndash; <?php esc_html_e( 'Make sure the post types you want to include in the index are indexed.', 'relevanssi' ); ?><br />
50 <?php // Translators: %s is '_sku'. ?>
51 &ndash; <?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>
52 <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>
53 </li>
54 <?php else : ?>
55 <li><p><?php esc_html_e( 'Great, you already have an index!', 'relevanssi' ); ?></p></li>
56 <?php endif; ?>
57 <li>
58 <?php // Translators: %1$s opens the link, %2$s is the anchor text, %3$s closes the link. ?>
59 <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>
60 </li>
61 <li>
62 <?php // Translators: %1$s opens the link, %2$s is the anchor text, %3$s closes the link. ?>
63 <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>
64 <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>
65 </li>
66 <li>
67 <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>
68 </li>
69 </ol>
70 <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>
71 </td>
72 </tr>
73 <tr>
74 <th scope="row"><?php esc_html_e( 'Relevanssi Live Ajax Search', 'relevanssi' ); ?></th>
75 <td>
76 <?php // Translators: %1$s opens the link, %2$s closes it. ?>
77 <p><?php printf( esc_html__( 'If you want a live search results, you can use the Relevanssi Live Ajax Search plugin. %1$sYou can find it in the plugin repository%2$s. It will make your search forms show instant results, powered by Relevanssi.', 'relevanssi' ), "<a href='https://wordpress.org/plugins/relevanssi-live-ajax-search/'>", '</a>' ); ?></p>
78 </td>
79 </tr>
80 <tr>
81 <th scope="row"><?php esc_html_e( 'Privacy and GDPR compliance', 'relevanssi' ); ?></th>
82 <td>
83 <?php // Translators: %1$s and %3$s open the links, %2$s closes them. ?>
84 <p><?php printf( esc_html__( '%1$sGDPR Compliance at Relevanssi knowledge base%2$s explains how using Relevanssi affects the GDPR compliance and the privacy policies of your site. Relevanssi also supports the %3$sprivacy policy tool%2$s and the WordPress user data export and erase tools.', 'relevanssi' ), "<a href='https://www.relevanssi.com/knowledge-base/gdpr-compliance/'>", '</a>', "<a href='privacy.php'>" ); ?></p>
85 </td>
86 </tr>
87 <tr>
88 <th scope="row"><?php esc_html_e( 'For more information', 'relevanssi' ); ?></th>
89 <td>
90 <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>
91 <?php // Translators: %1$s opens the link, %2$s closes the link. ?>
92 <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>
93 </td>
94 </tr>
95 <tr>
96 <th scope="row"><?php esc_html_e( 'Do you like Relevanssi?', 'relevanssi' ); ?></th>
97 <td>
98 <p><?php esc_html_e( 'If you do, the best way to show your appreciation is to spread the word and perhaps give us a good review on WordPress.org.', 'relevanssi' ); ?></p>
99 <?php // Translators: %1$s opens the link, %2$s closes the link. ?>
100 <p><?php printf( esc_html__( 'If you like Relevanssi, leaving a five-star review on WordPress.org will help others discover Relevanssi. %1$sYou can add your review here%2$s.', 'relevanssi' ), "<a href='https://wordpress.org/support/plugin/relevanssi/reviews/#new-post'>", '</a>' ); ?></p>
101 </td>
102 </tr>
103 <?php if ( ! RELEVANSSI_PREMIUM ) { ?>
104 <tr>
105 <th scope="row">
106 <?php esc_html_e( 'Buy Relevanssi Premium', 'relevanssi' ); ?>
107 </th>
108 <td>
109 <p><a href="https://www.relevanssi.com/buy-premium"><?php esc_html_e( 'Buy Relevanssi Premium now', 'relevanssi' ); ?></a>
110 <?php // Translators: %1$s is the coupon code, %2$s is the year it expires. ?>
111 <?php printf( esc_html__( 'use coupon code %1$s for 20%% discount (valid at least until the end of %2$s)', 'relevanssi' ), '<strong>FREE2023</strong>', '2023' ); ?></p>
112 <p><?php esc_html_e( 'Here are some improvements Relevanssi Premium offers:', 'relevanssi' ); ?></p>
113 <ul class="relevanssi_ul">
114 <li><?php esc_html_e( 'PDF content indexing', 'relevanssi' ); ?></li>
115 <li><?php esc_html_e( 'A Related posts feature', 'relevanssi' ); ?></li>
116 <li><?php esc_html_e( 'Index and search user profile pages', 'relevanssi' ); ?></li>
117 <li><?php esc_html_e( 'Index and search taxonomy term pages', 'relevanssi' ); ?></li>
118 <li><?php esc_html_e( 'Multisite searches across many subsites', 'relevanssi' ); ?></li>
119 <li><?php esc_html_e( 'WP CLI commands', 'relevanssi' ); ?></li>
120 <li><?php esc_html_e( 'Adjust weights separately for each post type and taxonomy', 'relevanssi' ); ?></li>
121 <li><?php esc_html_e( 'Internal link anchors can be search terms for the target posts', 'relevanssi' ); ?></li>
122 <li><?php esc_html_e( 'Index and search any columns in the wp_posts database', 'relevanssi' ); ?></li>
123 <li><?php esc_html_e( 'Hide Relevanssi branding from the User Searches page on a client installation', 'relevanssi' ); ?></li>
124 <li><?php esc_html_e( 'Redirect search queries to custom URLs', 'relevanssi' ); ?></li>
125 </ul>
126 </td>
127 </tr>
128 <?php } // End if ( ! RELEVANSSI_PREMIUM ). ?>
129 </table>
130 <?php
131 }
1 <?php
2 /**
3 * /lib/tabs/redirects-tab.php
4 *
5 * Prints out the Redirects tab in Relevanssi settings.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 /**
14 * Prints out the redirects tab in Relevanssi settings.
15 */
16 function relevanssi_redirects_tab() {
17 ?>
18 <h2><?php esc_html_e( 'Redirects', 'relevanssi' ); ?></h2>
19
20 <p><?php esc_html_e( 'With Relevanssi Premium, you can set up redirects. These are keywords that automatically redirect the user to certain page, without going through the usual search process. For example, you could set it up so that all searches for "job" automatically lead to your "Careers" page.', 'relevanssi' ); ?></p>
21 <?php // Translators: %1$s starts the link, %2$s closes it. ?>
22 <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>
23 <?php
24 }
1 <?php
2 /**
3 * /lib/tabs/search-tab.php
4 *
5 * Prints out the search tab in Relevanssi settings.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 /**
14 * Prints out the search tab in Relevanssi settings.
15 */
16 function relevanssi_search_tab() {
17 ?>
18 <p><?php esc_html_e( 'You can use this search to perform Relevanssi searches without any restrictions from WordPress. You can search all post types here.', 'relevanssi' ); ?></p>
19
20 <form action="" method="get">
21 <table class="form-table" role="presentation">
22 <tr>
23 <th scope="row">
24 <label for='s'><?php esc_html_e( 'Search terms', 'relevanssi' ); ?></label>
25 </th>
26 <td>
27 <input type='text' name='s' id='s' size='60' />
28 </td>
29 </tr>
30 <tr>
31 <th scope="row">
32 <label for='post_types'><?php esc_html_e( 'Post type', 'relevanssi' ); ?></label>
33 </th>
34 <td>
35 <select name='post_types' id='post_types'>
36 <option value="any"><?php esc_html_e( 'Any', 'relevanssi' ); ?></option>
37 <?php
38 echo implode(
39 ' ',
40 array_map( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
41 function ( $post_type ) {
42 $pt = get_post_type_object( $post_type );
43 if ( $pt ) {
44 $post_type_value = esc_attr( $post_type );
45 $post_type_name = esc_html( $pt->labels->singular_name );
46 return "<option value='{$post_type_value}'>{$post_type_name}</option>";
47 }
48 return null;
49 },
50 get_option( 'relevanssi_index_post_types', array() )
51 )
52 );
53 if ( 'on' === get_option( 'relevanssi_index_users' ) ) {
54 ?>
55 <option value='user'><?php esc_html_e( 'Users', 'relevanssi' ); ?></option>
56 <?php
57 }
58 ?>
59 </select>
60 </td>
61 </tr>
62 <tr>
63 <th scope="row">
64 <label for='posts_per_page'><?php esc_html_e( 'Posts per page', 'relevanssi' ); ?></label>
65 </th>
66 <td>
67 <select name='posts_per_page' id='posts_per_page'>
68 <option value='0'><?php esc_html_e( 'All', 'relevanssi' ); ?></option>
69 <option>10</option>
70 <option>50</option>
71 <option>100</option>
72 </select>
73 </td>
74 </tr>
75 <tr>
76 <th scope="row">
77 <label for='args'><?php esc_html_e( 'Search parameters', 'relevanssi' ); ?></label>
78 </th>
79 <td>
80 <input type='text' name='args' id='args' size='60' />
81 <?php // Translators: example query string. ?>
82 <p class='description'><?php printf( esc_html__( 'Use query parameter formatting here, the same that would appear on search page results URL. For example %s.', 'relevanssi' ), '<code>posts_per_page=10&post_types=page&from=2018-01-01</code>' ); ?></p>
83 </td>
84 </tr>
85 <tr>
86 <td>
87 </td>
88 <td>
89 <input type='submit' name='search' id='search' value='<?php echo esc_html_x( 'Search', 'button action', 'relevanssi' ); ?>' class='button' />
90 </td>
91 </tr>
92 </table>
93 </form>
94
95 <div id='results'></div>
96 <?php
97 }
1 <?php
2 /**
3 * /lib/tabs/stopwords-tab.php
4 *
5 * Prints out the Stopwords tab in Relevanssi settings.
6 *
7 * @package Relevanssi
8 * @author Mikko Saari
9 * @license https://wordpress.org/about/gpl/ GNU General Public License
10 * @see https://www.relevanssi.com/
11 */
12
13 /**
14 * Prints out the stopwords tab in Relevanssi settings.
15 */
16 function relevanssi_stopwords_tab() {
17 if ( class_exists( 'Polylang', false ) && ! relevanssi_get_current_language() ) {
18 relevanssi_polylang_all_languages_stopwords();
19 return;
20 }
21 ?>
22 <h3 id="stopwords"><?php esc_html_e( 'Stopwords', 'relevanssi' ); ?></h3>
23 <?php
24
25 relevanssi_show_stopwords();
26
27 ?>
28
29 <h3 id="bodystopwords"><?php esc_html_e( 'Content stopwords', 'relevanssi' ); ?></h3>
30
31 <?php
32 if ( function_exists( 'relevanssi_show_body_stopwords' ) ) {
33 relevanssi_show_body_stopwords();
34 } else {
35 printf(
36 '<p>%s</p>',
37 esc_html__(
38 'Content stopwords are a premium feature where you can set stopwords that only apply to the post content. Those stopwords will still be indexed if they appear in post titles, tags, categories, custom fields or other parts of the post. To use content stopwords, you need Relevanssi Premium.',
39 'relevanssi'
40 )
41 );
42 }
43
44 /**
45 * Filters whether the common words list is displayed or not.
46 *
47 * The list of 25 most common words is displayed by default, but if the
48 * index is big, displaying the list can take a long time. This filter can
49 * be used to turn the list off.
50 *
51 * @param boolean If true, show the list; if false, don't show it.
52 */
53 if ( apply_filters( 'relevanssi_display_common_words', true ) ) {
54 relevanssi_common_words( 25 );
55 }
56 }
57
58 /**
59 * Displays a list of stopwords.
60 *
61 * Displays the list of stopwords and gives the controls for adding new
62 * stopwords.
63 */
64 function relevanssi_show_stopwords() {
65 printf(
66 '<p>%s</p>',
67 esc_html__(
68 '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.',
69 'relevanssi'
70 )
71 );
72 ?>
73 <table class="form-table" role="presentation">
74 <tr>
75 <th scope="row">
76 <label for="addstopword"><p><?php esc_html_e( 'Stopword(s) to add', 'relevanssi' ); ?>
77 </th>
78 <td>
79 <textarea name="addstopword" id="addstopword" rows="2" cols="80"></textarea>
80 <p><input type="submit" value="<?php esc_attr_e( 'Add', 'relevanssi' ); ?>" class='button' /></p>
81 </td>
82 </tr>
83 </table>
84 <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>
85
86 <table class="form-table" role="presentation">
87 <tr>
88 <th scope="row">
89 <?php esc_html_e( 'Current stopwords', 'relevanssi' ); ?>
90 </th>
91 <td>
92 <ul>
93 <?php
94 $stopwords = array_map( 'stripslashes', relevanssi_fetch_stopwords() );
95 sort( $stopwords );
96 $exportlist = htmlspecialchars( implode( ', ', $stopwords ) );
97 array_walk(
98 $stopwords,
99 function ( $term ) {
100 printf( '<li style="display: inline;"><input type="submit" name="removestopword" value="%s"/></li>', esc_attr( $term ) );
101 }
102 );
103
104 ?>
105 </ul>
106 <p>
107 <input
108 type="submit"
109 id="removeallstopwords"
110 name="removeallstopwords"
111 value="<?php esc_attr_e( 'Remove all stopwords', 'relevanssi' ); ?>"
112 class='button'
113 />
114 <input
115 type="submit"
116 id="repopulatestopwords"
117 name="repopulatestopwords"
118 value="<?php esc_attr_e( 'Add default stopwords', 'relevanssi' ); ?>"
119 class='button'
120 />
121 </p>
122 </td>
123 </tr>
124 <tr>
125 <th scope="row">
126 <?php esc_html_e( 'Exportable list of stopwords', 'relevanssi' ); ?>
127 </th>
128 <td>
129 <label for="stopwords" class="screen-reader-text"><?php esc_html_e( 'Exportable list of stopwords', 'relevanssi' ); ?></label>
130 <textarea name="stopwords" id="stopwords" rows="2" cols="80"><?php echo esc_textarea( $exportlist ); ?></textarea>
131 <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>
132 </td>
133 </tr>
134 </table>
135
136 <?php
137 }
138
139 /**
140 * Displays an error message when Polylang is in all languages mode.
141 */
142 function relevanssi_polylang_all_languages_stopwords() {
143 ?>
144 <h3 id="stopwords"><?php esc_html_e( 'Stopwords', 'relevanssi' ); ?></h3>
145
146 <p class="description"><?php esc_html_e( 'You are using Polylang and are in "Show all languages" mode. Please select a language before adjusting the stopword settings.', 'relevanssi' ); ?></p>
147 <?php
148 }
149