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 +268 -224 4.5.25.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
@@ -132,17 +156,16 @@
132 156 * @param array $query The version query.
133 157 * @since 3.0
134 158 */
135 159 function log_version_query_error( $query ) {
136 - $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 + }
137 164
138 165 $logging_key = 'logging_ep_es_info';
139 166
140 - if ( $is_network ) {
141 - $logging = get_site_transient( $logging_key );
142 - } else {
143 - $logging = get_transient( $logging_key );
144 - }
167 + $logging = Utils\get_transient( $logging_key );
145 168
146 169 // Are we logging the version query results?
147 170 if ( '1' === $logging ) {
148 171 /**
@@ -169,17 +192,11 @@
169 192
170 193 // Store the response code, and remove the flag that says
171 194 // we're logging the response code so we don't log additional
172 195 // queries.
173 - if ( $is_network ) {
174 - set_site_transient( $response_code_key, $response_code, $cache_time );
175 - set_site_transient( $response_error_key, $response_error, $cache_time );
176 - delete_site_transient( $logging_key );
177 - } else {
178 - set_transient( $response_code_key, $response_code, $cache_time );
179 - set_transient( $response_error_key, $response_error, $cache_time );
180 - delete_transient( $logging_key );
181 - }
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 );
182 199 }
183 200 }
184 201
185 202 /**
@@ -191,9 +208,9 @@
191 208 if ( ! is_admin() && ! is_network_admin() ) {
192 209 return;
193 210 }
194 211
195 - 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 ) ) {
196 213 return;
197 214 }
198 215
199 216 if ( ! empty( $_GET['ep-skip-features'] ) ) {
@@ -225,9 +242,13 @@
225 242 if ( ! is_admin() && ! is_network_admin() ) {
226 243 return;
227 244 }
228 245
229 - 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 ) ) {
230 251 return;
231 252 }
232 253
233 254 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
@@ -235,10 +256,10 @@
235 256 } else {
236 257 delete_transient( 'ep_es_info' );
237 258 }
238 259
239 - if ( ! empty( $_GET['ep-retry'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
240 - wp_safe_redirect( remove_query_arg( 'ep-retry' ) );
260 + if ( $isset_retry ) {
261 + wp_safe_redirect( remove_query_arg( [ 'ep-retry', 'ep_retry_nonce' ] ) );
241 262 exit();
242 263 }
243 264 }
244 265
@@ -298,17 +319,10 @@
298 319 * @param bool $force Force ES info hard lookup.
299 320 * @since 3.0
300 321 */
301 322 function maybe_notice( $force = false ) {
302 - // Admins only.
303 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
304 - if ( ! is_super_admin() || ! is_network_admin() ) {
305 - return false;
306 - }
307 - } else {
308 - if ( is_network_admin() || ! current_user_can( Utils\get_capability() ) ) {
309 - return false;
310 - }
323 + if ( ! current_user_can( Utils\get_capability() ) ) {
324 + return false;
311 325 }
312 326
313 327 /**
314 328 * Filter how long results of Elasticsearch version query are stored
@@ -319,21 +333,13 @@
319 333 * @return {int} New time in seconds
320 334 */
321 335 $cache_time = apply_filters( 'ep_es_info_cache_expiration', ( 5 * MINUTE_IN_SECONDS ) );
322 336
323 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
324 - set_site_transient(
325 - 'logging_ep_es_info',
326 - '1',
327 - $cache_time
328 - );
329 - } else {
330 - $a = set_transient(
331 - 'logging_ep_es_info',
332 - '1',
333 - $cache_time
334 - );
335 - }
337 + Utils\set_transient(
338 + 'logging_ep_es_info',
339 + '1',
340 + $cache_time
341 + );
336 342
337 343 // Fetch ES version
338 344 Elasticsearch::factory()->get_elasticsearch_version( $force );
339 345
@@ -341,13 +347,14 @@
341 347
342 348 $notices = AdminNotices::factory()->get_notices();
343 349
344 350 foreach ( $notices as $notice_key => $notice ) {
351 + $class = 'notice notice-' . $notice['type'];
352 + if ( $notice['dismiss'] ) {
353 + $class .= ' is-dismissible';
354 + }
345 355 ?>
346 - <div data-ep-notice="<?php echo esc_attr( $notice_key ); ?>" class="notice notice-<?php echo esc_attr( $notice['type'] ); ?> <?php
347 - if ( $notice['dismiss'] ) :
348 - ?>
349 - is-dismissible<?php endif; ?>">
356 + <div data-ep-notice="<?php echo esc_attr( $notice_key ); ?>" class="<?php echo esc_attr( $class ); ?>">
350 357 <p>
351 358 <?php echo wp_kses( $notice['html'], 'ep-html' ); ?>
352 359 </p>
353 360 </div>
@@ -374,9 +381,9 @@
374 381 wp_send_json_error();
375 382 exit;
376 383 }
377 384
378 - AdminNotices::factory()->dismiss_notice( $_POST['notice'] );
385 + AdminNotices::factory()->dismiss_notice( sanitize_key( $_POST['notice'] ) );
379 386
380 387 wp_send_json_success();
381 388 }
382 389
@@ -412,11 +419,11 @@
412 419 *
413 420 * @since 2.2
414 421 */
415 422 function action_wp_ajax_ep_save_feature() {
416 - $_POST = wp_unslash( $_POST );
423 + $post = wp_unslash( $_POST );
417 424
418 - 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 ) ) {
419 426 wp_send_json_error();
420 427 exit;
421 428 }
422 429
@@ -426,12 +433,12 @@
426 433 wp_send_json_error( $error );
427 434 exit;
428 435 }
429 436
430 - $data = Features::factory()->update_feature( $_POST['feature'], $_POST['settings'] );
437 + $data = Features::factory()->update_feature( $post['feature'], $post['settings'] );
431 438
432 439 // Since we deactivated, delete auto activate notice.
433 - if ( empty( $_POST['settings']['active'] ) ) {
440 + if ( empty( $post['settings']['active'] ) ) {
434 441 Utils\delete_option( 'ep_feature_auto_activated_sync' );
435 442 }
436 443
437 444 wp_send_json_success( $data );
@@ -463,9 +470,19 @@
463 470
464 471 wp_localize_script( 'ep_admin_sites_scripts', 'epsa', $data );
465 472 }
466 473
467 - if ( in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'settings', 'install', 'health', 'weighting', 'synonyms', 'sync', 'status-report' ], 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 ) ) {
468 485 wp_enqueue_style(
469 486 'ep_admin_styles',
470 487 EP_URL . 'dist/css/dashboard-styles.css',
471 488 Utils\get_asset_info( 'dashboard-styles', 'dependencies' ),
@@ -481,20 +498,8 @@
481 498
482 499 wp_set_script_translations( 'ep_admin_script', 'elasticpress' );
483 500 }
484 501
485 - if ( in_array( Screen::factory()->get_current_screen(), [ 'weighting', 'install' ], true ) ) {
486 - wp_enqueue_script(
487 - 'ep_weighting_script',
488 - EP_URL . 'dist/js/weighting-script.js',
489 - Utils\get_asset_info( 'weighting-script', 'dependencies' ),
490 - Utils\get_asset_info( 'weighting-script', 'version' ),
491 - true
492 - );
493 -
494 - wp_set_script_translations( 'ep_weighting_script', 'elasticpress' );
495 - }
496 -
497 502 if ( in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'install' ], true ) ) {
498 503 wp_enqueue_script(
499 504 'ep_dashboard_scripts',
500 505 EP_URL . 'dist/js/dashboard-script.js',
@@ -504,11 +509,9 @@
504 509 );
505 510
506 511 wp_set_script_translations( 'ep_dashboard_scripts', 'elasticpress' );
507 512
508 - $sync_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ?
509 - network_admin_url( 'admin.php?page=elasticpress-sync&do_sync' ) :
510 - admin_url( 'admin.php?page=elasticpress-sync&do_sync' );
513 + $sync_url = Utils\get_sync_url( true );
511 514
512 515 $skip_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ?
513 516 network_admin_url( 'admin.php?page=elasticpress' ) :
514 517 admin_url( 'admin.php?page=elasticpress' );
@@ -527,20 +530,8 @@
527 530
528 531 wp_localize_script( 'ep_dashboard_scripts', 'epDash', $data );
529 532 }
530 533
531 - if ( in_array( Screen::factory()->get_current_screen(), [ 'settings' ], true ) ) {
532 - wp_enqueue_script(
533 - 'ep_settings_scripts',
534 - EP_URL . 'dist/js/settings-script.js',
535 - Utils\get_asset_info( 'settings-script', 'dependencies' ),
536 - Utils\get_asset_info( 'settings-script', 'version' ),
537 - true
538 - );
539 -
540 - wp_set_script_translations( 'ep_settings_scripts', 'elasticpress' );
541 - }
542 -
543 534 if ( in_array( Screen::factory()->get_current_screen(), [ 'health' ], true ) && ! empty( Utils\get_host() ) ) {
544 535 Stats::factory()->build_stats();
545 536
546 537 $data = Stats::factory()->get_localized();
@@ -574,72 +565,18 @@
574 565 array(
575 566 'nonce' => wp_create_nonce( 'ep_admin_nonce' ),
576 567 )
577 568 );
578 -}
579 569
580 -/**
581 - * Admin-init actions
582 - *
583 - * Sets up Settings API.
584 - *
585 - * @since 1.9
586 - * @return void
587 - */
588 -function action_admin_init() {
589 -
590 - // Save options for multisite.
591 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK && isset( $_POST['ep_language'] ) ) {
592 - check_admin_referer( 'elasticpress-options' );
593 -
594 - $language = sanitize_text_field( $_POST['ep_language'] );
595 - Utils\update_option( 'ep_language', $language );
596 -
597 - if ( isset( $_POST['ep_host'] ) ) {
598 - $host = esc_url_raw( trim( $_POST['ep_host'] ) );
599 - Utils\update_option( 'ep_host', $host );
600 - }
601 -
602 - if ( isset( $_POST['ep_credentials'] ) ) {
603 - $credentials = ( isset( $_POST['ep_credentials'] ) ) ? Utils\sanitize_credentials( $_POST['ep_credentials'] ) : [
604 - 'username' => '',
605 - 'token' => '',
606 - ];
607 -
608 - Utils\update_option( 'ep_credentials', $credentials );
609 - }
610 -
611 - if ( isset( $_POST['ep_bulk_setting'] ) ) {
612 - Utils\update_option( 'ep_bulk_setting', intval( $_POST['ep_bulk_setting'] ) );
613 - }
614 - } else {
615 - register_setting( 'elasticpress', 'ep_host', 'esc_url_raw' );
616 - register_setting( 'elasticpress', 'ep_credentials', 'ep_sanitize_credentials' );
617 - register_setting( 'elasticpress', 'ep_language', 'sanitize_text_field' );
618 - register_setting(
619 - 'elasticpress',
620 - 'ep_bulk_setting',
621 - [
622 - 'type' => 'integer',
623 - 'sanitize_callback' => __NAMESPACE__ . '\sanitize_bulk_settings',
624 - ]
625 - );
626 - }
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 + );
627 576 }
628 577
629 578 /**
630 - * Sanitize bulk settings.
631 - *
632 - * @param int $bulk_settings Number of bulk content items
633 - * @return int
634 - */
635 -function sanitize_bulk_settings( $bulk_settings = 350 ) {
636 - $bulk_settings = absint( $bulk_settings );
637 -
638 - return ( 0 === $bulk_settings ) ? 350 : $bulk_settings;
639 -}
640 -
641 -/**
642 579 * Output current ElasticPress dashboard screen
643 580 *
644 581 * @since 3.0
645 582 */
@@ -655,8 +592,17 @@
655 592 * @since 1.9
656 593 * @return void
657 594 */
658 595 function action_admin_menu() {
596 + Installer::factory()->calculate_install_status();
597 + if ( true !== Installer::factory()->get_install_status() && ! Utils\is_top_level_admin_context() ) {
598 + return;
599 + }
600 +
601 + if ( ! Utils\is_site_indexable() && ! is_network_admin() ) {
602 + return;
603 + }
604 +
659 605 $capability = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ? Utils\get_network_capability() : Utils\get_capability();
660 606
661 607 add_menu_page(
662 608 'ElasticPress',
@@ -663,11 +609,15 @@
663 609 'ElasticPress',
664 610 $capability,
665 611 'elasticpress',
666 612 __NAMESPACE__ . '\resolve_screen',
667 - 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiLz48L3N2Zz4='
613 + 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiIGZpbGw9IiNhN2FhYWQiLz48L3N2Zz4='
668 614 );
669 615
616 + if ( ! Utils\is_top_level_admin_context() ) {
617 + return;
618 + }
619 +
670 620 add_submenu_page(
671 621 'elasticpress',
672 622 esc_html__( 'ElasticPress Features', 'elasticpress' ),
673 623 esc_html__( 'Features', 'elasticpress' ),
@@ -713,8 +663,92 @@
713 663 );
714 664 }
715 665
716 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 +/**
717 751 * Uses the language from EP settings in mapping.
718 752 *
719 753 * @param string $language The current language.
720 754 * @param string $context The context where the function is running.
@@ -720,8 +754,10 @@
720 754 * @param string $context The context where the function is running.
721 755 * @return string The updated language.
722 756 */
723 757 function use_language_in_setting( $language = 'english', $context = '' ) {
758 + global $locale, $wp_local_package;
759 +
724 760 // Get the currently set language.
725 761 $ep_language = Utils\get_language();
726 762
727 763 // Bail early if no EP language is set.
@@ -728,63 +764,32 @@
728 764 if ( empty( $ep_language ) ) {
729 765 return $language;
730 766 }
731 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 +
732 779 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
733 780 $translations = wp_get_available_translations();
734 781
735 - // Bail early if not in the array of available translations.
736 - if ( empty( $translations[ $ep_language ]['english_name'] ) ) {
737 - 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';
738 787 }
739 788
740 - $wp_language = $translations[ $ep_language ]['language'];
789 + $es_languages = get_available_languages();
741 790
742 791 /**
743 - * Languages supported in Elasticsearch mappings.
744 - * Array format: Elasticsearch analyzer name => WordPress language package name
745 - *
746 - * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html
747 - */
748 - $es_languages = [
749 - 'arabic' => [ 'ar', 'ary' ],
750 - 'armenian' => [ 'hy' ],
751 - 'basque' => [ 'eu' ],
752 - 'bengali' => [ 'bn', 'bn_BD' ],
753 - 'brazilian' => [ 'pt_BR' ],
754 - 'bulgarian' => [ 'bg' ],
755 - 'catalan' => [ 'ca' ],
756 - 'cjk' => [], // CJK characters (not a language)
757 - 'czech' => [ 'cs' ],
758 - 'danish' => [ 'da' ],
759 - 'dutch' => [ 'nl_NL_formal', 'nl_NL', 'nl_BE' ],
760 - 'english' => [ 'en', 'en_AU', 'en_GB', 'en_NZ', 'en_CA', 'en_ZA' ],
761 - 'estonian' => [ 'et' ],
762 - 'finnish' => [ 'fi' ],
763 - 'french' => [ 'fr', 'fr_CA', 'fr_FR', 'fr_BE' ],
764 - 'galician' => [ 'gl_ES' ],
765 - 'german' => [ 'de', 'de_DE', 'de_DE_formal', 'de_CH', 'de_CH_informal', 'de_AT' ],
766 - 'greek' => [ 'el' ],
767 - 'hindi' => [ 'hi_IN' ],
768 - 'hungarian' => [ 'hu_HU' ],
769 - 'indonesian' => [ 'id_ID' ],
770 - 'irish' => [], // WordPress doesn't support Irish as an active locale currently
771 - 'italian' => [ 'it_IT' ],
772 - 'latvian' => [ 'lv' ],
773 - 'lithuanian' => [ 'lt_LT' ],
774 - 'norwegian' => [ 'nb_NO' ],
775 - 'persian' => [ 'fa_IR' ],
776 - 'portuguese' => [ 'pt', 'pt_AO', 'pt_PT', 'pt_PT_ao90' ],
777 - 'romanian' => [ 'ro_RO' ],
778 - 'russian' => [ 'ru_RU' ],
779 - 'sorani' => [ 'ckb' ],
780 - 'spanish' => [ 'es_CR', 'es_MX', 'es_VE', 'es_AR', 'es_CL', 'es_GT', 'es_PE', 'es_ES', 'es_UY', 'es_CO' ],
781 - 'swedish' => [ 'sv_SE' ],
782 - 'turkish' => [ 'tr_TR' ],
783 - 'thai' => [ 'th' ],
784 - ];
785 -
786 - /**
787 792 * Languages supported in Elasticsearch snowball token filters.
788 793 *
789 794 * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-snowball-tokenfilter.html
790 795 */
@@ -813,8 +818,12 @@
813 818 'Swedish',
814 819 'Turkish',
815 820 ];
816 821
822 + $es_snowball_similar = [
823 + 'Brazilian' => 'Portuguese',
824 + ];
825 +
817 826 foreach ( $es_languages as $analyzer_name => $analyzer_language_codes ) {
818 827 if ( in_array( $wp_language, $analyzer_language_codes, true ) ) {
819 828 $language = $analyzer_name;
820 829 break;
@@ -821,15 +830,20 @@
821 830 }
822 831 }
823 832
824 833 if ( 'filter_ewp_snowball' === $context ) {
825 - if ( in_array( ucfirst( $language ), $es_snowball_languages, true ) ) {
826 - 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;
827 837 }
828 838
829 - return 'English';
839 + return $es_snowball_similar[ $uc_first_language ] ?? 'English';
830 840 }
831 841
842 + if ( 'filter_ep_stop' === $context ) {
843 + return "_{$language}_";
844 + }
845 +
832 846 return $language;
833 847 }
834 848
835 849 /**
@@ -853,23 +867,25 @@
853 867 *
854 868 * @return void | string
855 869 */
856 870 function add_blogs_column( $column_name, $blog_id ) {
871 + if ( 'elasticpress' !== $column_name ) {
872 + return;
873 + }
874 +
857 875 $site = get_site( $blog_id );
858 876 if ( $site->deleted || $site->archived || $site->spam ) {
859 877 return;
860 878 }
861 - if ( 'elasticpress' === $column_name ) {
862 - $is_indexable = get_blog_option( $blog_id, 'ep_indexable', 'yes' );
863 879
864 - printf(
865 - '<input %1$s class="index-toggle" data-blog-id="%2$s" disabled type="checkbox">',
866 - checked( $is_indexable, 'yes', false ),
867 - esc_attr( $blog_id )
868 - );
869 - }
880 + $is_indexable = get_site_meta( $blog_id, 'ep_indexable', true );
881 + $is_indexable = '' !== $is_indexable ? $is_indexable : 'yes';
870 882
871 - 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 + );
872 888 }
873 889
874 890 /**
875 891 * AJAX callback to update ep_indexable site option.
@@ -880,10 +896,15 @@
880 896
881 897 if ( - 1 === $blog_id || ! check_ajax_referer( 'epsa', 'nonce', false ) ) {
882 898 return wp_send_json_error();
883 899 }
884 - $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 + */
885 904 $result = update_blog_option( $blog_id, 'ep_indexable', $checked );
905 +
906 + $result = update_site_meta( $blog_id, 'ep_indexable', $checked );
886 907 $data = [
887 908 'blog_id' => $blog_id,
888 909 'result' => $result,
889 910 ];
@@ -891,23 +912,8 @@
891 912 return wp_send_json_success( $data );
892 913 }
893 914
894 915 /**
895 - * Registers the API endpoint
896 - */
897 -function setup_endpoint() {
898 - register_rest_route(
899 - 'elasticpress/v1',
900 - 'indexing_status',
901 - [
902 - 'methods' => 'GET',
903 - 'callback' => __NAMESPACE__ . '\handle_indexing_status',
904 - 'permission_callback' => '__return_true',
905 - ]
906 - );
907 -}
908 -
909 -/**
910 916 * Handle the fetch for indexing status
911 917 */
912 918 function handle_indexing_status() {
913 919 $indexing_status = \ElasticPress\Utils\get_indexing_status();
@@ -933,5 +939,43 @@
933 939 }
934 940 }
935 941
936 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 + );
937 981 }