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 +273 -240 4.4.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'] ) ) {
@@ -222,9 +242,13 @@
222 242 if ( ! is_admin() && ! is_network_admin() ) {
223 243 return;
224 244 }
225 245
226 - 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 ) ) {
227 251 return;
228 252 }
229 253
230 254 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
@@ -232,10 +256,11 @@
232 256 } else {
233 257 delete_transient( 'ep_es_info' );
234 258 }
235 259
236 - if ( ! empty( $_GET['ep-retry'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
237 - 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();
238 263 }
239 264 }
240 265
241 266 /**
@@ -294,30 +319,12 @@
294 319 * @param bool $force Force ES info hard lookup.
295 320 * @since 3.0
296 321 */
297 322 function maybe_notice( $force = false ) {
298 - // Admins only.
299 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
300 - if ( ! is_super_admin() ) {
301 - return false;
302 - }
303 - } else {
304 - if ( ! current_user_can( 'manage_options' ) ) {
305 - return false;
306 - }
323 + if ( ! current_user_can( Utils\get_capability() ) ) {
324 + return false;
307 325 }
308 326
309 - // If in network mode, don't output notice in admin and vice-versa.
310 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
311 - if ( ! is_network_admin() ) {
312 - return false;
313 - }
314 - } else {
315 - if ( is_network_admin() ) {
316 - return false;
317 - }
318 - }
319 -
320 327 /**
321 328 * Filter how long results of Elasticsearch version query are stored
322 329 *
323 330 * @since 23.0
@@ -326,21 +333,13 @@
326 333 * @return {int} New time in seconds
327 334 */
328 335 $cache_time = apply_filters( 'ep_es_info_cache_expiration', ( 5 * MINUTE_IN_SECONDS ) );
329 336
330 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
331 - set_site_transient(
332 - 'logging_ep_es_info',
333 - '1',
334 - $cache_time
335 - );
336 - } else {
337 - $a = set_transient(
338 - 'logging_ep_es_info',
339 - '1',
340 - $cache_time
341 - );
342 - }
337 + Utils\set_transient(
338 + 'logging_ep_es_info',
339 + '1',
340 + $cache_time
341 + );
343 342
344 343 // Fetch ES version
345 344 Elasticsearch::factory()->get_elasticsearch_version( $force );
346 345
@@ -348,13 +347,14 @@
348 347
349 348 $notices = AdminNotices::factory()->get_notices();
350 349
351 350 foreach ( $notices as $notice_key => $notice ) {
351 + $class = 'notice notice-' . $notice['type'];
352 + if ( $notice['dismiss'] ) {
353 + $class .= ' is-dismissible';
354 + }
352 355 ?>
353 - <div data-ep-notice="<?php echo esc_attr( $notice_key ); ?>" class="notice notice-<?php echo esc_attr( $notice['type'] ); ?> <?php
354 - if ( $notice['dismiss'] ) :
355 - ?>
356 - is-dismissible<?php endif; ?>">
356 + <div data-ep-notice="<?php echo esc_attr( $notice_key ); ?>" class="<?php echo esc_attr( $class ); ?>">
357 357 <p>
358 358 <?php echo wp_kses( $notice['html'], 'ep-html' ); ?>
359 359 </p>
360 360 </div>
@@ -376,14 +376,14 @@
376 376 wp_send_json_error();
377 377 exit;
378 378 }
379 379
380 - if ( ! current_user_can( 'manage_options' ) ) {
380 + if ( ! current_user_can( Utils\get_capability() ) ) {
381 381 wp_send_json_error();
382 382 exit;
383 383 }
384 384
385 - AdminNotices::factory()->dismiss_notice( $_POST['notice'] );
385 + AdminNotices::factory()->dismiss_notice( sanitize_key( $_POST['notice'] ) );
386 386
387 387 wp_send_json_success();
388 388 }
389 389
@@ -419,11 +419,11 @@
419 419 *
420 420 * @since 2.2
421 421 */
422 422 function action_wp_ajax_ep_save_feature() {
423 - $_POST = wp_unslash( $_POST );
423 + $post = wp_unslash( $_POST );
424 424
425 - 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 ) ) {
426 426 wp_send_json_error();
427 427 exit;
428 428 }
429 429
@@ -433,12 +433,12 @@
433 433 wp_send_json_error( $error );
434 434 exit;
435 435 }
436 436
437 - $data = Features::factory()->update_feature( $_POST['feature'], $_POST['settings'] );
437 + $data = Features::factory()->update_feature( $post['feature'], $post['settings'] );
438 438
439 439 // Since we deactivated, delete auto activate notice.
440 - if ( empty( $_POST['settings']['active'] ) ) {
440 + if ( empty( $post['settings']['active'] ) ) {
441 441 Utils\delete_option( 'ep_feature_auto_activated_sync' );
442 442 }
443 443
444 444 wp_send_json_success( $data );
@@ -470,9 +470,19 @@
470 470
471 471 wp_localize_script( 'ep_admin_sites_scripts', 'epsa', $data );
472 472 }
473 473
474 - 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 ) ) {
475 485 wp_enqueue_style(
476 486 'ep_admin_styles',
477 487 EP_URL . 'dist/css/dashboard-styles.css',
478 488 Utils\get_asset_info( 'dashboard-styles', 'dependencies' ),
@@ -488,20 +498,8 @@
488 498
489 499 wp_set_script_translations( 'ep_admin_script', 'elasticpress' );
490 500 }
491 501
492 - if ( in_array( Screen::factory()->get_current_screen(), [ 'weighting', 'install' ], true ) ) {
493 - wp_enqueue_script(
494 - 'ep_weighting_script',
495 - EP_URL . 'dist/js/weighting-script.js',
496 - Utils\get_asset_info( 'weighting-script', 'dependencies' ),
497 - Utils\get_asset_info( 'weighting-script', 'version' ),
498 - true
499 - );
500 -
501 - wp_set_script_translations( 'ep_weighting_script', 'elasticpress' );
502 - }
503 -
504 502 if ( in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'install' ], true ) ) {
505 503 wp_enqueue_script(
506 504 'ep_dashboard_scripts',
507 505 EP_URL . 'dist/js/dashboard-script.js',
@@ -511,11 +509,9 @@
511 509 );
512 510
513 511 wp_set_script_translations( 'ep_dashboard_scripts', 'elasticpress' );
514 512
515 - $sync_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ?
516 - network_admin_url( 'admin.php?page=elasticpress-sync&do_sync' ) :
517 - admin_url( 'admin.php?page=elasticpress-sync&do_sync' );
513 + $sync_url = Utils\get_sync_url( true );
518 514
519 515 $skip_url = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ?
520 516 network_admin_url( 'admin.php?page=elasticpress' ) :
521 517 admin_url( 'admin.php?page=elasticpress' );
@@ -534,20 +530,8 @@
534 530
535 531 wp_localize_script( 'ep_dashboard_scripts', 'epDash', $data );
536 532 }
537 533
538 - if ( in_array( Screen::factory()->get_current_screen(), [ 'settings' ], true ) ) {
539 - wp_enqueue_script(
540 - 'ep_settings_scripts',
541 - EP_URL . 'dist/js/settings-script.js',
542 - Utils\get_asset_info( 'settings-script', 'dependencies' ),
543 - Utils\get_asset_info( 'settings-script', 'version' ),
544 - true
545 - );
546 -
547 - wp_set_script_translations( 'ep_settings_scripts', 'elasticpress' );
548 - }
549 -
550 534 if ( in_array( Screen::factory()->get_current_screen(), [ 'health' ], true ) && ! empty( Utils\get_host() ) ) {
551 535 Stats::factory()->build_stats();
552 536
553 537 $data = Stats::factory()->get_localized();
@@ -581,72 +565,18 @@
581 565 array(
582 566 'nonce' => wp_create_nonce( 'ep_admin_nonce' ),
583 567 )
584 568 );
585 -}
586 569
587 -/**
588 - * Admin-init actions
589 - *
590 - * Sets up Settings API.
591 - *
592 - * @since 1.9
593 - * @return void
594 - */
595 -function action_admin_init() {
596 -
597 - // Save options for multisite.
598 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK && isset( $_POST['ep_language'] ) ) {
599 - check_admin_referer( 'elasticpress-options' );
600 -
601 - $language = sanitize_text_field( $_POST['ep_language'] );
602 - Utils\update_option( 'ep_language', $language );
603 -
604 - if ( isset( $_POST['ep_host'] ) ) {
605 - $host = esc_url_raw( trim( $_POST['ep_host'] ) );
606 - Utils\update_option( 'ep_host', $host );
607 - }
608 -
609 - if ( isset( $_POST['ep_credentials'] ) ) {
610 - $credentials = ( isset( $_POST['ep_credentials'] ) ) ? Utils\sanitize_credentials( $_POST['ep_credentials'] ) : [
611 - 'username' => '',
612 - 'token' => '',
613 - ];
614 -
615 - Utils\update_option( 'ep_credentials', $credentials );
616 - }
617 -
618 - if ( isset( $_POST['ep_bulk_setting'] ) ) {
619 - Utils\update_option( 'ep_bulk_setting', intval( $_POST['ep_bulk_setting'] ) );
620 - }
621 - } else {
622 - register_setting( 'elasticpress', 'ep_host', 'esc_url_raw' );
623 - register_setting( 'elasticpress', 'ep_credentials', 'ep_sanitize_credentials' );
624 - register_setting( 'elasticpress', 'ep_language', 'sanitize_text_field' );
625 - register_setting(
626 - 'elasticpress',
627 - 'ep_bulk_setting',
628 - [
629 - 'type' => 'integer',
630 - 'sanitize_callback' => __NAMESPACE__ . '\sanitize_bulk_settings',
631 - ]
632 - );
633 - }
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 + );
634 576 }
635 577
636 578 /**
637 - * Sanitize bulk settings.
638 - *
639 - * @param int $bulk_settings Number of bulk content items
640 - * @return int
641 - */
642 -function sanitize_bulk_settings( $bulk_settings = 350 ) {
643 - $bulk_settings = absint( $bulk_settings );
644 -
645 - return ( 0 === $bulk_settings ) ? 350 : $bulk_settings;
646 -}
647 -
648 -/**
649 579 * Output current ElasticPress dashboard screen
650 580 *
651 581 * @since 3.0
652 582 */
@@ -662,14 +592,19 @@
662 592 * @since 1.9
663 593 * @return void
664 594 */
665 595 function action_admin_menu() {
666 - $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 + }
667 600
668 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
669 - $capability = 'manage_network';
601 + if ( ! Utils\is_site_indexable() && ! is_network_admin() ) {
602 + return;
670 603 }
671 604
605 + $capability = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ? Utils\get_network_capability() : Utils\get_capability();
606 +
672 607 add_menu_page(
673 608 'ElasticPress',
674 609 'ElasticPress',
675 610 $capability,
@@ -674,11 +609,15 @@
674 609 'ElasticPress',
675 610 $capability,
676 611 'elasticpress',
677 612 __NAMESPACE__ . '\resolve_screen',
678 - 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiLz48L3N2Zz4='
613 + 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNzMgNzEuMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNzMgNzEuMzsiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwYXRoIGQ9Ik0zNi41LDQuN0MxOS40LDQuNyw1LjYsMTguNiw1LjYsMzUuN2MwLDEwLDQuNywxOC45LDEyLjEsMjQuNWw0LjUtNC41YzAuMS0wLjEsMC4xLTAuMiwwLjItMC4zbDAuNy0wLjdsNi40LTYuNGMyLjEsMS4yLDQuNSwxLjksNy4xLDEuOWM4LDAsMTQuNS02LjUsMTQuNS0xNC41cy02LjUtMTQuNS0xNC41LTE0LjVTMjIsMjcuNiwyMiwzNS42YzAsMi44LDAuOCw1LjMsMi4xLDcuNWwtNi40LDYuNGMtMi45LTMuOS00LjYtOC43LTQuNi0xMy45YzAtMTIuOSwxMC41LTIzLjQsMjMuNC0yMy40czIzLjQsMTAuNSwyMy40LDIzLjRTNDkuNCw1OSwzNi41LDU5Yy0yLjEsMC00LjEtMC4zLTYtMC44bC0wLjYsMC42bC01LjIsNS40YzMuNiwxLjUsNy42LDIuMywxMS44LDIuM2MxNy4xLDAsMzAuOS0xMy45LDMwLjktMzAuOVM1My42LDQuNywzNi41LDQuN3oiIGZpbGw9IiNhN2FhYWQiLz48L3N2Zz4='
679 614 );
680 615
616 + if ( ! Utils\is_top_level_admin_context() ) {
617 + return;
618 + }
619 +
681 620 add_submenu_page(
682 621 'elasticpress',
683 622 esc_html__( 'ElasticPress Features', 'elasticpress' ),
684 623 esc_html__( 'Features', 'elasticpress' ),
@@ -724,8 +663,92 @@
724 663 );
725 664 }
726 665
727 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 +/**
728 751 * Uses the language from EP settings in mapping.
729 752 *
730 753 * @param string $language The current language.
731 754 * @param string $context The context where the function is running.
@@ -731,8 +754,10 @@
731 754 * @param string $context The context where the function is running.
732 755 * @return string The updated language.
733 756 */
734 757 function use_language_in_setting( $language = 'english', $context = '' ) {
758 + global $locale, $wp_local_package;
759 +
735 760 // Get the currently set language.
736 761 $ep_language = Utils\get_language();
737 762
738 763 // Bail early if no EP language is set.
@@ -739,63 +764,32 @@
739 764 if ( empty( $ep_language ) ) {
740 765 return $language;
741 766 }
742 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 +
743 779 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
744 780 $translations = wp_get_available_translations();
745 781
746 - // Bail early if not in the array of available translations.
747 - if ( empty( $translations[ $ep_language ]['english_name'] ) ) {
748 - 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';
749 787 }
750 788
751 - $wp_language = $translations[ $ep_language ]['language'];
789 + $es_languages = get_available_languages();
752 790
753 791 /**
754 - * Languages supported in Elasticsearch mappings.
755 - * Array format: Elasticsearch analyzer name => WordPress language package name
756 - *
757 - * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html
758 - */
759 - $es_languages = [
760 - 'arabic' => [ 'ar', 'ary' ],
761 - 'armenian' => [ 'hy' ],
762 - 'basque' => [ 'eu' ],
763 - 'bengali' => [ 'bn', 'bn_BD' ],
764 - 'brazilian' => [ 'pt_BR' ],
765 - 'bulgarian' => [ 'bg' ],
766 - 'catalan' => [ 'ca' ],
767 - 'cjk' => [], // CJK characters (not a language)
768 - 'czech' => [ 'cs' ],
769 - 'danish' => [ 'da' ],
770 - 'dutch' => [ 'nl_NL_formal', 'nl_NL', 'nl_BE' ],
771 - 'english' => [ 'en', 'en_AU', 'en_GB', 'en_NZ', 'en_CA', 'en_ZA' ],
772 - 'estonian' => [ 'et' ],
773 - 'finnish' => [ 'fi' ],
774 - 'french' => [ 'fr', 'fr_CA', 'fr_FR', 'fr_BE' ],
775 - 'galician' => [ 'gl_ES' ],
776 - 'german' => [ 'de', 'de_DE', 'de_DE_formal', 'de_CH', 'de_CH_informal', 'de_AT' ],
777 - 'greek' => [ 'el' ],
778 - 'hindi' => [ 'hi_IN' ],
779 - 'hungarian' => [ 'hu_HU' ],
780 - 'indonesian' => [ 'id_ID' ],
781 - 'irish' => [], // WordPress doesn't support Irish as an active locale currently
782 - 'italian' => [ 'it_IT' ],
783 - 'latvian' => [ 'lv' ],
784 - 'lithuanian' => [ 'lt_LT' ],
785 - 'norwegian' => [ 'nb_NO' ],
786 - 'persian' => [ 'fa_IR' ],
787 - 'portuguese' => [ 'pt', 'pt_AO', 'pt_PT', 'pt_PT_ao90' ],
788 - 'romanian' => [ 'ro_RO' ],
789 - 'russian' => [ 'ru_RU' ],
790 - 'sorani' => [ 'ckb' ],
791 - 'spanish' => [ 'es_CR', 'es_MX', 'es_VE', 'es_AR', 'es_CL', 'es_GT', 'es_PE', 'es_ES', 'es_UY', 'es_CO' ],
792 - 'swedish' => [ 'sv_SE' ],
793 - 'turkish' => [ 'tr_TR' ],
794 - 'thai' => [ 'th' ],
795 - ];
796 -
797 - /**
798 792 * Languages supported in Elasticsearch snowball token filters.
799 793 *
800 794 * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-snowball-tokenfilter.html
801 795 */
@@ -824,8 +818,12 @@
824 818 'Swedish',
825 819 'Turkish',
826 820 ];
827 821
822 + $es_snowball_similar = [
823 + 'Brazilian' => 'Portuguese',
824 + ];
825 +
828 826 foreach ( $es_languages as $analyzer_name => $analyzer_language_codes ) {
829 827 if ( in_array( $wp_language, $analyzer_language_codes, true ) ) {
830 828 $language = $analyzer_name;
831 829 break;
@@ -832,15 +830,20 @@
832 830 }
833 831 }
834 832
835 833 if ( 'filter_ewp_snowball' === $context ) {
836 - if ( in_array( ucfirst( $language ), $es_snowball_languages, true ) ) {
837 - 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;
838 837 }
839 838
840 - return 'English';
839 + return $es_snowball_similar[ $uc_first_language ] ?? 'English';
841 840 }
842 841
842 + if ( 'filter_ep_stop' === $context ) {
843 + return "_{$language}_";
844 + }
845 +
843 846 return $language;
844 847 }
845 848
846 849 /**
@@ -864,23 +867,25 @@
864 867 *
865 868 * @return void | string
866 869 */
867 870 function add_blogs_column( $column_name, $blog_id ) {
871 + if ( 'elasticpress' !== $column_name ) {
872 + return;
873 + }
874 +
868 875 $site = get_site( $blog_id );
869 876 if ( $site->deleted || $site->archived || $site->spam ) {
870 877 return;
871 878 }
872 - if ( 'elasticpress' === $column_name ) {
873 - $is_indexable = get_blog_option( $blog_id, 'ep_indexable', 'yes' );
874 879
875 - printf(
876 - '<input %1$s class="index-toggle" data-blog-id="%2$s" disabled type="checkbox">',
877 - checked( $is_indexable, 'yes', false ),
878 - esc_attr( $blog_id )
879 - );
880 - }
880 + $is_indexable = get_site_meta( $blog_id, 'ep_indexable', true );
881 + $is_indexable = '' !== $is_indexable ? $is_indexable : 'yes';
881 882
882 - 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 + );
883 888 }
884 889
885 890 /**
886 891 * AJAX callback to update ep_indexable site option.
@@ -891,10 +896,15 @@
891 896
892 897 if ( - 1 === $blog_id || ! check_ajax_referer( 'epsa', 'nonce', false ) ) {
893 898 return wp_send_json_error();
894 899 }
895 - $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 + */
896 904 $result = update_blog_option( $blog_id, 'ep_indexable', $checked );
905 +
906 + $result = update_site_meta( $blog_id, 'ep_indexable', $checked );
897 907 $data = [
898 908 'blog_id' => $blog_id,
899 909 'result' => $result,
900 910 ];
@@ -902,23 +912,8 @@
902 912 return wp_send_json_success( $data );
903 913 }
904 914
905 915 /**
906 - * Registers the API endpoint
907 - */
908 -function setup_endpoint() {
909 - register_rest_route(
910 - 'elasticpress/v1',
911 - 'indexing_status',
912 - [
913 - 'methods' => 'GET',
914 - 'callback' => __NAMESPACE__ . '\handle_indexing_status',
915 - 'permission_callback' => '__return_true',
916 - ]
917 - );
918 -}
919 -
920 -/**
921 916 * Handle the fetch for indexing status
922 917 */
923 918 function handle_indexing_status() {
924 919 $indexing_status = \ElasticPress\Utils\get_indexing_status();
@@ -944,5 +939,43 @@
944 939 }
945 940 }
946 941
947 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 + );
948 981 }