| @@ -6,13 +6,13 @@ | ||
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | namespace ElasticPress\Feature\Documents; |
| 9 | 9 | |
| 10 | -use ElasticPress\Feature as Feature; | |
| 11 | -use ElasticPress\Elasticsearch as Elasticsearch; | |
| 12 | -use ElasticPress\FeatureRequirementsStatus as FeatureRequirementsStatus; | |
| 13 | -use ElasticPress\Indexables as Indexables; | |
| 14 | -use ElasticPress\Utils as Utils; | |
| 10 | +use ElasticPress\Elasticsearch; | |
| 11 | +use ElasticPress\Feature; | |
| 12 | +use ElasticPress\FeatureRequirementsStatus; | |
| 13 | +use ElasticPress\Indexables; | |
| 14 | +use ElasticPress\Utils; | |
| 15 | 15 | |
| 16 | 16 | /** |
| 17 | 17 | * Documents feature class. |
| 18 | 18 | */ |
| @@ -24,17 +24,27 @@ | ||
| 24 | 24 | */ |
| 25 | 25 | public function __construct() { |
| 26 | 26 | $this->slug = 'documents'; |
| 27 | 27 | |
| 28 | - $this->title = esc_html__( 'Documents', 'elasticpress' ); | |
| 28 | + $this->group = 'indexing-options'; | |
| 29 | 29 | |
| 30 | - $this->summary = __( 'Indexes text inside of popular file types, and adds those files types to search results.', 'elasticpress' ); | |
| 30 | + $this->requires_install_reindex = false; | |
| 31 | 31 | |
| 32 | - $this->docs_url = __( 'https://elasticpress.zendesk.com/hc/en-us/articles/360050447492-Configuring-ElasticPress-via-the-Plugin-Dashboard#documents', 'elasticpress' ); | |
| 32 | + parent::__construct(); | |
| 33 | + } | |
| 33 | 34 | |
| 34 | - $this->requires_install_reindex = false; | |
| 35 | + /** | |
| 36 | + * Sets i18n strings. | |
| 37 | + * | |
| 38 | + * @return void | |
| 39 | + * @since 5.2.0 | |
| 40 | + */ | |
| 41 | + public function set_i18n_strings(): void { | |
| 42 | + $this->title = esc_html__( 'Documents', 'elasticpress' ); | |
| 35 | 43 | |
| 36 | - parent::__construct(); | |
| 44 | + $this->summary = '<p>' . __( 'Website search results will include popular document file types, using file names as well as their content. Supported file types include: ppt, pptx, doc, docx, xls, xlsx, pdf, csv, txt.', 'elasticpress' ) . '</p>'; | |
| 45 | + | |
| 46 | + $this->docs_url = __( 'https://www.elasticpress.io/resources/articles/configuring-elasticpress-via-the-plugin-dashboard/#documents', 'elasticpress' ); | |
| 37 | 47 | } |
| 38 | 48 | |
| 39 | 49 | /** |
| 40 | 50 | * Setup feature filters |
| @@ -59,8 +69,10 @@ | ||
| 59 | 69 | add_filter( 'ep_term_suggest_post_status', [ $this, 'filter_autosuggest_post_status' ] ); |
| 60 | 70 | |
| 61 | 71 | add_filter( 'ep_weighting_fields_for_post_type', [ $this, 'filter_weightable_fields_for_post_type' ], 10, 2 ); |
| 62 | 72 | add_filter( 'ep_weighting_default_post_type_weights', [ $this, 'filter_attachment_post_type_weights' ], 10, 2 ); |
| 73 | + | |
| 74 | + add_filter( 'ep_ajax_wp_query_integration', [ $this, 'maybe_enable_ajax_wp_query_integration' ] ); | |
| 63 | 75 | } |
| 64 | 76 | |
| 65 | 77 | /** |
| 66 | 78 | * Add attachment post type to be searched. We used to search these by default. |
| @@ -95,9 +107,9 @@ | ||
| 95 | 107 | * @since 2.3 |
| 96 | 108 | * @return array |
| 97 | 109 | */ |
| 98 | 110 | public function attachments_mapping( $mapping ) { |
| 99 | - if ( version_compare( Elasticsearch::factory()->get_elasticsearch_version(), '7.0', '<' ) ) { | |
| 111 | + if ( version_compare( (string) Elasticsearch::factory()->get_elasticsearch_version(), '7.0', '<' ) ) { | |
| 100 | 112 | $mapping['mappings']['post']['properties']['attachments'] = array( |
| 101 | 113 | 'type' => 'object', |
| 102 | 114 | ); |
| 103 | 115 | } else { |
| @@ -109,12 +121,9 @@ | ||
| 109 | 121 | return $mapping; |
| 110 | 122 | } |
| 111 | 123 | |
| 112 | 124 | /** |
| 113 | - * This is some complex logic to handle the front end search query. If we have a search query, | |
| 114 | - * add the attachment post type to post_type and inherit to post_status. If post_status is not set, | |
| 115 | - * we assume publish/inherit is wanted. post_type should always be set. We also add allowed mime types. | |
| 116 | - * If mime types are already set, append. | |
| 125 | + * Handle the search query | |
| 117 | 126 | * |
| 118 | 127 | * @param WP_Query $query WP_Query to modify to search. |
| 119 | 128 | * @since 2.3 |
| 120 | 129 | */ |
| @@ -122,59 +131,23 @@ | ||
| 122 | 131 | if ( ! Utils\is_integrated_request( $this->slug, [ 'public', 'ajax' ] ) ) { |
| 123 | 132 | return; |
| 124 | 133 | } |
| 125 | 134 | |
| 135 | + // If not a search, return. | |
| 126 | 136 | $s = $query->get( 's', false ); |
| 127 | - | |
| 128 | 137 | if ( empty( $s ) ) { |
| 129 | 138 | return; |
| 130 | 139 | } |
| 131 | 140 | |
| 132 | - $post_status = $query->get( 'post_status', [] ); | |
| 133 | - $post_type = $query->get( 'post_type', [] ); | |
| 134 | - $mime_types = $query->get( 'post_mime_type', [] ); | |
| 135 | - | |
| 136 | - if ( ! empty( $post_type ) ) { | |
| 137 | - if ( 'any' !== $post_type ) { | |
| 138 | - if ( is_string( $post_type ) ) { | |
| 139 | - $post_type = explode( ' ', $post_type ); | |
| 140 | - $post_type[] = 'attachment'; | |
| 141 | - | |
| 142 | - $query->set( 'post_type', array_unique( $post_type ) ); | |
| 143 | - } | |
| 144 | - } | |
| 141 | + // Return if attachments are not involved in the query. | |
| 142 | + // If post_type is empty, attachments will be included automatically. | |
| 143 | + $post_type = (array) $query->get( 'post_type', [] ); | |
| 144 | + if ( ! empty( $post_type ) && ! in_array( 'attachment', $post_type, true ) ) { | |
| 145 | + return; | |
| 145 | 146 | } |
| 146 | 147 | |
| 147 | - if ( empty( $post_status ) ) { | |
| 148 | - $post_status = array_values( | |
| 149 | - get_post_stati( | |
| 150 | - [ | |
| 151 | - 'public' => true, | |
| 152 | - 'exclude_from_search' => false, | |
| 153 | - ] | |
| 154 | - ) | |
| 155 | - ); | |
| 156 | - | |
| 157 | - // Add inherit for documents | |
| 158 | - $post_status[] = 'inherit'; | |
| 159 | - } else { | |
| 160 | - if ( is_string( $post_status ) ) { | |
| 161 | - $post_status = explode( ' ', $post_status ); | |
| 162 | - } | |
| 163 | - | |
| 164 | - $post_status[] = 'inherit'; | |
| 165 | - } | |
| 166 | - | |
| 167 | - $query->set( 'post_status', array_unique( $post_status ) ); | |
| 168 | - | |
| 169 | - if ( ! empty( $mime_types ) && is_string( $mime_types ) ) { | |
| 170 | - $mime_types = explode( ' ', $mime_types ); | |
| 171 | - } | |
| 172 | - | |
| 173 | - $mime_types = array_merge( $mime_types, $this->get_allowed_ingest_mime_types() ); | |
| 174 | - $mime_types[] = ''; // This let's us query non-attachments as well as attachments. | |
| 175 | - | |
| 176 | - $query->set( 'post_mime_type', array_unique( array_values( $mime_types ) ) ); | |
| 148 | + $this->maybe_set_post_status( $query ); | |
| 149 | + $this->maybe_set_mime_type( $query ); | |
| 177 | 150 | } |
| 178 | 151 | |
| 179 | 152 | /** |
| 180 | 153 | * Change Elasticsearch request path if processing attachment |
| @@ -189,27 +162,31 @@ | ||
| 189 | 162 | if ( 'post' !== $type ) { |
| 190 | 163 | return $path; |
| 191 | 164 | } |
| 192 | 165 | |
| 193 | - if ( 'attachment' === $post['post_type'] ) { | |
| 194 | - if ( ! empty( $post['attachments'][0]['data'] ) && isset( $post['post_mime_type'] ) && in_array( $post['post_mime_type'], $this->get_allowed_ingest_mime_types(), true ) ) { | |
| 195 | - $index = Indexables::factory()->get( 'post' )->get_index_name(); | |
| 166 | + if ( 'attachment' !== $post['post_type'] ) { | |
| 167 | + return $path; | |
| 168 | + } | |
| 196 | 169 | |
| 197 | - /** | |
| 198 | - * Filter documents pipeline ID | |
| 199 | - * | |
| 200 | - * @hook ep_documents_pipeline_id | |
| 201 | - * @param {string} $id Pipeline ID | |
| 202 | - * @return {string} new ID | |
| 203 | - */ | |
| 204 | - $pipeline_id = apply_filters( 'ep_documents_pipeline_id', Indexables::factory()->get( 'post' )->get_index_name() . '-attachment' ); | |
| 170 | + if ( empty( $post['attachments'][0]['data'] ) || ! isset( $post['post_mime_type'] ) || ! in_array( $post['post_mime_type'], $this->get_allowed_ingest_mime_types(), true ) ) { | |
| 171 | + return $path; | |
| 172 | + } | |
| 205 | 173 | |
| 206 | - if ( version_compare( Elasticsearch::factory()->get_elasticsearch_version(), '7.0', '<' ) ) { | |
| 207 | - $path = trailingslashit( $index ) . 'post/' . $post['ID'] . '?pipeline=' . $pipeline_id; | |
| 208 | - } else { | |
| 209 | - $path = trailingslashit( $index ) . '_doc/' . $post['ID'] . '?pipeline=' . $pipeline_id; | |
| 210 | - } | |
| 211 | - } | |
| 174 | + $index = Indexables::factory()->get( 'post' )->get_index_name(); | |
| 175 | + | |
| 176 | + /** | |
| 177 | + * Filter documents pipeline ID | |
| 178 | + * | |
| 179 | + * @hook ep_documents_pipeline_id | |
| 180 | + * @param {string} $id Pipeline ID | |
| 181 | + * @return {string} new ID | |
| 182 | + */ | |
| 183 | + $pipeline_id = apply_filters( 'ep_documents_pipeline_id', Indexables::factory()->get( 'post' )->get_index_name() . '-attachment' ); | |
| 184 | + | |
| 185 | + if ( version_compare( (string) Elasticsearch::factory()->get_elasticsearch_version(), '7.0', '<' ) ) { | |
| 186 | + $path = trailingslashit( $index ) . 'post/' . $post['ID'] . '?pipeline=' . $pipeline_id; | |
| 187 | + } else { | |
| 188 | + $path = trailingslashit( $index ) . '_doc/' . $post['ID'] . '?pipeline=' . $pipeline_id; | |
| 212 | 189 | } |
| 213 | 190 | |
| 214 | 191 | return $path; |
| 215 | 192 | } |
| @@ -326,9 +303,9 @@ | ||
| 326 | 303 | * @since 2.3 |
| 327 | 304 | * @return mixed |
| 328 | 305 | */ |
| 329 | 306 | public function requirements_status() { |
| 330 | - $status = new FeatureRequirementsStatus( 1 ); | |
| 307 | + $status = new FeatureRequirementsStatus( 1, null, $this ); | |
| 331 | 308 | |
| 332 | 309 | if ( empty( Elasticsearch::factory()->get_elasticsearch_version( false ) ) ) { |
| 333 | 310 | return $status; |
| 334 | 311 | } |
| @@ -342,9 +319,9 @@ | ||
| 342 | 319 | $status->code = 2; |
| 343 | 320 | $status->message[] = __( 'The <a href="https://www.elastic.co/guide/en/elasticsearch/plugins/master/ingest-attachment.html">Ingest Attachment plugin</a> for Elasticsearch is not installed. To get the most out of ElasticPress, without the hassle of Elasticsearch management, check out <a href="https://elasticpress.io">ElasticPress.io</a> hosting.', 'elasticpress' ); |
| 344 | 321 | } else { |
| 345 | 322 | $status->code = 1; |
| 346 | - $status->message[] = __( 'This feature modifies the default user experience for your visitors by adding popular document file types to search results. <strong>All supported documents</strong> uploaded to your media library will appear in search results.', 'elasticpress' ); | |
| 323 | + $status->message[] = __( 'This feature modifies the default user experience for your visitors by adding popular document file types to search results. All supported documents (PDFs and Microsoft Office) uploaded to your media library will appear in search results.', 'elasticpress' ); | |
| 347 | 324 | } |
| 348 | 325 | |
| 349 | 326 | return $status; |
| 350 | 327 | } |
| @@ -349,19 +326,8 @@ | ||
| 349 | 326 | return $status; |
| 350 | 327 | } |
| 351 | 328 | |
| 352 | 329 | /** |
| 353 | - * Output feature box long | |
| 354 | - * | |
| 355 | - * @since 2.3 | |
| 356 | - */ | |
| 357 | - public function output_feature_box_long() { | |
| 358 | - ?> | |
| 359 | - <p><?php esc_html_e( 'Website search results will include popular document file types, using file names as well as their content. Supported file types include: ppt, pptx, doc, docx, xls, xlsx, pdf.', 'elasticpress' ); ?></p> | |
| 360 | - <?php | |
| 361 | - } | |
| 362 | - | |
| 363 | - /** | |
| 364 | 330 | * Make sure to create pipeline after activation |
| 365 | 331 | * |
| 366 | 332 | * @since 2.6 |
| 367 | 333 | */ |
| @@ -437,8 +403,10 @@ | ||
| 437 | 403 | 'xls' => 'application/vnd.ms-excel', |
| 438 | 404 | 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', |
| 439 | 405 | 'doc' => 'application/msword', |
| 440 | 406 | 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
| 407 | + 'csv' => 'text/csv', | |
| 408 | + 'txt' => 'text/plain', | |
| 441 | 409 | ) |
| 442 | 410 | ); |
| 443 | 411 | } |
| 444 | 412 | |
| @@ -515,6 +483,119 @@ | ||
| 515 | 483 | ]; |
| 516 | 484 | } |
| 517 | 485 | |
| 518 | 486 | return $weights; |
| 487 | + } | |
| 488 | + | |
| 489 | + /** | |
| 490 | + * Enable integration if we are in the media library admin ajax search | |
| 491 | + * | |
| 492 | + * @param bool $integrate Whether it should be integrated or not | |
| 493 | + * @return bool | |
| 494 | + */ | |
| 495 | + public function maybe_enable_ajax_wp_query_integration( $integrate ) { | |
| 496 | + return ( $this->is_admin_ajax_search() && $this->is_media_library_ajax_enabled() ) ? true : $integrate; | |
| 497 | + } | |
| 498 | + | |
| 499 | + /** | |
| 500 | + * If post_status is not set, we assume publish/inherit is wanted. | |
| 501 | + * | |
| 502 | + * @param WP_Query $query WP_Query to modify to search. | |
| 503 | + * @return void | |
| 504 | + */ | |
| 505 | + protected function maybe_set_post_status( $query ) { | |
| 506 | + $post_status = $query->get( 'post_status', [] ); | |
| 507 | + | |
| 508 | + if ( empty( $post_status ) ) { | |
| 509 | + $post_status = array_values( | |
| 510 | + get_post_stati( | |
| 511 | + [ | |
| 512 | + 'public' => true, | |
| 513 | + 'exclude_from_search' => false, | |
| 514 | + ] | |
| 515 | + ) | |
| 516 | + ); | |
| 517 | + | |
| 518 | + // Add inherit for documents | |
| 519 | + $post_status[] = 'inherit'; | |
| 520 | + } else { | |
| 521 | + if ( is_string( $post_status ) ) { | |
| 522 | + $post_status = explode( ' ', $post_status ); | |
| 523 | + } | |
| 524 | + | |
| 525 | + $post_status[] = 'inherit'; | |
| 526 | + } | |
| 527 | + | |
| 528 | + $query->set( 'post_status', array_unique( $post_status ) ); | |
| 529 | + } | |
| 530 | + | |
| 531 | + /** | |
| 532 | + * Add allowed mime types. If mime types are already set, append. | |
| 533 | + * | |
| 534 | + * @param WP_Query $query WP_Query to modify to search. | |
| 535 | + * @return void | |
| 536 | + */ | |
| 537 | + protected function maybe_set_mime_type( $query ) { | |
| 538 | + /** | |
| 539 | + * Mime types | |
| 540 | + * | |
| 541 | + * By default, we do not restrict results by mime types in the Media Library AJAX search, | |
| 542 | + * otherwise images, and SVGs, for example, will not be returned. | |
| 543 | + */ | |
| 544 | + $should_set_mime_types = ! $this->is_admin_ajax_search() || ! $this->is_media_library_ajax_enabled(); | |
| 545 | + | |
| 546 | + /** | |
| 547 | + * Filter whether mime type restriction should be applied to the current WP Query | |
| 548 | + * | |
| 549 | + * @since 5.1.0 | |
| 550 | + * @hook ep_documents_wp_query_set_mime_types | |
| 551 | + * @param {bool} $should_set Whether to restrict this query with mime types or not | |
| 552 | + * @param {WP_Query} $query WP Query object | |
| 553 | + * @return {bool} New value | |
| 554 | + */ | |
| 555 | + $should_set_mime_types = apply_filters( 'ep_documents_wp_query_set_mime_types', $should_set_mime_types, $query ); | |
| 556 | + | |
| 557 | + if ( ! $should_set_mime_types ) { | |
| 558 | + return; | |
| 559 | + } | |
| 560 | + | |
| 561 | + // Set mime types | |
| 562 | + $mime_types = $query->get( 'post_mime_type', [] ); | |
| 563 | + | |
| 564 | + if ( ! empty( $mime_types ) && is_string( $mime_types ) ) { | |
| 565 | + $mime_types = explode( ' ', $mime_types ); | |
| 566 | + } | |
| 567 | + | |
| 568 | + $mime_types = array_merge( $mime_types, $this->get_allowed_ingest_mime_types() ); | |
| 569 | + $mime_types[] = ''; // This let's us query non-attachments as well as attachments. | |
| 570 | + | |
| 571 | + $query->set( 'post_mime_type', array_unique( array_values( $mime_types ) ) ); | |
| 572 | + } | |
| 573 | + | |
| 574 | + /** | |
| 575 | + * Whether the feature should work on the Media Library admin ajax request | |
| 576 | + * | |
| 577 | + * @return boolean | |
| 578 | + */ | |
| 579 | + protected function is_media_library_ajax_enabled() { | |
| 580 | + $protected_content = \ElasticPress\Features::factory()->get_registered_feature( 'protected_content' ); | |
| 581 | + | |
| 582 | + /** | |
| 583 | + * Filter whether the feature should work on the Media Library admin ajax request | |
| 584 | + * | |
| 585 | + * @since 5.1.0 | |
| 586 | + * @hook ep_documents_media_library_ajax_enabled | |
| 587 | + * @param {bool} $enabled Whether to integrate or not | |
| 588 | + * @return {bool} New value | |
| 589 | + */ | |
| 590 | + return apply_filters( 'ep_documents_media_library_ajax_enabled', $protected_content->is_active() ); | |
| 591 | + } | |
| 592 | + | |
| 593 | + /** | |
| 594 | + * Whether we are in the admin ajax search request for the media library | |
| 595 | + * | |
| 596 | + * @return boolean | |
| 597 | + */ | |
| 598 | + protected function is_admin_ajax_search() { | |
| 599 | + return wp_doing_ajax() && isset( $_REQUEST['action'] ) && 'query-attachments' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 519 | 600 | } |
| 520 | 601 | } |