PluginProbe
ElasticPress / 5.3.5
ElasticPress v5.3.5
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
← All changes | includes/dashboard.php +299 -254 4.3.15.3.5 View file →
@@ -7,16 +7,15 @@
7 7 */
8 8
9 9 namespace ElasticPress\Dashboard;
10 10
11 -use ElasticPress\Utils as Utils;
11 +use ElasticPress\AdminNotices;
12 12 use ElasticPress\Elasticsearch;
13 13 use ElasticPress\Features;
14 -use ElasticPress\Indexables;
15 14 use ElasticPress\Installer;
16 -use ElasticPress\AdminNotices;
17 15 use ElasticPress\Screen;
18 16 use ElasticPress\Stats;
17 +use ElasticPress\Utils;
19 18
20 19 if ( ! defined( 'ABSPATH' ) ) {
21 20 exit; // Exit if accessed directly.
22 21 }
@@ -29,15 +28,13 @@
29 28 function setup() {
30 29 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { // Must be network admin in multisite.
31 30 add_action( 'network_admin_menu', __NAMESPACE__ . '\action_admin_menu' );
32 31 add_action( 'admin_bar_menu', __NAMESPACE__ . '\action_network_admin_bar_menu', 50 );
33 - } else {
34 - add_action( 'admin_menu', __NAMESPACE__ . '\action_admin_menu' );
35 32 }
36 33
34 + add_action( 'admin_menu', __NAMESPACE__ . '\action_admin_menu' );
37 35 add_action( 'wp_ajax_ep_save_feature', __NAMESPACE__ . '\action_wp_ajax_ep_save_feature' );
38 36 add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\action_admin_enqueue_dashboard_scripts' );
39 - add_action( 'admin_init', __NAMESPACE__ . '\action_admin_init' );
40 37 add_action( 'admin_init', __NAMESPACE__ . '\maybe_clear_es_info_cache' );
41 38 add_action( 'admin_init', __NAMESPACE__ . '\maybe_skip_install' );
42 39 add_action( 'wp_ajax_ep_notice_dismiss', __NAMESPACE__ . '\action_wp_ajax_ep_notice_dismiss' );
43 40 add_action( 'admin_notices', __NAMESPACE__ . '\maybe_notice' );
@@ -46,11 +43,17 @@
46 43 add_filter( 'network_admin_plugin_action_links', __NAMESPACE__ . '\filter_plugin_action_links', 10, 2 );
47 44 add_action( 'ep_add_query_log', __NAMESPACE__ . '\log_version_query_error' );
48 45 add_filter( 'ep_analyzer_language', __NAMESPACE__ . '\use_language_in_setting', 10, 2 );
49 46 add_filter( 'wp_kses_allowed_html', __NAMESPACE__ . '\filter_allowed_html', 10, 2 );
50 - add_action( 'manage_blogs_custom_column', __NAMESPACE__ . '\add_blogs_column', 10, 2 );
51 - add_action( 'rest_api_init', __NAMESPACE__ . '\setup_endpoint' );
47 + add_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\block_assets' );
48 + add_action( 'init', __NAMESPACE__ . '\register_react_jsx_runtime', 1 );
52 49
50 + if ( version_compare( get_bloginfo( 'version' ), '5.8', '>=' ) ) {
51 + add_action( 'block_categories_all', __NAMESPACE__ . '\block_categories' );
52 + } else {
53 + add_action( 'block_categories', __NAMESPACE__ . '\block_categories' );
54 + }
55 +
53 56 /**
54 57 * Filter whether to show 'ElasticPress Indexing' option on Multisite in admin UI or not.
55 58 *
56 59 * @since 3.6.0
@@ -57,9 +60,9 @@
57 60 * @hook ep_show_indexing_option_on_multisite
58 61 * @param {bool} $show True to show.
59 62 * @return {bool} New value
60 63 */
61 - $show_indexing_option_on_multisite = apply_filters( 'ep_show_indexing_option_on_multisite', true );
64 + $show_indexing_option_on_multisite = apply_filters( 'ep_show_indexing_option_on_multisite', defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK );
62 65
63 66 if ( $show_indexing_option_on_multisite ) {
64 67 add_filter( 'wpmu_blogs_columns', __NAMESPACE__ . '\filter_blogs_columns', 10, 1 );
65 68 add_action( 'manage_sites_custom_column', __NAMESPACE__ . '\add_blogs_column', 10, 2 );
@@ -67,8 +70,29 @@
67 70 }
68 71 }
69 72
70 73 /**
74 + * Register the `react-jsx-runtime` script handle on WordPress versions that
75 + * do not ship it. WordPress added it in 6.6; 10up-toolkit emits it as a
76 + * dependency of the React admin and Instant Results bundles, and without the
77 + * handle those scripts never print on WordPress 6.2.
78 + *
79 + * @since 5.3.4
80 + * @return void
81 + */
82 +function register_react_jsx_runtime() {
83 + if ( wp_script_is( 'react-jsx-runtime', 'registered' ) ) {
84 + return;
85 + }
86 +
87 + wp_register_script( 'react-jsx-runtime', false, [ 'react' ], '18', true );
88 + wp_add_inline_script(
89 + 'react-jsx-runtime',
90 + 'window.ReactJSXRuntime=window.ReactJSXRuntime||{Fragment:window.React.Fragment,jsx:function(type,config,maybeKey){var props=Object.assign({},config);if(maybeKey!==undefined){props.key=maybeKey;}return window.React.createElement(type,props);},jsxs:function(type,config,maybeKey){var props=Object.assign({},config);if(maybeKey!==undefined){props.key=maybeKey;}return window.React.createElement(type,props);}};'
91 + );
92 +}
93 +
94 +/**
71 95 * Add ep-html kses context
72 96 *
73 97 * @param array $allowedtags HTML tags
74 98 * @param string $context Context string
@@ -114,9 +138,12 @@
114 138 'name' => true,
115 139 'target' => true,
116 140 ];
117 141
118 - $ep_tags['a'] = $atts;
142 + $ep_tags['a'] = array_merge(
143 + $atts,
144 + [ 'target' => true ]
145 + );
119 146
120 147 return $ep_tags;
121 148 }
122 149
@@ -129,17 +156,16 @@
129 156 * @param array $query The version query.
130 157 * @since 3.0
131 158 */
132 159 function log_version_query_error( $query ) {
133 - $is_network = defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK;
160 + // Ignore fake requests like the autosuggest template generation
161 + if ( ! empty( $query['request'] ) && is_array( $query['request'] ) && ! empty( $query['request']['is_ep_fake_request'] ) ) {
162 + return;
163 + }
134 164
135 165 $logging_key = 'logging_ep_es_info';
136 166
137 - if ( $is_network ) {
138 - $logging = get_site_transient( $logging_key );
139 - } else {
140 - $logging = get_transient( $logging_key );
141 - }
167 + $logging = Utils\get_transient( $logging_key );
142 168
143 169 // Are we logging the version query results?
144 170 if ( '1' === $logging ) {
145 171 /**
@@ -166,17 +192,11 @@
166 192
167 193 // Store the response code, and remove the flag that says
168 194 // we're logging the response code so we don't log additional
169 195 // queries.
170 - if ( $is_network ) {
171 - set_site_transient( $response_code_key, $response_code, $cache_time );
172 - set_site_transient( $response_error_key, $response_error, $cache_time );
173 - delete_site_transient( $logging_key );
174 - } else {
175 - set_transient( $response_code_key, $response_code, $cache_time );
176 - set_transient( $response_error_key, $response_error, $cache_time );
177 - delete_transient( $logging_key );
178 - }
196 + Utils\set_transient( $response_code_key, $response_code, $cache_time );
197 + Utils\set_transient( $response_error_key, $response_error, $cache_time );
198 + Utils\delete_transient( $logging_key );
179 199 }
180 200 }
181 201
182 202 /**
@@ -188,9 +208,9 @@
188 208 if ( ! is_admin() && ! is_network_admin() ) {
189 209 return;
190 210 }
191 211
192 - if ( empty( $_GET['ep-skip-install'] ) || empty( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], 'ep-skip-install' ) || ! in_array( Screen::factory()->get_current_screen(), [ 'install' ], true ) ) { // phpcs:ignore WordPress.Security.NonceVerification
212 + if ( empty( $_GET['ep-skip-install'] ) || empty( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['nonce'] ), 'ep-skip-install' ) || ! in_array( Screen::factory()->get_current_screen(), [ 'install' ], true ) ) {
193 213 return;
194 214 }
195 215
196 216 if ( ! empty( $_GET['ep-skip-features'] ) ) {
@@ -202,13 +222,12 @@
202 222 }
203 223
204 224 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
205 225 $redirect_url = network_admin_url( 'admin.php?page=elasticpress' );
206 - update_site_option( 'ep_skip_install', true );
207 226 } else {
208 227 $redirect_url = admin_url( 'admin.php?page=elasticpress' );
209 - update_option( 'ep_skip_install', true );
210 228 }
229 + Utils\update_option( 'ep_skip_install', true );
211 230
212 231 wp_safe_redirect( $redirect_url );
213 232 exit;
214 233 }
@@ -223,9 +242,13 @@
223 242 if ( ! is_admin() && ! is_network_admin() ) {
224 243 return;
225 244 }
226 245
227 - if ( empty( $_GET['ep-retry'] ) && ! in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'settings', 'install' ], true ) ) { // phpcs:ignore WordPress.Security.NonceVerification
246 + $isset_retry = ! empty( $_GET['ep-retry'] ) &&
247 + ! empty( $_GET['ep_retry_nonce'] ) &&
248 + wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['ep_retry_nonce'] ) ), 'ep_retry_nonce' );
249 +
250 + if ( ! $isset_retry && ! in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'settings', 'install' ], true ) ) {
228 251 return;
229 252 }
230 253
231 254 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
@@ -233,10 +256,11 @@
233 256 } else {
234 257 delete_transient( 'ep_es_info' );
235 258 }
236 259
237 - if ( ! empty( $_GET['ep-retry'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
238 - wp_safe_redirect( remove_query_arg( 'ep-retry' ) );
260 + if ( $isset_retry ) {
261 + wp_safe_redirect( remove_query_arg( [ 'ep-retry', 'ep_retry_nonce' ] ) );
262 + exit();
239 263 }
240 264 }
241 265
242 266 /**
@@ -295,30 +319,12 @@
295 319 * @param bool $force Force ES info hard lookup.
296 320 * @since 3.0
297 321 */
298 322 function maybe_notice( $force = false ) {
299 - // Admins only.
300 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
301 - if ( ! is_super_admin() ) {
302 - return false;
303 - }
304 - } else {
305 - if ( ! current_user_can( 'manage_options' ) ) {
306 - return false;
307 - }
323 + if ( ! current_user_can( Utils\get_capability() ) ) {
324 + return false;
308 325 }
309 326
310 - // If in network mode, don't output notice in admin and vice-versa.
311 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
312 - if ( ! is_network_admin() ) {
313 - return false;
314 - }
315 - } else {
316 - if ( is_network_admin() ) {
317 - return false;
318 - }
319 - }
320 -
321 327 /**
322 328 * Filter how long results of Elasticsearch version query are stored
323 329 *
324 330 * @since 23.0
@@ -327,21 +333,13 @@
327 333 * @return {int} New time in seconds
328 334 */
329 335 $cache_time = apply_filters( 'ep_es_info_cache_expiration', ( 5 * MINUTE_IN_SECONDS ) );
330 336
331 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
332 - set_site_transient(
333 - 'logging_ep_es_info',
334 - '1',
335 - $cache_time
336 - );
337 - } else {
338 - $a = set_transient(
339 - 'logging_ep_es_info',
340 - '1',
341 - $cache_time
342 - );
343 - }
337 + Utils\set_transient(
338 + 'logging_ep_es_info',
339 + '1',
340 + $cache_time
341 + );
344 342
345 343 // Fetch ES version
346 344 Elasticsearch::factory()->get_elasticsearch_version( $force );
347 345
@@ -349,13 +347,14 @@
349 347
350 348 $notices = AdminNotices::factory()->get_notices();
351 349
352 350 foreach ( $notices as $notice_key => $notice ) {
351 + $class = 'notice notice-' . $notice['type'];
352 + if ( $notice['dismiss'] ) {
353 + $class .= ' is-dismissible';
354 + }
353 355 ?>
354 - <div data-ep-notice="<?php echo esc_attr( $notice_key ); ?>" class="notice notice-<?php echo esc_attr( $notice['type'] ); ?> <?php
355 - if ( $notice['dismiss'] ) :
356 - ?>
357 - is-dismissible<?php endif; ?>">
356 + <div data-ep-notice="<?php echo esc_attr( $notice_key ); ?>" class="<?php echo esc_attr( $class ); ?>">
358 357 <p>
359 358 <?php echo wp_kses( $notice['html'], 'ep-html' ); ?>
360 359 </p>
361 360 </div>
@@ -377,14 +376,14 @@
377 376 wp_send_json_error();
378 377 exit;
379 378 }
380 379
381 - if ( ! current_user_can( 'manage_options' ) ) {
380 + if ( ! current_user_can( Utils\get_capability() ) ) {
382 381 wp_send_json_error();
383 382 exit;
384 383 }
385 384
386 - AdminNotices::factory()->dismiss_notice( $_POST['notice'] );
385 + AdminNotices::factory()->dismiss_notice( sanitize_key( $_POST['notice'] ) );
387 386
388 387 wp_send_json_success();
389 388 }
390 389
@@ -420,11 +419,11 @@
420 419 *
421 420 * @since 2.2
422 421 */
423 422 function action_wp_ajax_ep_save_feature() {
424 - $_POST = wp_unslash( $_POST );
423 + $post = wp_unslash( $_POST );
425 424
426 - if ( empty( $_POST['feature'] ) || empty( $_POST['settings'] ) || ! check_ajax_referer( 'ep_dashboard_nonce', 'nonce', false ) ) {
425 + if ( empty( $post['feature'] ) || empty( $post['settings'] ) || ! check_ajax_referer( 'ep_dashboard_nonce', 'nonce', false ) ) {
427 426 wp_send_json_error();
428 427 exit;
429 428 }
430 429
@@ -434,17 +433,13 @@
434 433 wp_send_json_error( $error );
435 434 exit;
436 435 }
437 436
438 - $data = Features::factory()->update_feature( $_POST['feature'], $_POST['settings'] );
437 + $data = Features::factory()->update_feature( $post['feature'], $post['settings'] );
439 438
440 439 // Since we deactivated, delete auto activate notice.
441 - if ( empty( $_POST['settings']['active'] ) ) {
442 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
443 - delete_site_option( 'ep_feature_auto_activated_sync' );
444 - } else {
445 - delete_option( 'ep_feature_auto_activated_sync' );
446 - }
440 + if ( empty( $post['settings']['active'] ) ) {
441 + Utils\delete_option( 'ep_feature_auto_activated_sync' );
447 442 }
448 443
449 444 wp_send_json_success( $data );
450 445 }
@@ -459,14 +454,16 @@
459 454 wp_enqueue_style( 'wp-components' );
460 455
461 456 wp_enqueue_script(
462 457 'ep_admin_sites_scripts',
463 - EP_URL . 'dist/js/sites-admin-script.min.js',
458 + EP_URL . 'dist/js/sites-admin-script.js',
464 459 Utils\get_asset_info( 'sites-admin-script', 'dependencies' ),
465 460 Utils\get_asset_info( 'sites-admin-script', 'version' ),
466 461 true
467 462 );
468 463
464 + wp_set_script_translations( 'ep_admin_sites_scripts', 'elasticpress' );
465 +
469 466 $data = [
470 467 'ajax_url' => admin_url( 'admin-ajax.php' ),
471 468 'nonce' => wp_create_nonce( 'epsa' ),
472 469 ];
@@ -473,47 +470,49 @@
473 470
474 471 wp_localize_script( 'ep_admin_sites_scripts', 'epsa', $data );
475 472 }
476 473
477 - if ( in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'settings', 'install', 'health', 'weighting', 'synonyms', 'sync' ], true ) ) {
474 + $general_ep_screens = [ 'dashboard', 'settings', 'install', 'health', 'weighting', 'synonyms', 'sync', 'status-report' ];
475 + /**
476 + * Filter the query logger object
477 + *
478 + * @since 5.3.3
479 + * @hook elasticpress_general_ep_screens
480 + * @param {array} $general_ep_screens The general ElasticPress screens.
481 + * @return {array} The filtered general ElasticPress screens.
482 + */
483 + $general_ep_screens = apply_filters( 'elasticpress_general_ep_screens', $general_ep_screens );
484 + if ( in_array( Screen::factory()->get_current_screen(), $general_ep_screens, true ) ) {
478 485 wp_enqueue_style(
479 486 'ep_admin_styles',
480 - EP_URL . 'dist/css/dashboard-styles.min.css',
487 + EP_URL . 'dist/css/dashboard-styles.css',
481 488 Utils\get_asset_info( 'dashboard-styles', 'dependencies' ),
482 489 Utils\get_asset_info( 'dashboard-styles', 'version' )
483 490 );
484 491 wp_enqueue_script(
485 492 'ep_admin_script',
486 - EP_URL . 'dist/js/admin-script.min.js',
493 + EP_URL . 'dist/js/admin-script.js',
487 494 Utils\get_asset_info( 'admin-script', 'dependencies' ),
488 495 Utils\get_asset_info( 'admin-script', 'version' ),
489 496 true
490 497 );
491 - }
492 498
493 - if ( in_array( Screen::factory()->get_current_screen(), [ 'weighting', 'install' ], true ) ) {
494 - wp_enqueue_script(
495 - 'ep_weighting_script',
496 - EP_URL . 'dist/js/weighting-script.min.js',
497 - Utils\get_asset_info( 'weighting-script', 'dependencies' ),
498 - Utils\get_asset_info( 'weighting-script', 'version' ),
499 - true
500 - );
499 + wp_set_script_translations( 'ep_admin_script', 'elasticpress' );
501 500 }
502 501
503 502 if ( in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'install' ], true ) ) {
504 503 wp_enqueue_script(
505 504 'ep_dashboard_scripts',
506 - EP_URL . 'dist/js/dashboard-script.min.js',
505 + EP_URL . 'dist/js/dashboard-script.js',
507 506 Utils\get_asset_info( 'dashboard-script', 'dependencies' ),
508 507 Utils\get_asset_info( 'dashboard-script', 'version' ),
509 508 true
510 509 );
511 510
512 - $sync_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ?
513 - network_admin_url( 'admin.php?page=elasticpress-sync&do_sync' ) :
514 - admin_url( 'admin.php?page=elasticpress-sync&do_sync' );
511 + wp_set_script_translations( 'ep_dashboard_scripts', 'elasticpress' );
515 512
513 + $sync_url = Utils\get_sync_url( true );
514 +
516 515 $skip_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ?
517 516 network_admin_url( 'admin.php?page=elasticpress' ) :
518 517 admin_url( 'admin.php?page=elasticpress' );
519 518
@@ -531,18 +530,8 @@
531 530
532 531 wp_localize_script( 'ep_dashboard_scripts', 'epDash', $data );
533 532 }
534 533
535 - if ( in_array( Screen::factory()->get_current_screen(), [ 'settings' ], true ) ) {
536 - wp_enqueue_script(
537 - 'ep_settings_scripts',
538 - EP_URL . 'dist/js/settings-script.min.js',
539 - Utils\get_asset_info( 'settings-script', 'dependencies' ),
540 - Utils\get_asset_info( 'settings-script', 'version' ),
541 - true
542 - );
543 - }
544 -
545 534 if ( in_array( Screen::factory()->get_current_screen(), [ 'health' ], true ) && ! empty( Utils\get_host() ) ) {
546 535 Stats::factory()->build_stats();
547 536
548 537 $data = Stats::factory()->get_localized();
@@ -548,25 +537,29 @@
548 537 $data = Stats::factory()->get_localized();
549 538
550 539 wp_enqueue_script(
551 540 'ep_stats',
552 - EP_URL . 'dist/js/stats-script.min.js',
541 + EP_URL . 'dist/js/stats-script.js',
553 542 Utils\get_asset_info( 'stats-script', 'dependencies' ),
554 543 Utils\get_asset_info( 'stats-script', 'version' ),
555 544 true
556 545 );
557 546
547 + wp_set_script_translations( 'ep_stats', 'elasticpress' );
548 +
558 549 wp_localize_script( 'ep_stats', 'epChartData', $data );
559 550 }
560 551
561 552 wp_register_script(
562 553 'ep_notice_script',
563 - EP_URL . 'dist/js/notice-script.min.js',
554 + EP_URL . 'dist/js/notice-script.js',
564 555 Utils\get_asset_info( 'notice-script', 'dependencies' ),
565 556 Utils\get_asset_info( 'notice-script', 'version' ),
566 557 true
567 558 );
568 559
560 + wp_set_script_translations( 'ep_notice_script', 'elasticpress' );
561 +
569 562 wp_localize_script(
570 563 'ep_notice_script',
571 564 'epAdmin',
572 565 array(
@@ -572,78 +565,18 @@
572 565 array(
573 566 'nonce' => wp_create_nonce( 'ep_admin_nonce' ),
574 567 )
575 568 );
576 -}
577 569
578 -/**
579 - * Admin-init actions
580 - *
581 - * Sets up Settings API.
582 - *
583 - * @since 1.9
584 - * @return void
585 - */
586 -function action_admin_init() {
587 -
588 - // Save options for multisite.
589 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK && isset( $_POST['ep_language'] ) ) {
590 - check_admin_referer( 'elasticpress-options' );
591 -
592 - $language = sanitize_text_field( $_POST['ep_language'] );
593 - update_site_option( 'ep_language', $language );
594 -
595 - if ( isset( $_POST['ep_host'] ) ) {
596 - $host = esc_url_raw( trim( $_POST['ep_host'] ) );
597 - update_site_option( 'ep_host', $host );
598 - }
599 -
600 - if ( isset( $_POST['ep_prefix'] ) ) {
601 - $prefix = ( isset( $_POST['ep_prefix'] ) ) ? sanitize_text_field( wp_unslash( $_POST['ep_prefix'] ) ) : '';
602 - update_site_option( 'ep_prefix', $prefix );
603 - }
604 -
605 - if ( isset( $_POST['ep_credentials'] ) ) {
606 - $credentials = ( isset( $_POST['ep_credentials'] ) ) ? Utils\sanitize_credentials( $_POST['ep_credentials'] ) : [
607 - 'username' => '',
608 - 'token' => '',
609 - ];
610 -
611 - update_site_option( 'ep_credentials', $credentials );
612 - }
613 -
614 - if ( isset( $_POST['ep_bulk_setting'] ) ) {
615 - update_site_option( 'ep_bulk_setting', intval( $_POST['ep_bulk_setting'] ) );
616 - }
617 - } else {
618 - register_setting( 'elasticpress', 'ep_host', 'esc_url_raw' );
619 - register_setting( 'elasticpress', 'ep_prefix', 'sanitize_text_field' );
620 - register_setting( 'elasticpress', 'ep_credentials', 'ep_sanitize_credentials' );
621 - register_setting( 'elasticpress', 'ep_language', 'sanitize_text_field' );
622 - register_setting(
623 - 'elasticpress',
624 - 'ep_bulk_setting',
625 - [
626 - 'type' => 'integer',
627 - 'sanitize_callback' => __NAMESPACE__ . '\sanitize_bulk_settings',
628 - ]
629 - );
630 - }
570 + wp_enqueue_style(
571 + 'ep_general_styles',
572 + EP_URL . 'dist/css/general-styles.css',
573 + Utils\get_asset_info( 'general-styles', 'dependencies' ),
574 + Utils\get_asset_info( 'general-styles', 'version' )
575 + );
631 576 }
632 577
633 578 /**
634 - * Sanitize bulk settings.
635 - *
636 - * @param int $bulk_settings Number of bulk content items
637 - * @return int
638 - */
639 -function sanitize_bulk_settings( $bulk_settings = 350 ) {
640 - $bulk_settings = absint( $bulk_settings );
641 -
642 - return ( 0 === $bulk_settings ) ? 350 : $bulk_settings;
643 -}
644 -
645 -/**
646 579 * Output current ElasticPress dashboard screen
647 580 *
648 581 * @since 3.0
649 582 */
@@ -659,14 +592,19 @@
659 592 * @since 1.9
660 593 * @return void
661 594 */
662 595 function action_admin_menu() {
663 - $capability = 'manage_options';
596 + Installer::factory()->calculate_install_status();
597 + if ( true !== Installer::factory()->get_install_status() && ! Utils\is_top_level_admin_context() ) {
598 + return;
599 + }
664 600
665 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
666 - $capability = 'manage_network';
601 + if ( ! Utils\is_site_indexable() && ! is_network_admin() ) {
602 + return;
667 603 }
668 604
605 + $capability = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ? Utils\get_network_capability() : Utils\get_capability();
606 +
669 607 add_menu_page(
670 608 'ElasticPress',
671 609 'ElasticPress',
672 610 $capability,
@@ -671,11 +609,15 @@
671 609 'ElasticPress',
672 610 $capability,
673 611 'elasticpress',
674 612 __NAMESPACE__ . '\resolve_screen',
675 - 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiLz48L3N2Zz4='
613 + 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiIGZpbGw9IiNhN2FhYWQiLz48L3N2Zz4='
676 614 );
677 615
616 + if ( ! Utils\is_top_level_admin_context() ) {
617 + return;
618 + }
619 +
678 620 add_submenu_page(
679 621 'elasticpress',
680 622 esc_html__( 'ElasticPress Features', 'elasticpress' ),
681 623 esc_html__( 'Features', 'elasticpress' ),
@@ -709,11 +651,104 @@
709 651 $capability,
710 652 'elasticpress-health',
711 653 __NAMESPACE__ . '\resolve_screen'
712 654 );
655 +
656 + add_submenu_page(
657 + 'elasticpress',
658 + esc_html__( 'ElasticPress Status Report', 'elasticpress' ),
659 + esc_html__( 'Status Report', 'elasticpress' ),
660 + $capability,
661 + 'elasticpress-status-report',
662 + __NAMESPACE__ . '\resolve_screen'
663 + );
713 664 }
714 665
715 666 /**
667 + * Languages supported in Elasticsearch mappings.
668 + *
669 + * If $format is 'elasticsearch', the array format is `Elasticsearch analyzer name => [ WordPress language package names ]`.
670 + *
671 + * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html
672 + * @since 4.7.0
673 + * @param string $format Format of the return ('locales' or 'elasticsearch' )
674 + * @return array
675 + */
676 +function get_available_languages( string $format = 'elasticsearch' ): array {
677 + /**
678 + * Filter available languages in Elasticsearch.
679 + *
680 + * The returned array should follow the format `Elasticsearch analyzer name => [ WordPress language package names ]`.
681 + *
682 + * @since 4.7.0
683 + * @hook ep_available_languages
684 + * @param {bool} $available_languages List of available languages
685 + * @return {bool} New list
686 + */
687 + $es_languages = apply_filters(
688 + 'ep_available_languages',
689 + [
690 + 'arabic' => [ 'ar', 'ary' ],
691 + 'armenian' => [ 'hy' ],
692 + 'basque' => [ 'eu' ],
693 + 'bengali' => [ 'bn', 'bn_BD' ],
694 + 'brazilian' => [ 'pt_BR' ],
695 + 'bulgarian' => [ 'bg', 'bg_BG' ],
696 + 'catalan' => [ 'ca' ],
697 + 'cjk' => [], // CJK characters (not a language)
698 + 'czech' => [ 'cs', 'cs_CZ' ],
699 + 'danish' => [ 'da', 'da_DK' ],
700 + 'dutch' => [ 'nl_NL_formal', 'nl_NL', 'nl_BE' ],
701 + 'english' => [ 'en', 'en_AU', 'en_GB', 'en_NZ', 'en_CA', 'en_US', 'en_ZA' ],
702 + 'estonian' => [ 'et' ],
703 + 'finnish' => [ 'fi' ],
704 + 'french' => [ 'fr', 'fr_CA', 'fr_FR', 'fr_BE' ],
705 + 'galician' => [ 'gl_ES' ],
706 + 'german' => [ 'de', 'de_DE', 'de_DE_formal', 'de_CH', 'de_CH_informal', 'de_AT' ],
707 + 'greek' => [ 'el' ],
708 + 'hindi' => [ 'hi_IN' ],
709 + 'hungarian' => [ 'hu_HU' ],
710 + 'indonesian' => [ 'id_ID' ],
711 + 'irish' => [], // WordPress doesn't support Irish as an active locale currently
712 + 'italian' => [ 'it_IT' ],
713 + 'latvian' => [ 'lv' ],
714 + 'lithuanian' => [ 'lt_LT' ],
715 + 'norwegian' => [ 'nb_NO' ],
716 + 'persian' => [ 'fa_IR' ],
717 + 'portuguese' => [ 'pt', 'pt_AO', 'pt_PT', 'pt_PT_ao90' ],
718 + 'romanian' => [ 'ro_RO' ],
719 + 'russian' => [ 'ru_RU' ],
720 + 'sorani' => [ 'ckb' ],
721 + 'spanish' => [ 'es_CR', 'es_MX', 'es_VE', 'es_AR', 'es_CL', 'es_GT', 'es_PE', 'es_ES', 'es_UY', 'es_CO' ],
722 + 'swedish' => [ 'sv_SE' ],
723 + 'turkish' => [ 'tr_TR' ],
724 + 'thai' => [ 'th' ],
725 + ]
726 + );
727 +
728 + if ( 'locales' === $format ) {
729 + $arr = array_reduce(
730 + $es_languages,
731 + function ( $acc, $lang ) {
732 + $lang = array_filter(
733 + $lang,
734 + function ( $locale ) {
735 + // English is always added. This removes the duplicates
736 + return ! in_array( $locale, [ 'en', 'en_US' ], true );
737 + }
738 + );
739 + $acc = array_merge( $acc, $lang );
740 + return $acc;
741 + },
742 + []
743 + );
744 + return $arr;
745 + }
746 +
747 + return $es_languages;
748 +}
749 +
750 +/**
716 751 * Uses the language from EP settings in mapping.
717 752 *
718 753 * @param string $language The current language.
719 754 * @param string $context The context where the function is running.
@@ -719,8 +754,10 @@
719 754 * @param string $context The context where the function is running.
720 755 * @return string The updated language.
721 756 */
722 757 function use_language_in_setting( $language = 'english', $context = '' ) {
758 + global $locale, $wp_local_package;
759 +
723 760 // Get the currently set language.
724 761 $ep_language = Utils\get_language();
725 762
726 763 // Bail early if no EP language is set.
@@ -727,63 +764,32 @@
727 764 if ( empty( $ep_language ) ) {
728 765 return $language;
729 766 }
730 767
768 + /**
769 + * WordPress does not reset the language when switch_blog() is called.
770 + *
771 + * @see https://core.trac.wordpress.org/ticket/49263
772 + */
773 + if ( 'site-default' === $ep_language ) {
774 + $locale = null;
775 + $wp_local_package = null;
776 + $ep_language = get_locale();
777 + }
778 +
731 779 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
732 780 $translations = wp_get_available_translations();
733 781
734 - // Bail early if not in the array of available translations.
735 - if ( empty( $translations[ $ep_language ]['english_name'] ) ) {
736 - return $language;
782 + // Default to en_US if not in the array of available translations.
783 + if ( ! empty( $translations[ $ep_language ]['english_name'] ) ) {
784 + $wp_language = $translations[ $ep_language ]['language'];
785 + } else {
786 + $wp_language = 'en_US';
737 787 }
738 788
739 - $wp_language = $translations[ $ep_language ]['language'];
789 + $es_languages = get_available_languages();
740 790
741 791 /**
742 - * Languages supported in Elasticsearch mappings.
743 - * Array format: Elasticsearch analyzer name => WordPress language package name
744 - *
745 - * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html
746 - */
747 - $es_languages = [
748 - 'arabic' => [ 'ar', 'ary' ],
749 - 'armenian' => [ 'hy' ],
750 - 'basque' => [ 'eu' ],
751 - 'bengali' => [ 'bn', 'bn_BD' ],
752 - 'brazilian' => [ 'pt_BR' ],
753 - 'bulgarian' => [ 'bg' ],
754 - 'catalan' => [ 'ca' ],
755 - 'cjk' => [], // CJK characters (not a language)
756 - 'czech' => [ 'cs' ],
757 - 'danish' => [ 'da' ],
758 - 'dutch' => [ 'nl_NL_formal', 'nl_NL', 'nl_BE' ],
759 - 'english' => [ 'en', 'en_AU', 'en_GB', 'en_NZ', 'en_CA', 'en_ZA' ],
760 - 'estonian' => [ 'et' ],
761 - 'finnish' => [ 'fi' ],
762 - 'french' => [ 'fr', 'fr_CA', 'fr_FR', 'fr_BE' ],
763 - 'galician' => [ 'gl_ES' ],
764 - 'german' => [ 'de', 'de_DE', 'de_DE_formal', 'de_CH', 'de_CH_informal', 'de_AT' ],
765 - 'greek' => [ 'el' ],
766 - 'hindi' => [ 'hi_IN' ],
767 - 'hungarian' => [ 'hu_HU' ],
768 - 'indonesian' => [ 'id_ID' ],
769 - 'irish' => [], // WordPress doesn't support Irish as an active locale currently
770 - 'italian' => [ 'it_IT' ],
771 - 'latvian' => [ 'lv' ],
772 - 'lithuanian' => [ 'lt_LT' ],
773 - 'norwegian' => [ 'nb_NO' ],
774 - 'persian' => [ 'fa_IR' ],
775 - 'portuguese' => [ 'pt', 'pt_AO', 'pt_PT', 'pt_PT_ao90' ],
776 - 'romanian' => [ 'ro_RO' ],
777 - 'russian' => [ 'ru_RU' ],
778 - 'sorani' => [ 'ckb' ],
779 - 'spanish' => [ 'es_CR', 'es_MX', 'es_VE', 'es_AR', 'es_CL', 'es_GT', 'es_PE', 'es_ES', 'es_UY', 'es_CO' ],
780 - 'swedish' => [ 'sv_SE' ],
781 - 'turkish' => [ 'tr_TR' ],
782 - 'thai' => [ 'th' ],
783 - ];
784 -
785 - /**
786 792 * Languages supported in Elasticsearch snowball token filters.
787 793 *
788 794 * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-snowball-tokenfilter.html
789 795 */
@@ -812,8 +818,12 @@
812 818 'Swedish',
813 819 'Turkish',
814 820 ];
815 821
822 + $es_snowball_similar = [
823 + 'Brazilian' => 'Portuguese',
824 + ];
825 +
816 826 foreach ( $es_languages as $analyzer_name => $analyzer_language_codes ) {
817 827 if ( in_array( $wp_language, $analyzer_language_codes, true ) ) {
818 828 $language = $analyzer_name;
819 829 break;
@@ -820,15 +830,20 @@
820 830 }
821 831 }
822 832
823 833 if ( 'filter_ewp_snowball' === $context ) {
824 - if ( in_array( ucfirst( $language ), $es_snowball_languages, true ) ) {
825 - return ucfirst( $language );
834 + $uc_first_language = ucfirst( $language );
835 + if ( in_array( $uc_first_language, $es_snowball_languages, true ) ) {
836 + return $uc_first_language;
826 837 }
827 838
828 - return 'English';
839 + return $es_snowball_similar[ $uc_first_language ] ?? 'English';
829 840 }
830 841
842 + if ( 'filter_ep_stop' === $context ) {
843 + return "_{$language}_";
844 + }
845 +
831 846 return $language;
832 847 }
833 848
834 849 /**
@@ -852,23 +867,25 @@
852 867 *
853 868 * @return void | string
854 869 */
855 870 function add_blogs_column( $column_name, $blog_id ) {
871 + if ( 'elasticpress' !== $column_name ) {
872 + return;
873 + }
874 +
856 875 $site = get_site( $blog_id );
857 876 if ( $site->deleted || $site->archived || $site->spam ) {
858 877 return;
859 878 }
860 - if ( 'elasticpress' === $column_name ) {
861 - $is_indexable = get_blog_option( $blog_id, 'ep_indexable', 'yes' );
862 879
863 - printf(
864 - '<input %1$s class="index-toggle" data-blog-id="%2$s" disabled type="checkbox">',
865 - checked( $is_indexable, 'yes', false ),
866 - esc_attr( $blog_id )
867 - );
868 - }
880 + $is_indexable = get_site_meta( $blog_id, 'ep_indexable', true );
881 + $is_indexable = '' !== $is_indexable ? $is_indexable : 'yes';
869 882
870 - return $column_name;
883 + printf(
884 + '<input %1$s class="index-toggle" data-blog-id="%2$s" disabled type="checkbox">',
885 + checked( $is_indexable, 'yes', false ),
886 + esc_attr( $blog_id )
887 + );
871 888 }
872 889
873 890 /**
874 891 * AJAX callback to update ep_indexable site option.
@@ -879,10 +896,15 @@
879 896
880 897 if ( - 1 === $blog_id || ! check_ajax_referer( 'epsa', 'nonce', false ) ) {
881 898 return wp_send_json_error();
882 899 }
883 - $old = get_blog_option( $blog_id, 'ep_indexable' );
900 +
901 + /**
902 + * NOTE: This will be removed in ElasticPress 5.0.0. Implementations should rely on site_meta since 4.7.0.
903 + */
884 904 $result = update_blog_option( $blog_id, 'ep_indexable', $checked );
905 +
906 + $result = update_site_meta( $blog_id, 'ep_indexable', $checked );
885 907 $data = [
886 908 'blog_id' => $blog_id,
887 909 'result' => $result,
888 910 ];
@@ -890,23 +912,8 @@
890 912 return wp_send_json_success( $data );
891 913 }
892 914
893 915 /**
894 - * Registers the API endpoint
895 - */
896 -function setup_endpoint() {
897 - register_rest_route(
898 - 'elasticpress/v1',
899 - 'indexing_status',
900 - [
901 - 'methods' => 'GET',
902 - 'callback' => __NAMESPACE__ . '\handle_indexing_status',
903 - 'permission_callback' => '__return_true',
904 - ]
905 - );
906 -}
907 -
908 -/**
909 916 * Handle the fetch for indexing status
910 917 */
911 918 function handle_indexing_status() {
912 919 $indexing_status = \ElasticPress\Utils\get_indexing_status();
@@ -932,5 +939,43 @@
932 939 }
933 940 }
934 941
935 942 return $status;
943 +}
944 +
945 +/**
946 + * Add an ElasticPress block category.
947 + *
948 + * @param array $block_categories Array of categories for block types.
949 + * @return array Array of categories for block types.
950 + */
951 +function block_categories( $block_categories ) {
952 + $block_categories[] = [
953 + 'slug' => 'elasticpress',
954 + 'title' => 'ElasticPress',
955 + ];
956 +
957 + return $block_categories;
958 +}
959 +
960 +/**
961 + * Enqueue shared block editor assets.
962 + *
963 + * @return void
964 + */
965 +function block_assets() {
966 + wp_enqueue_script(
967 + 'elasticpress-blocks',
968 + EP_URL . 'dist/js/blocks-script.js',
969 + Utils\get_asset_info( 'blocks-script', 'dependencies' ),
970 + Utils\get_asset_info( 'blocks-script', 'version' ),
971 + true
972 + );
973 +
974 + wp_localize_script(
975 + 'elasticpress-blocks',
976 + 'epBlocks',
977 + [
978 + 'syncUrl' => Utils\get_sync_url(),
979 + ]
980 + );
936 981 }