admin-ajax.php
14.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
<?php
/**
* /lib/admin-ajax.php
*
* @package Relevanssi
* @author Mikko Saari
* @license https://wordpress.org/about/gpl/ GNU General Public License
* @see https://www.relevanssi.com/
*/
add_action( 'wp_ajax_relevanssi_truncate_index', 'relevanssi_truncate_index_ajax_wrapper' );
add_action( 'wp_ajax_relevanssi_index_posts', 'relevanssi_index_posts_ajax_wrapper' );
add_action( 'wp_ajax_relevanssi_count_posts', 'relevanssi_count_posts_ajax_wrapper' );
add_action( 'wp_ajax_relevanssi_count_missing_posts', 'relevanssi_count_missing_posts_ajax_wrapper' );
add_action( 'wp_ajax_relevanssi_list_categories', 'relevanssi_list_categories' );
add_action( 'wp_ajax_relevanssi_admin_search', 'relevanssi_admin_search' );
add_action( 'wp_ajax_relevanssi_update_counts', 'relevanssi_update_counts' );
add_action( 'wp_ajax_nopriv_relevanssi_update_counts', 'relevanssi_update_counts' );
add_action( 'wp_ajax_relevanssi_list_custom_fields', 'relevanssi_list_custom_fields' );
/**
* Checks if current user can access Relevanssi options.
*
* If the current user doesn't have sufficient access to Relevanssi options,
* the function will die. If the user has access, nothing happens.
*
* @return void
*/
function relevanssi_current_user_can_access_options() {
/**
* Filters the capability required to access Relevanssi options.
*
* @param string The capability required. Default 'manage_options'.
*/
if ( ! current_user_can( apply_filters( 'relevanssi_options_capability', 'manage_options' ) ) ) {
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'relevanssi' ) );
}
}
/**
* Truncates the Relevanssi index.
*
* Wipes the index clean using relevanssi_truncate_index().
*/
function relevanssi_truncate_index_ajax_wrapper() {
check_ajax_referer( 'relevanssi_indexing_nonce', 'security' );
relevanssi_current_user_can_access_options();
$response = relevanssi_truncate_index();
echo wp_json_encode( $response );
wp_die();
}
/**
* Indexes posts in AJAX context.
*
* AJAX wrapper for indexing posts. Parses the arguments, uses the
* relevanssi_build_index() to do the hard work, then creates the AJAX response.
*/
function relevanssi_index_posts_ajax_wrapper() {
check_ajax_referer( 'relevanssi_indexing_nonce', 'security' );
relevanssi_current_user_can_access_options();
$completed = absint( $_POST['completed'] );
$total = absint( $_POST['total'] );
$offset = absint( $_POST['offset'] );
$limit = absint( $_POST['limit'] );
$extend = strval( $_POST['extend'] );
if ( 'true' === $extend ) {
$extend = true;
}
if ( $limit < 1 ) {
$limit = 1;
}
$response = array();
$is_ajax = true;
$verbose = false;
if ( $extend ) {
$offset = true;
}
$indexing_response = relevanssi_build_index( $offset, $verbose, $limit, $is_ajax );
if ( $indexing_response['indexing_complete'] ) {
$response['completed'] = 'done';
$response['percentage'] = 100;
$completed += $indexing_response['indexed'];
$response['total_posts'] = $completed;
$processed = $total;
} else {
$completed += $indexing_response['indexed'];
$response['completed'] = $completed;
if ( true === $offset ) {
$processed = $completed;
} else {
$offset = $offset + $limit;
$processed = $offset;
}
if ( $total > 0 ) {
$response['percentage'] = $processed / $total * 100;
} else {
$response['percentage'] = 0;
}
}
$response['feedback'] = sprintf(
// translators: Number of posts indexed on this go, total number of posts indexed so far, number of posts processed on this go, total number of posts to process.
_n(
'Indexed %1$d post (total %2$d), processed %3$d / %4$d.',
'Indexed %1$d posts (total %2$d), processed %3$d / %4$d.',
$indexing_response['indexed'],
'relevanssi'
),
$indexing_response['indexed'],
$completed,
$processed,
$total
) . "\n";
$response['offset'] = $offset;
echo wp_json_encode( $response );
wp_die();
}
/**
* Counts the posts to index.
*
* AJAX wrapper for relevanssi_count_total_posts().
*/
function relevanssi_count_posts_ajax_wrapper() {
relevanssi_current_user_can_access_options();
$count = relevanssi_count_total_posts();
echo wp_json_encode( $count );
wp_die();
}
/**
* Counts the posts missing from the index.
*
* AJAX wrapper for relevanssi_count_missing_posts().
*/
function relevanssi_count_missing_posts_ajax_wrapper() {
relevanssi_current_user_can_access_options();
$count = relevanssi_count_missing_posts();
echo wp_json_encode( $count );
wp_die();
}
/**
* Lists categories.
*
* AJAX wrapper for get_categories().
*/
function relevanssi_list_categories() {
relevanssi_current_user_can_access_options();
$categories = get_categories(
array(
'taxonomy' => 'category',
'hide_empty' => false,
)
);
echo wp_json_encode( $categories );
wp_die();
}
/**
* Performs an admin search.
*
* Performs an admin dashboard search.
*
* @since 2.2.0
*/
function relevanssi_admin_search() {
check_ajax_referer( 'relevanssi_admin_search_nonce', 'security' );
/**
* Filters the capability required to access Relevanssi admin search page.
*
* @param string The capability required. Default 'edit_posts'.
*/
if ( ! current_user_can( apply_filters( 'relevanssi_admin_search_capability', 'edit_posts' ) ) ) {
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'relevanssi' ) );
}
$args = array();
if ( isset( $_POST['args'] ) ) {
parse_str( $_POST['args'], $args );
}
if ( isset( $_POST['posts_per_page'] ) ) {
$posts_per_page = intval( $_POST['posts_per_page'] );
if ( $posts_per_page > 0 ) {
$args['posts_per_page'] = $posts_per_page;
}
}
if ( isset( $_POST['post_types'] ) ) {
$post_type = $_POST['post_types'];
$args['post_types'] = $post_type;
}
if ( isset( $_POST['offset'] ) ) {
$offset = intval( $_POST['offset'] );
if ( $offset > 0 ) {
$args['offset'] = $offset;
}
}
if ( isset( $_POST['s'] ) ) {
$args['s'] = $_POST['s'];
}
$query = new WP_Query();
$query->parse_query( $args );
$query->set( 'relevanssi_admin_search', true );
$query = apply_filters( 'relevanssi_modify_wp_query', $query );
relevanssi_do_query( $query );
$results = relevanssi_admin_search_debugging_info( $query );
// Take the posts array and create a string out of it.
$offset = 0;
if ( isset( $query->query_vars['offset'] ) ) {
$offset = $query->query_vars['offset'];
}
$results .= relevanssi_admin_search_format_posts( $query->posts, $query->found_posts, $offset, $args['s'] );
echo wp_json_encode( $results );
wp_die();
}
/**
* Formats the posts for admin search.
*
* Results are presented as an ordered list of posts. The format is very basic, and
* can be modified with the 'relevanssi_admin_search_element' filter hook.
*
* @param array $posts The posts array.
* @param int $total The number of posts found in total.
* @param int $offset Offset value.
* @param string $query The search query.
*
* @return string The formatted posts.
*
* @since 2.2.0
*/
function relevanssi_admin_search_format_posts( $posts, $total, $offset, $query ) {
$result = '<h3>' . __( 'Results', 'relevanssi' ) . '</h3>';
$start = $offset + 1;
$end = $offset + count( $posts );
// Translators: %1$d is the total number of posts found, %2$d is the current search result count, %3$d is the offset.
$result .= '<p>' . sprintf( __( 'Found a total of %1$d posts, showing posts %2$d–%3$s.', 'relevanssi' ), $total, $start, '<span id="offset">' . $end . '</span>' ) . '</p>';
if ( $offset > 0 ) {
$result .= sprintf( '<button type="button" id="prev_page">%s</button>', __( 'Previous page', 'relevanssi' ) );
}
if ( count( $posts ) + $offset < $total ) {
$result .= sprintf( '<button type="button" id="next_page">%s</button>', __( 'Next page', 'relevanssi' ) );
}
$result .= '<ol start="' . $start . '">';
$score_label = __( 'Score:', 'relevanssi' );
foreach ( $posts as $post ) {
$blog_name = '';
if ( isset( $post->blog_id ) && function_exists( 'switch_to_blog' ) ) {
switch_to_blog( $post->blog_id );
$blog_name = get_bloginfo( 'name' ) . ': ';
}
$permalink = get_permalink( $post->ID );
$edit_url = get_edit_post_link( $post->ID );
$post_type = $post->post_type;
if ( isset( $post->relevanssi_link ) ) {
$permalink = $post->relevanssi_link;
}
if ( 'user' === $post->post_type ) {
$edit_url = get_edit_user_link( $post->ID );
}
if ( empty( $edit_url ) ) {
if ( isset( $post->term_id ) ) {
$edit_url = get_edit_term_link( $post->term_id, $post->post_type );
}
}
$title = sprintf( '<a href="%1$s">%2$s %3$s</a>', $permalink, $post->post_title, $post_type );
$edit_link = '';
if ( current_user_can( 'edit_post', $post->ID ) ) {
$edit_link = sprintf( '(<a href="%1$s">%2$s %3$s</a>)', $edit_url, __( 'Edit', 'relevanssi' ), $post_type );
}
$pinning_buttons = '';
$pinned = '';
if ( function_exists( 'relevanssi_admin_search_pinning' ) ) {
// Relevanssi Premium adds pinning features to the admin search.
list( $pinning_buttons, $pinned ) = relevanssi_admin_search_pinning( $post, $query );
}
$post_element = <<<EOH
<li>$blog_name <strong>$title</strong> $edit_link $pinning_buttons <br />
$post->post_excerpt<br />
$score_label $post->relevance_score $pinned</li>
EOH;
/**
* Filters the admin search results element.
*
* The post element is a <li> element. Feel free to edit the element any
* way you want to.
*
* @param string $post_element The post element.
* @param object $post The post object.
*/
$result .= apply_filters( 'relevanssi_admin_search_element', $post_element, $post );
if ( isset( $post->blog_id ) && function_exists( 'restore_current_blog' ) ) {
restore_current_blog();
}
}
$result .= '</ol>';
return $result;
}
/**
* Shows debugging information about the search.
*
* Formats the WP_Query parameters, looks at some filter hooks and presents the
* information in an easy-to-read format.
*
* @param WP_Query $query The WP_Query object.
*
* @return string The formatted debugging information.
*
* @since 2.2.0
*/
function relevanssi_admin_search_debugging_info( $query ) {
$result = '<div id="debugging">';
$result .= '<h3>' . __( 'Query variables', 'relevanssi' ) . '</h3>';
$result .= '<ul style="list-style: disc; margin-left: 1.5em">';
foreach ( $query->query_vars as $key => $value ) {
if ( 'tax_query' === $key ) {
$result .= '<li>tax_query:<ul style="list-style: disc; margin-left: 1.5em">';
$result .= implode(
'',
array_map(
function ( $row ) {
$result = '';
if ( is_array( $row ) ) {
foreach ( $row as $row_key => $row_value ) {
$result .= "<li>$row_key: $row_value</li>";
}
}
return $result;
},
$value
)
);
$result .= '</ul></li>';
} else {
if ( is_array( $value ) ) {
$value = relevanssi_flatten_array( $value );
}
if ( empty( $value ) ) {
continue;
}
$result .= "<li>$key: $value</li>";
}
}
if ( ! empty( $query->tax_query ) ) {
$result .= '<li>tax_query:<ul style="list-style: disc; margin-left: 1.5em">';
foreach ( $query->tax_query as $tax_query ) {
if ( ! is_array( $tax_query ) ) {
continue;
}
foreach ( $tax_query as $key => $value ) {
if ( is_array( $value ) ) {
$value = relevanssi_flatten_array( $value );
}
$result .= "<li>$key: $value</li>";
}
}
$result .= '</ul></li>';
}
$result .= '</ul>';
global $wp_filter;
$filters = array(
'relevanssi_search_ok',
'relevanssi_modify_wp_query',
'relevanssi_search_filters',
'relevanssi_where',
'relevanssi_join',
'relevanssi_fuzzy_query',
'relevanssi_exact_match_bonus',
'relevanssi_query_filter',
'relevanssi_match',
'relevanssi_post_ok',
'relevanssi_search_again',
'relevanssi_results',
'relevanssi_orderby',
'relevanssi_order',
'relevanssi_default_tax_query_relation',
'relevanssi_hits_filter',
);
$result .= '<h3>' . __( 'Filters', 'relevanssi' ) . '</h3>';
$result .= '<button type="button" id="show_filters">' . __( 'show', 'relevanssi' ) . '</button>';
$result .= '<button type="button" id="hide_filters" style="display: none">' . __( 'hide', 'relevanssi' ) . '</button>';
$result .= '<div id="relevanssi_filter_list">';
foreach ( $filters as $filter ) {
if ( isset( $wp_filter[ $filter ] ) ) {
$result .= '<h4>' . $filter . '</h4>';
$result .= '<ul style="list-style: disc; margin-left: 1.5em">';
foreach ( $wp_filter[ $filter ] as $priority => $functions ) {
foreach ( $functions as $function ) {
if ( $function['function'] instanceof Closure ) {
$function['function'] = 'Anonymous function';
}
$result .= "<li>$priority: " . $function['function'] . '</li>';
}
}
$result .= '</ul>';
}
}
$result .= '</div>';
$result .= '</div>';
return $result;
}
/**
* Updates count options.
*
* Updates 'relevanssi_doc_count', 'relevanssi_terms_count' (and in Premium
* 'relevanssi_user_count' and 'relevanssi_taxterm_count'). These are slightly
* expensive queries, so they are updated when necessary as a non-blocking AJAX
* action and stored in options for quick retrieval.
*
* @global object $wpdb The WordPress database interface.
* @global array $relevanssi_variables The Relevanssi global variable, used for table names.
*/
function relevanssi_update_counts() {
global $wpdb, $relevanssi_variables;
relevanssi_update_doc_count();
$terms_count = $wpdb->get_var( 'SELECT COUNT(*) FROM ' . $relevanssi_variables['relevanssi_table'] ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
update_option( 'relevanssi_terms_count', is_null( $terms_count ) ? 0 : $terms_count, false );
if ( RELEVANSSI_PREMIUM ) {
$user_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT item) FROM ' . $relevanssi_variables['relevanssi_table'] . " WHERE type = 'user'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
$taxterm_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT item) FROM ' . $relevanssi_variables['relevanssi_table'] . " WHERE (type != 'post' AND type != 'attachment' AND type != 'user')" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
update_option( 'relevanssi_user_count', is_null( $user_count ) ? 0 : $user_count, false );
update_option( 'relevanssi_taxterm_count', is_null( $taxterm_count ) ? 0 : $taxterm_count, false );
}
}
/**
* Returns a comma-separated list of indexed custom field names.
*
* @uses relevanssi_list_all_indexed_custom_fields()
*/
function relevanssi_list_custom_fields() {
$response = relevanssi_list_all_indexed_custom_fields();
echo wp_json_encode( $response );
wp_die();
}