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/classes/Feature/ProtectedContent/ProtectedContent.php +189 -29 4.4.05.3.5 View file →
@@ -7,12 +7,12 @@
7 7 */
8 8
9 9 namespace ElasticPress\Feature\ProtectedContent;
10 10
11 -use ElasticPress\Utils as Utils;
12 -use ElasticPress\Feature as Feature;
13 -use ElasticPress\Features as Features;
14 -use ElasticPress\FeatureRequirementsStatus as FeatureRequirementsStatus;
11 +use ElasticPress\Feature;
12 +use ElasticPress\FeatureRequirementsStatus;
13 +use ElasticPress\Features;
14 +use ElasticPress\Utils;
15 15
16 16 if ( ! defined( 'ABSPATH' ) ) {
17 17 exit; // Exit if accessed directly.
18 18 }
@@ -29,14 +29,10 @@
29 29 */
30 30 public function __construct() {
31 31 $this->slug = 'protected_content';
32 32
33 - $this->title = esc_html__( 'Protected Content', 'elasticpress' );
33 + $this->group = 'indexing-options';
34 34
35 - $this->summary = __( 'Optionally index all of your content, including private and unpublished content, to speed up searches and queries in places like the administrative dashboard.', 'elasticpress' );
36 -
37 - $this->docs_url = __( 'https://elasticpress.zendesk.com/hc/en-us/articles/360050447492-Configuring-ElasticPress-via-the-Plugin-Dashboard#protected-content', 'elasticpress' );
38 -
39 35 $this->requires_install_reindex = true;
40 36
41 37 $this->available_during_installation = true;
42 38
@@ -43,8 +39,23 @@
43 39 parent::__construct();
44 40 }
45 41
46 42 /**
43 + * Sets i18n strings.
44 + *
45 + * @return void
46 + * @since 5.2.0
47 + */
48 + public function set_i18n_strings(): void {
49 + $this->title = esc_html__( 'Protected Content', 'elasticpress' );
50 +
51 + $this->summary = '<p>' . __( 'Syncs unpublished content — including private, draft, and scheduled posts — improving load times in places like the administrative dashboard where WordPress needs to include protected content in a query.', 'elasticpress' ) . '</p>' .
52 + '<p><em>' . __( 'We recommend using a secured Elasticsearch setup, such as ElasticPress.io, to prevent potential exposure of content not intended for the public.', 'elasticpress' ) . '</em></p>';
53 +
54 + $this->docs_url = __( 'https://www.elasticpress.io/resources/articles/configuring-elasticpress-via-the-plugin-dashboard/#protected-content', 'elasticpress' );
55 + }
56 +
57 + /**
47 58 * Setup all feature filters
48 59 *
49 60 * @since 2.1
50 61 */
@@ -56,13 +67,16 @@
56 67 add_filter( 'ep_post_sync_args', [ $this, 'include_post_password' ], 10, 2 );
57 68 add_filter( 'ep_post_sync_args', [ $this, 'remove_fields_from_password_protected' ], 11, 2 );
58 69 add_filter( 'ep_search_post_return_args', [ $this, 'return_post_password' ] );
59 70 add_filter( 'ep_skip_autosave_sync', '__return_false' );
71 + add_filter( 'ep_pre_kill_sync_for_password_protected', [ $this, 'sync_password_protected' ], 10, 2 );
60 72
61 73 if ( is_admin() ) {
62 74 add_filter( 'ep_admin_wp_query_integration', '__return_true' );
63 75 add_action( 'pre_get_posts', [ $this, 'integrate' ] );
64 76 add_filter( 'ep_post_query_db_args', [ $this, 'query_password_protected_posts' ] );
77 + add_filter( 'ep_set_sort', [ $this, 'maybe_change_sort' ] );
78 + add_filter( 'ep_post_formatted_args', [ $this, 'filter_private_posts_for_current_user' ], 10, 2 );
65 79 }
66 80
67 81 if ( Features::factory()->get_registered_feature( 'comments' )->is_active() ) {
68 82 add_filter( 'ep_indexable_comment_status', [ $this, 'get_comment_statuses' ] );
@@ -170,12 +184,10 @@
170 184 }
171 185 }
172 186
173 187 $query->set( 'ep_integrate', true );
174 - } else {
175 - if ( ! empty( $supported_post_types[ $post_type ] ) ) {
188 + } elseif ( ! empty( $supported_post_types[ $post_type ] ) ) {
176 189 $query->set( 'ep_integrate', true );
177 - }
178 190 }
179 191
180 192 /**
181 193 * Remove articles weighting by date in admin.
@@ -271,9 +283,9 @@
271 283 return $post_args;
272 284 }
273 285
274 286 /**
275 - * Exclude proctected post from the frontend queries.
287 + * Exclude protected post from the frontend queries.
276 288 *
277 289 * @since 4.0.0
278 290 *
279 291 * @param array $formatted_args Formatted Elasticsearch query
@@ -352,28 +364,14 @@
352 364 }
353 365 }
354 366
355 367 $comment_query->query_vars['ep_integrate'] = true;
356 - } else {
357 - if ( in_array( $comment_type, $supported_comment_types, true ) ) {
368 + } elseif ( in_array( $comment_type, $supported_comment_types, true ) ) {
358 369 $comment_query->query_vars['ep_integrate'] = true;
359 - }
360 370 }
361 -
362 371 }
363 372
364 373 /**
365 - * Output feature box long
366 - *
367 - * @since 2.1
368 - */
369 - public function output_feature_box_long() {
370 - ?>
371 - <p><?php echo wp_kses_post( __( 'Securely indexes unpublished content—including private, draft, and scheduled posts —improving load times in places like the administrative dashboard where WordPress needs to include protected content in a query. <em>We recommend using a secured Elasticsearch setup, such as ElasticPress.io, to prevent potential exposure of content not intended for the public.</em>', 'elasticpress' ) ); ?></p>
372 - <?php
373 - }
374 -
375 - /**
376 374 * Fetches all post statuses we need to index
377 375 *
378 376 * @since 2.1
379 377 * @param array $statuses Post statuses array
@@ -404,9 +402,9 @@
404 402 * @since 2.2
405 403 * @return FeatureRequirementsStatus
406 404 */
407 405 public function requirements_status() {
408 - $status = new FeatureRequirementsStatus( 1 );
406 + $status = new FeatureRequirementsStatus( 1, null, $this );
409 407
410 408 if ( ! Utils\is_epio() ) {
411 409 $status->message = __( "You aren't using <a href='https://elasticpress.io'>ElasticPress.io</a> so we can't be sure your Elasticsearch instance is secure.", 'elasticpress' );
412 410 }
@@ -411,6 +409,168 @@
411 409 $status->message = __( "You aren't using <a href='https://elasticpress.io'>ElasticPress.io</a> so we can't be sure your Elasticsearch instance is secure.", 'elasticpress' );
412 410 }
413 411
414 412 return $status;
413 + }
414 +
415 + /**
416 + * Bypass the default check for password protected posts.
417 + *
418 + * @since 4.6.0
419 + * @param null|bool $new_skip Short-circuit flag
420 + * @param bool $skip Current value of $skip
421 + * @return bool
422 + */
423 + public function sync_password_protected( $new_skip, bool $skip ): bool {
424 + return $skip;
425 + }
426 +
427 + /**
428 + * Maybe change the sort order for the WP Dashboard.
429 + *
430 + * If the admin user has enabled the setting to use the default WordPress sort order,
431 + * we will change the sort order to (somewhat) match the default WP behavior.
432 + *
433 + * @since 5.1.4
434 + *
435 + * @param array $default_sort The previous value of the `ep_set_sort` filter
436 + * @return array
437 + */
438 + public function maybe_change_sort( $default_sort ) {
439 + if ( ! function_exists( '\get_current_screen' ) ) {
440 + return $default_sort;
441 + }
442 +
443 + $screen = get_current_screen();
444 + if ( empty( $screen ) || 'edit' !== $screen->base ) {
445 + return $default_sort;
446 + }
447 +
448 + if ( ! $this->get_setting( 'use_default_wp_sort' ) ) {
449 + return $default_sort;
450 + }
451 +
452 + return [
453 + [ 'post_date' => [ 'order' => 'desc' ] ],
454 + [ 'post_title.sortable' => [ 'order' => 'asc' ] ],
455 + ];
456 + }
457 +
458 + /**
459 + * Filter private posts for current user.
460 + *
461 + * Private statuses (including custom ones from `get_post_stati( [ 'private' => true ] )`)
462 + * are never added to the all-authors clause. Users without `read_private_posts` can
463 + * still match their own private posts via the author-restricted clause below. Merging
464 + * a requested private status into the all-authors list would otherwise expose other
465 + * authors' private posts.
466 + *
467 + * @param array $formatted_args Formatted Elasticsearch query
468 + * @param array $args Query variables
469 + *
470 + * @return array
471 + */
472 + public function filter_private_posts_for_current_user( $formatted_args, $args ): array {
473 + $post_statuses = ! empty( $args['post_status'] ) ? $args['post_status'] : [];
474 + $post_statuses = is_string( $post_statuses ) ? explode( ',', $post_statuses ) : $post_statuses;
475 + $post_types = ! empty( $args['post_type'] ) ? (array) $args['post_type'] : [];
476 + $valid_post_types = array_filter( $post_types, 'post_type_exists' );
477 + $private_statuses = get_post_stati( [ 'private' => true ] );
478 +
479 + $base_statuses = array_merge(
480 + get_post_stati( [ 'public' => true ] ),
481 + get_post_stati(
482 + [
483 + 'protected' => true,
484 + 'show_in_admin_all_list' => true,
485 + ]
486 + ),
487 + $post_statuses
488 + );
489 + // Requested private statuses belong only in the capability or author clauses.
490 + $base_statuses = array_unique( array_diff( $base_statuses, $private_statuses ) );
491 +
492 + $post_types_with_capability = [];
493 + $post_types_without_capability = [];
494 +
495 + foreach ( $valid_post_types as $post_type ) {
496 + $post_type_object = get_post_type_object( $post_type );
497 +
498 + if ( empty( $post_type_object ) || empty( $post_type_object->cap->read_private_posts ) ) {
499 + continue;
500 + }
501 +
502 + $read_private_cap = $post_type_object->cap->read_private_posts;
503 +
504 + if ( current_user_can( $read_private_cap ) ) {
505 + $post_types_with_capability[] = $post_type;
506 + } else {
507 + $post_types_without_capability[] = $post_type;
508 + }
509 + }
510 +
511 + $should_clauses = [];
512 +
513 + if ( ! empty( $post_types_with_capability ) ) {
514 + $all_statuses = array_unique( array_merge( $base_statuses, $private_statuses ) );
515 +
516 + $should_clauses[] = [
517 + 'bool' => [
518 + 'must' => [
519 + [ 'terms' => [ 'post_type.raw' => array_values( $post_types_with_capability ) ] ],
520 + [ 'terms' => [ 'post_status' => array_values( $all_statuses ) ] ],
521 + ],
522 + ],
523 + ];
524 + }
525 +
526 + if ( ! empty( $post_types_without_capability ) ) {
527 + $should_clauses[] = [
528 + 'bool' => [
529 + 'must' => [
530 + [ 'terms' => [ 'post_type.raw' => array_values( $post_types_without_capability ) ] ],
531 + [ 'terms' => [ 'post_status' => array_values( $base_statuses ) ] ],
532 + ],
533 + ],
534 + ];
535 +
536 + if ( ! empty( $private_statuses ) ) {
537 + $should_clauses[] = [
538 + 'bool' => [
539 + 'must' => [
540 + [ 'terms' => [ 'post_type.raw' => array_values( $post_types_without_capability ) ] ],
541 + [ 'terms' => [ 'post_status' => array_values( $private_statuses ) ] ],
542 + [ 'term' => [ 'post_author.id' => get_current_user_id() ] ],
543 + ],
544 + ],
545 + ];
546 + }
547 + }
548 +
549 + if ( ! empty( $should_clauses ) ) {
550 + $formatted_args['post_filter']['bool']['should'] = array_merge(
551 + $formatted_args['post_filter']['bool']['should'] ?? [],
552 + $should_clauses
553 + );
554 + $formatted_args['post_filter']['bool']['minimum_should_match'] = 1;
555 + }
556 +
557 + return $formatted_args;
558 + }
559 +
560 + /**
561 + * Set the `settings_schema` attribute
562 + *
563 + * @since 5.1.4
564 + */
565 + protected function set_settings_schema() {
566 + $this->settings_schema = [
567 + [
568 + 'default' => '0',
569 + 'key' => 'use_default_wp_sort',
570 + 'help' => __( 'Enable to use WordPress default sort for searches inside the WP Dashboard.', 'elasticpress' ),
571 + 'label' => __( 'Use default WordPress sort on the WP Dashboard', 'elasticpress' ),
572 + 'type' => 'checkbox',
573 + ],
574 + ];
415 575 }
416 576 }