PluginProbe
ElasticPress / 4.2.0
ElasticPress v4.2.0
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
elasticpress / includes / classes / Indexable / User / User.php

User.php in ElasticPress 4.2.0, at includes/classes/Indexable/User/User.php

1,000 lines 24.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * User indexable
4 *
5 * @since 3.0
6 * @package elasticpress
7 */
8
9 namespace ElasticPress\Indexable\User;
10
11 use ElasticPress\Indexable as Indexable;
12 use ElasticPress\Elasticsearch as Elasticsearch;
13 use \WP_User_Query as WP_User_Query;
14 use ElasticPress\Utils as Utils;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // Exit if accessed directly.
18 }
19
20 /**
21 * User indexable class
22 */
23 class User extends Indexable {
24
25 /**
26 * We only need one user index
27 *
28 * @var boolean
29 * @since 3.0
30 */
31 public $global = true;
32
33 /**
34 * Indexable slug
35 *
36 * @var string
37 * @since 3.0
38 */
39 public $slug = 'user';
40
41 /**
42 * Create indexable and setup dependencies
43 *
44 * @since 3.0
45 */
46 public function __construct() {
47 $this->labels = [
48 'plural' => esc_html__( 'Users', 'elasticpress' ),
49 'singular' => esc_html__( 'User', 'elasticpress' ),
50 ];
51
52 $this->sync_manager = new SyncManager( $this->slug );
53 $this->query_integration = new QueryIntegration( $this->slug );
54 }
55
56 /**
57 * Format query vars into ES query
58 *
59 * @param array $query_vars WP_User_Query args.
60 * @param WP_User_Query $query User query object
61 * @since 3.0
62 * @return array
63 */
64 public function format_args( $query_vars, $query ) {
65 global $wpdb;
66
67 /**
68 * Handle `number` query var
69 */
70 if ( ! empty( $query_vars['number'] ) ) {
71 $number = (int) $query_vars['number'];
72
73 // ES have a maximum size allowed so we have to convert "-1" to a maximum size.
74 if ( -1 === $number ) {
75 /**
76 * Set the maximum results window size.
77 *
78 * The request will return a HTTP 500 Internal Error if the size of the
79 * request is larger than the [index.max_result_window] parameter in ES.
80 * See the scroll api for a more efficient way to request large data sets.
81 *
82 * @return int The max results window size.
83 *
84 * @since 2.3.0
85 */
86
87 /**
88 * Filter max result size if set to -1
89 *
90 * @hook ep_max_results_window
91 * @param {int} $window Max result window
92 * @return {int} New window
93 */
94 $number = apply_filters( 'ep_max_results_window', 10000 );
95 }
96 } else {
97 /**
98 * Filter max result size if set to -1
99 *
100 * @hook ep_max_results_window
101 * @param {int} $window Max result window
102 * @return {int} New window
103 */
104 $number = apply_filters( 'ep_max_results_window', 10000 );
105 }
106
107 $formatted_args = [
108 'from' => 0,
109 'size' => $number,
110 ];
111
112 $filter = [
113 'bool' => [
114 'must' => [],
115 ],
116 ];
117
118 $use_filters = false;
119
120 /**
121 * Support `blog_id` query arg
122 */
123 $blog_id = false;
124 if ( isset( $query_vars['blog_id'] ) ) {
125 $blog_id = (int) $query_vars['blog_id'];
126 }
127
128 /**
129 * Support `role` query arg
130 */
131 if ( ! empty( $blog_id ) ) {
132 // If a blog id is set, we will apply at least one filter for roles.
133 $use_filters = true;
134
135 // If there are no specific roles named, make sure the user is a member of the site.
136 if ( empty( $query_vars['role'] ) && empty( $query_vars['role__in'] ) && empty( $query_vars['role__not_in'] ) ) {
137 $filter['bool']['must'][] = array(
138 'exists' => array(
139 'field' => 'capabilities.' . $blog_id . '.roles',
140 ),
141 );
142 /**
143 * EP versions prior to 4.1.0 set non-existent roles as `0`.
144 */
145 $filter['bool']['must_not'][] = array(
146 'term' => array(
147 'capabilities.' . $blog_id . '.roles' => 0,
148 ),
149 );
150 } elseif ( ! empty( $query_vars['role'] ) ) {
151 $roles = (array) $query_vars['role'];
152
153 foreach ( $roles as $role ) {
154 $filter['bool']['must'][] = array(
155 'terms' => array(
156 'capabilities.' . $blog_id . '.roles' => [
157 strtolower( $role ),
158 ],
159 ),
160 );
161 }
162 } else {
163 if ( ! empty( $query_vars['role__in'] ) ) {
164 $roles_in = (array) $query_vars['role__in'];
165
166 $roles_in = array_map( 'strtolower', $roles_in );
167
168 $filter['bool']['must'][] = array(
169 'terms' => array(
170 'capabilities.' . $blog_id . '.roles' => $roles_in,
171 ),
172 );
173 }
174
175 if ( ! empty( $query_vars['role__not_in'] ) ) {
176 $roles_not_in = (array) $query_vars['role__not_in'];
177
178 foreach ( $roles_not_in as $role ) {
179 $filter['bool']['must_not'][] = array(
180 'terms' => array(
181 'capabilities.' . $blog_id . '.roles' => [
182 strtolower( $role ),
183 ],
184 ),
185 );
186 }
187 }
188 }
189 }
190
191 $meta_queries = [];
192
193 /**
194 * Support `meta_key`, `meta_value`, and `meta_compare`
195 */
196 if ( ! empty( $query_vars['meta_key'] ) ) {
197 $meta_query_array = [
198 'key' => $query_vars['meta_key'],
199 ];
200
201 if ( isset( $query_vars['meta_value'] ) ) {
202 $meta_query_array['value'] = $query_vars['meta_value'];
203 }
204
205 if ( isset( $query_vars['meta_compare'] ) ) {
206 $meta_query_array['compare'] = $query_vars['meta_compare'];
207 }
208
209 $meta_queries[] = $meta_query_array;
210 }
211
212 /**
213 * 'meta_query' arg support.
214 */
215 if ( ! empty( $query_vars['meta_query'] ) ) {
216 $meta_queries = array_merge( $meta_queries, $query_vars['meta_query'] );
217 }
218
219 if ( ! empty( $meta_queries ) ) {
220 $filter['bool']['must'][] = $this->build_meta_query( $meta_queries );
221
222 $use_filters = true;
223 }
224
225 /**
226 * Support `fields` query var.
227 */
228 if ( isset( $query_vars['fields'] ) && 'all' !== $query_vars['fields'] && 'all_with_meta' !== $query_vars['fields'] ) {
229 $fields = (array) $query_vars['fields'];
230 $id_position = array_search( 'id', $fields, true );
231 if ( false !== $id_position ) {
232 $fields[ $id_position ] = 'ID';
233 }
234 $formatted_args['_source'] = [
235 'includes' => $fields,
236 ];
237 }
238
239 /**
240 * Support `nicename` query var
241 */
242 if ( ! empty( $query_vars['nicename'] ) ) {
243 $filter['bool']['must'][] = array(
244 'terms' => array(
245 'user_nicename' => [
246 $query_vars['nicename'],
247 ],
248 ),
249 );
250
251 $use_filters = true;
252 }
253
254 /**
255 * Support `nicename` query var
256 */
257 if ( ! empty( $query_vars['nicename__not_in'] ) ) {
258 $filter['bool']['must'][] = [
259 'bool' => [
260 'must_not' => [
261 [
262 'terms' => [
263 'user_nicename' => (array) $query_vars['nicename__not_in'],
264 ],
265 ],
266 ],
267 ],
268 ];
269
270 $use_filters = true;
271 }
272
273 /**
274 * Support `nicename__in` query var
275 */
276 if ( ! empty( $query_vars['nicename__in'] ) ) {
277 $filter['bool']['must'][] = array(
278 'terms' => array(
279 'user_nicename' => (array) $query_vars['nicename__in'],
280 ),
281 );
282
283 $use_filters = true;
284 }
285
286 /**
287 * Support `login` query var
288 */
289 if ( ! empty( $query_vars['login'] ) ) {
290 $filter['bool']['must'][] = array(
291 'terms' => array(
292 'user_login' => [
293 $query_vars['login'],
294 ],
295 ),
296 );
297
298 $use_filters = true;
299 }
300
301 /**
302 * Support `login__in` query var
303 */
304 if ( ! empty( $query_vars['login__in'] ) ) {
305 $filter['bool']['must'][] = array(
306 'terms' => array(
307 'user_login' => (array) $query_vars['login__in'],
308 ),
309 );
310
311 $use_filters = true;
312 }
313
314 /**
315 * Support `login__not_in` query var
316 */
317 if ( ! empty( $query_vars['login__not_in'] ) ) {
318 $filter['bool']['must'][] = [
319 'bool' => [
320 'must_not' => [
321 [
322 'terms' => [
323 'user_login' => (array) $query_vars['login__not_in'],
324 ],
325 ],
326 ],
327 ],
328 ];
329
330 $use_filters = true;
331 }
332
333 /**
334 * Handle `offset` and `paged` query vars. Paged takes priority if both are set.
335 */
336 if ( isset( $query_vars['offset'] ) ) {
337 $formatted_args['from'] = (int) $query_vars['offset'];
338 }
339
340 if ( isset( $query_vars['paged'] ) && $query_vars['paged'] > 1 ) {
341 $formatted_args['from'] = $number * ( $query_vars['paged'] - 1 );
342 }
343
344 /**
345 * Support `include` parameter
346 */
347 if ( ! empty( $query_vars['include'] ) ) {
348 $filter['bool']['must'][] = [
349 'bool' => [
350 'must' => [
351 'terms' => [
352 'ID' => array_values( (array) $query_vars['include'] ),
353 ],
354 ],
355 ],
356 ];
357
358 $use_filters = true;
359 }
360
361 /**
362 * Support `exclude` parameter
363 */
364 if ( ! empty( $query_vars['exclude'] ) ) {
365 $filter['bool']['must'][] = [
366 'bool' => [
367 'must_not' => [
368 'terms' => [
369 'ID' => array_values( (array) $query_vars['exclude'] ),
370 ],
371 ],
372 ],
373 ];
374
375 $use_filters = true;
376 }
377
378 /**
379 * Need to support a few more params
380 *
381 * @todo Support the following parameters:
382 *
383 * $who
384 * $has_published_posts
385 */
386
387 /**
388 * Handle `search` query_var
389 */
390 if ( ! empty( $query_vars['search'] ) ) {
391
392 /**
393 * Remove *'s from beginning and end of user search string'
394 *
395 * @hook ep_user_search_remove_wildcards
396 * @param {boolean} $remove True to remove
397 * @param {array} $query Current query
398 * @param {array} $query_vars Query variables
399 * @since 3.4
400 * @return {boolean}
401 */
402 if ( apply_filters( 'ep_user_search_remove_wildcards', true, $query, $query_vars ) ) {
403 $query_vars['search'] = trim( $query_vars['search'], '*' );
404 }
405
406 $search_fields = ( ! empty( $query_vars['search_columns'] ) ) ? $query_vars['search_columns'] : [];
407
408 if ( ! empty( $query_vars['search_fields'] ) ) {
409 $search_fields = array_merge( $search_fields, $query_vars['search_fields'] );
410 }
411
412 /**
413 * Handle `search_fields` query var and `search_columns`. search_columns is a bit too
414 * simplistic for our needs since we want to be able to search meta too. We just merge
415 * search columns into search_fields. search_fields overwrites search_columns.
416 */
417 if ( ! empty( $search_fields ) ) {
418 $prepared_search_fields = [];
419
420 // WP_User_Query uses shortened column names so we need to expand those.
421 if ( ! empty( $search_fields['login'] ) ) {
422 $prepared_search_fields['user_login'] = $search_fields['login'];
423
424 unset( $search_fields['login'] );
425 }
426
427 if ( ! empty( $search_fields['url'] ) ) {
428 $prepared_search_fields['user_url'] = $search_fields['url'];
429
430 unset( $search_fields['url'] );
431 }
432
433 if ( ! empty( $search_fields['nicename'] ) ) {
434 $prepared_search_fields['user_nicename'] = $search_fields['nicename'];
435
436 unset( $search_fields['nicename'] );
437 }
438
439 if ( ! empty( $search_fields['email'] ) ) {
440 $prepared_search_fields['user_email'] = $search_fields['email'];
441
442 unset( $search_fields['email'] );
443 }
444
445 if ( ! empty( $search_fields['meta'] ) ) {
446 $metas = (array) $search_fields['meta'];
447
448 foreach ( $metas as $meta ) {
449 $prepared_search_fields[] = 'meta.' . $meta . '.value';
450 }
451
452 unset( $search_fields['meta'] );
453 }
454
455 $prepared_search_fields = array_merge( $search_fields, $prepared_search_fields );
456 } else {
457 $prepared_search_fields = [
458 'user_login',
459 'user_nicename',
460 'display_name',
461 'user_url',
462 'user_email',
463 'meta.first_name',
464 'meta.last_name',
465 'meta.nickname',
466 ];
467 }
468
469 /**
470 * Filter search fields in user query
471 *
472 * @hook ep_user_search_fields
473 * @param {array} $prepared_search_fields Prepared search fields
474 * @param {array} $query_vars Query variables
475 * @since 3.0
476 * @return {array} Search fields
477 */
478 $prepared_search_fields = apply_filters( 'ep_user_search_fields', $prepared_search_fields, $query_vars );
479
480 $query = array(
481 'bool' => array(
482 'should' => array(
483 array(
484 'multi_match' => array(
485 'query' => $query_vars['search'],
486 'type' => 'phrase',
487 'fields' => $prepared_search_fields,
488 /**
489 * Filter boost for user match phrase query
490 *
491 * @hook ep_user_match_phrase_boost
492 * @param {int} $boost Phrase boost
493 * @param {array} $prepared_search_fields Search fields
494 * @param {array} $query_vars Query variables
495 * @since 3.0
496 * @return {int} New phrase boost
497 */
498 'boost' => apply_filters( 'ep_user_match_phrase_boost', 4, $prepared_search_fields, $query_vars ),
499 ),
500 ),
501 array(
502 'multi_match' => array(
503 'query' => $query_vars['search'],
504 'fields' => $prepared_search_fields,
505 /**
506 * Filter boost for user match query
507 *
508 * @hook ep_user_match_boost
509 * @param {int} $boost Boost
510 * @param {array} $prepared_search_fields Search fields
511 * @param {array} $query_vars Query variables
512 * @since 3.0
513 * @return {int} New boost
514 */
515 'boost' => apply_filters( 'ep_user_match_boost', 2, $prepared_search_fields, $query_vars ),
516 'fuzziness' => 0,
517 'operator' => 'and',
518 ),
519 ),
520 array(
521 'multi_match' => array(
522 'fields' => $prepared_search_fields,
523 'query' => $query_vars['search'],
524 /**
525 * Filter fuzziness for user query
526 *
527 * @hook ep_user_fuzziness_arg
528 * @param {int} $fuzziness Fuzziness
529 * @param {array} $prepared_search_fields Search fields
530 * @param {array} $query_vars Query variables
531 * @since 3.0
532 * @return {int} New fuzziness
533 */
534 'fuzziness' => apply_filters( 'ep_user_fuzziness_arg', 1, $prepared_search_fields, $query_vars ),
535 ),
536 ),
537 ),
538 ),
539 );
540
541 /**
542 * Filter formatted Elasticsearch user query (only contains query part)
543 *
544 * @hook ep_user_formatted_args_query
545 * @param {array} $query Current query
546 * @param {array} $query_vars Query variables
547 * @since 3.0
548 * @return {array} New query
549 */
550 $formatted_args['query'] = apply_filters( 'ep_user_formatted_args_query', $query, $query_vars );
551
552 } else {
553 $formatted_args['query']['match_all'] = [
554 'boost' => 1,
555 ];
556 }
557
558 if ( $use_filters ) {
559 $formatted_args['post_filter'] = $filter;
560 }
561
562 /**
563 * Handle order and orderby
564 */
565 if ( ! empty( $query_vars['order'] ) ) {
566 $order = trim( strtolower( $query_vars['order'] ) );
567 } else {
568 $order = 'desc';
569 }
570
571 if ( empty( $query_vars['orderby'] ) && ( ! isset( $query_vars['search'] ) || '' === $query_vars['search'] ) ) {
572 $query_vars['orderby'] = 'user_login';
573 }
574
575 // Set sort type.
576 if ( ! empty( $query_vars['orderby'] ) ) {
577 $formatted_args['sort'] = $this->parse_orderby( $query_vars['orderby'], $order, $query_vars );
578 } else {
579 // Default sort is to use the score (based on relevance).
580 $formatted_args['sort'] = array(
581 array(
582 '_score' => array(
583 'order' => $order,
584 ),
585 ),
586 );
587 }
588
589 /**
590 * Filter formatted Elasticsearch user query (entire query)
591 *
592 * @hook ep_user_formatted_args_query
593 * @param {array} $formatted_args Formatted Elasticsearch query
594 * @param {array} $query_vars Query variables
595 * @param {array} $query Query part
596 * @since 3.0
597 * @return {array} New query
598 */
599 return apply_filters( 'ep_user_formatted_args', $formatted_args, $query_vars, $query );
600 }
601
602 /**
603 * Convert the alias to a properly-prefixed sort value.
604 *
605 * @since 3.0
606 * @param string $orderby Orderby query var
607 * @param string $default_order Order direction
608 * @param array $query_vars Query vars
609 * @return array
610 */
611 public function parse_orderby( $orderby, $default_order, $query_vars ) {
612 /**
613 * More params to support
614 *
615 * @todo Need to support:
616 *
617 * include
618 * login__in
619 * nicename__in
620 * post_count
621 */
622
623 if ( ! is_array( $orderby ) ) {
624 $orderby = explode( ' ', $orderby );
625 }
626
627 $sort = [];
628
629 if ( empty( $orderby ) ) {
630 return $sort;
631 }
632
633 $unsupported_clauses = [ 'rand', 'include', 'login__in', 'nicename__in', 'post_count' ];
634
635 foreach ( $orderby as $key => $value ) {
636 if ( is_string( $key ) ) {
637 $orderby_clause = $key;
638 $order = $value;
639 } else {
640 $orderby_clause = $value;
641 $order = $default_order;
642 }
643
644 if ( empty( $orderby_clause ) || in_array( $orderby_clause, $unsupported_clauses, true ) ) {
645 continue;
646 }
647
648 switch ( $orderby_clause ) {
649 case 'relevance':
650 $orderby_field = '_score';
651 break;
652
653 case 'user_login':
654 case 'login':
655 $orderby_field = 'user_login.raw';
656 break;
657
658 case 'ID':
659 case 'id':
660 $orderby_field = 'ID';
661 break;
662
663 case 'display_name':
664 case 'name':
665 $orderby_field = 'display_name.sortable';
666 break;
667
668 case 'nicename':
669 case 'user_nicename':
670 $orderby_field = 'user_nicename.raw';
671 break;
672
673 case 'user_email':
674 case 'email':
675 $orderby_field = 'user_email.raw';
676 break;
677
678 case 'user_url':
679 case 'url':
680 $orderby_field = 'user_url.raw';
681 break;
682
683 case 'user_registered':
684 case 'registered':
685 $orderby_field = 'user_registered';
686 break;
687
688 case 'meta_value':
689 if ( ! empty( $query_vars['meta_key'] ) ) {
690 $orderby_field = 'meta.' . $query_vars['meta_key'] . '.raw';
691 }
692 break;
693
694 case 'meta_value_num':
695 if ( ! empty( $query_vars['meta_key'] ) ) {
696 $orderby_field = 'meta.' . $query_vars['meta_key'] . '.long';
697 }
698 break;
699
700 default:
701 $orderby_field = $orderby_clause;
702 break;
703 }
704
705 $sort[] = array(
706 $orderby_field => array(
707 'order' => $order,
708 ),
709 );
710 }
711
712 return $sort;
713 }
714
715 /**
716 * Query DB for users
717 *
718 * @param array $args Query arguments
719 * @since 3.0
720 * @return array
721 */
722 public function query_db( $args ) {
723 global $wpdb;
724
725 $defaults = [
726 'number' => 350,
727 'offset' => 0,
728 'orderby' => 'ID',
729 'order' => 'desc',
730 ];
731
732 if ( isset( $args['per_page'] ) ) {
733 $args['number'] = $args['per_page'];
734 }
735
736 /**
737 * Filter query database arguments for user indexable
738 *
739 * @hook ep_user_query_db_args
740 * @param {array} $args Database query arguments
741 * @since 3.0
742 * @return {array} New arguments
743 */
744 $args = apply_filters( 'ep_user_query_db_args', wp_parse_args( $args, $defaults ) );
745
746 $args['order'] = trim( strtolower( $args['order'] ) );
747
748 if ( ! in_array( $args['order'], [ 'asc', 'desc' ], true ) ) {
749 $args['order'] = 'desc';
750 }
751
752 $orderby_args = sanitize_sql_orderby( "{$args['orderby']} {$args['order']}" );
753 $orderby = $orderby_args ? sprintf( 'ORDER BY %s', $orderby_args ) : '';
754
755 /**
756 * WP_User_Query doesn't let us get users across all blogs easily. This is the best
757 * way to do that.
758 */
759 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
760 $objects = $wpdb->get_results( $wpdb->prepare( "SELECT SQL_CALC_FOUND_ROWS ID FROM {$wpdb->users} {$orderby} LIMIT %d, %d", (int) $args['offset'], (int) $args['number'] ) );
761
762 return [
763 'objects' => $objects,
764 'total_objects' => ( 0 === count( $objects ) ) ? 0 : (int) $wpdb->get_var( 'SELECT FOUND_ROWS()' ),
765 ];
766 }
767
768 /**
769 * Generate the mapping array
770 *
771 * @since 3.6.0
772 * @return array
773 */
774 public function generate_mapping() {
775 $es_version = Elasticsearch::factory()->get_elasticsearch_version();
776 if ( empty( $es_version ) ) {
777 /**
778 * Filter fallback Elasticsearch version
779 *
780 * @hook ep_fallback_elasticsearch_version
781 * @param {string} $version Fall back Elasticsearch version
782 * @return {string} New version
783 */
784 $es_version = apply_filters( 'ep_fallback_elasticsearch_version', '2.0' );
785 }
786
787 $mapping_file = 'initial.php';
788
789 if ( version_compare( $es_version, '5.0', '<' ) ) {
790 $mapping_file = 'pre-5-0.php';
791 } elseif ( version_compare( $es_version, '7.0', '>=' ) ) {
792 $mapping_file = '7-0.php';
793 }
794
795 /**
796 * Filter user indexable mapping file
797 *
798 * @hook ep_user_mapping_file
799 * @param {string} $file Path to file
800 * @since 3.0
801 * @return {string} New file path
802 */
803 $mapping = require apply_filters( 'ep_user_mapping_file', __DIR__ . '/../../../mappings/user/' . $mapping_file );
804
805 /**
806 * Filter user indexable mapping
807 *
808 * @hook ep_user_mapping
809 * @param {array} $mapping Mapping
810 * @since 3.0
811 * @return {array} New mapping
812 */
813 $mapping = apply_filters( 'ep_user_mapping', $mapping );
814
815 return $mapping;
816 }
817
818 /**
819 * Prepare a user document for indexing
820 *
821 * @param int $user_id User id
822 * @since 3.0
823 * @return array
824 */
825 public function prepare_document( $user_id ) {
826 $user = get_user_by( 'ID', $user_id );
827
828 if ( empty( $user ) ) {
829 return false;
830 }
831
832 $user_args = [
833 'ID' => $user_id,
834 'user_login' => $user->user_login,
835 'user_email' => $user->user_email,
836 'user_nicename' => $user->user_nicename,
837 'spam' => $user->spam,
838 'deleted' => $user->spam,
839 'user_status' => $user->user_status,
840 'display_name' => $user->display_name,
841 'user_registered' => $user->user_registered,
842 'user_url' => $user->user_url,
843 'capabilities' => $this->prepare_capabilities( $user_id ),
844 'meta' => $this->prepare_meta_types( $this->prepare_meta( $user_id ) ),
845 ];
846
847 /**
848 * Filter prepared user document before index
849 *
850 * @hook ep_user_sync_args
851 * @param {array} $user_args Document
852 * @param {int} $user_id User ID
853 * @since 3.0
854 * @return {array} New document
855 */
856 $user_args = apply_filters( 'ep_user_sync_args', $user_args, $user_id );
857
858 return $user_args;
859 }
860
861 /**
862 * Prepare capabilities for indexing
863 *
864 * @param int $user_id User ID
865 * @since 3.0
866 * @return array
867 */
868 public function prepare_capabilities( $user_id ) {
869 global $wpdb;
870
871 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
872 $sites = Utils\get_sites();
873 } else {
874 $sites = [
875 [
876 'blog_id' => (int) get_current_blog_id(),
877 ],
878 ];
879 }
880
881 $prepared_roles = [];
882
883 foreach ( $sites as $site ) {
884 $roles = (array) get_user_meta( $user_id, $wpdb->get_blog_prefix( $site['blog_id'] ) . 'capabilities', true );
885
886 if ( ! empty( $roles ) ) {
887 $prepared_roles[ (int) $site['blog_id'] ] = [
888 'roles' => array_keys( (array) $roles ),
889 ];
890 }
891 }
892
893 return $prepared_roles;
894 }
895
896 /**
897 * Prepare meta to send to ES
898 *
899 * @param int $user_id User id
900 * @since 3.0
901 * @return array
902 */
903 public function prepare_meta( $user_id ) {
904 /**
905 * Filter pre-prepare meta for a user
906 *
907 * @hook ep_prepare_user_meta_data
908 * @param {array} $meta Meta data
909 * @param {int} $user_id User ID
910 * @return {array} New meta
911 */
912 $meta = apply_filters( 'ep_prepare_user_meta_data', (array) get_user_meta( $user_id ), $user_id );
913
914 if ( empty( $meta ) ) {
915 /**
916 * Filter final list of prepared user meta.
917 *
918 * @hook ep_prepared_user_meta
919 * @param {array} $prepared_meta Prepared meta
920 * @param {integer} $user_id User ID
921 * @since 3.4
922 * @return {array} Prepared meta
923 */
924 return apply_filters( 'ep_prepared_user_meta', [], $user_id );
925 }
926
927 $prepared_meta = [];
928
929 /**
930 * Filter indexable private meta for users
931 *
932 * @hook ep_prepare_user_meta_allowed_protected_keys
933 * @param {array} $meta Meta keys
934 * @param {int} $user_id User ID
935 * @since 3.0
936 * @return {array} New meta array
937 */
938 $allowed_protected_keys = apply_filters( 'ep_prepare_user_meta_allowed_protected_keys', [], $user_id );
939
940 /**
941 * Filter out excluded indexable public meta keys for users
942 *
943 * @hook ep_prepare_user_meta_excluded_public_keys
944 * @param {array} $meta Meta keys
945 * @param {int} $user_id User ID
946 * @since 3.0
947 * @return {array} New meta array
948 */
949 $excluded_public_keys = apply_filters(
950 'ep_prepare_user_meta_excluded_public_keys',
951 [
952 'session_tokens',
953 ],
954 $user_id
955 );
956
957 foreach ( $meta as $key => $value ) {
958
959 $allow_index = false;
960
961 if ( is_protected_meta( $key ) ) {
962
963 if ( true === $allowed_protected_keys || in_array( $key, $allowed_protected_keys, true ) ) {
964 $allow_index = true;
965 }
966 } else {
967
968 if ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
969 $allow_index = true;
970 }
971 }
972
973 /**
974 * Filter whether to whitelist a specific user meta key
975 *
976 * @hookep_prepare_user_meta_whitelist_key
977 * @param {bool} $index True to force index
978 * @param {string} $key User meta key
979 * @param {int} $user_id User ID
980 * @since 3.0
981 * @return {bool} New index value
982 */
983 if ( true === $allow_index || apply_filters( 'ep_prepare_user_meta_whitelist_key', false, $key, $user_id ) ) {
984 $prepared_meta[ $key ] = maybe_unserialize( $value );
985 }
986 }
987
988 /**
989 * Filter final list of prepared user meta.
990 *
991 * @hook ep_prepared_user_meta
992 * @param {array} $prepared_meta Prepared meta
993 * @param {integer} $user_id User ID
994 * @since 3.4
995 * @return {array} Prepared meta
996 */
997 return apply_filters( 'ep_prepared_user_meta', $prepared_meta, $user_id );
998 }
999 }
1000