PluginProbe
ElasticPress / 4.3.1
ElasticPress v4.3.1
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.3.1, at includes/classes/Indexable/User/User.php

930 lines 21.9 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 $search_algorithm = $this->get_search_algorithm( $query_vars['search'], $prepared_search_fields, $query_vars );
481 $formatted_args['query'] = $search_algorithm->get_query( 'user', $query_vars['search'], $prepared_search_fields, $query_vars );
482 } else {
483 $formatted_args['query']['match_all'] = [
484 'boost' => 1,
485 ];
486 }
487
488 if ( $use_filters ) {
489 $formatted_args['post_filter'] = $filter;
490 }
491
492 /**
493 * Handle order and orderby
494 */
495 if ( ! empty( $query_vars['order'] ) ) {
496 $order = trim( strtolower( $query_vars['order'] ) );
497 } else {
498 $order = 'desc';
499 }
500
501 if ( empty( $query_vars['orderby'] ) && ( ! isset( $query_vars['search'] ) || '' === $query_vars['search'] ) ) {
502 $query_vars['orderby'] = 'user_login';
503 }
504
505 // Set sort type.
506 if ( ! empty( $query_vars['orderby'] ) ) {
507 $formatted_args['sort'] = $this->parse_orderby( $query_vars['orderby'], $order, $query_vars );
508 } else {
509 // Default sort is to use the score (based on relevance).
510 $formatted_args['sort'] = array(
511 array(
512 '_score' => array(
513 'order' => $order,
514 ),
515 ),
516 );
517 }
518
519 /**
520 * Filter formatted Elasticsearch user query (entire query)
521 *
522 * @hook ep_user_formatted_args_query
523 * @param {array} $formatted_args Formatted Elasticsearch query
524 * @param {array} $query_vars Query variables
525 * @param {array} $query Query part
526 * @since 3.0
527 * @return {array} New query
528 */
529 return apply_filters( 'ep_user_formatted_args', $formatted_args, $query_vars, $query );
530 }
531
532 /**
533 * Convert the alias to a properly-prefixed sort value.
534 *
535 * @since 3.0
536 * @param string $orderby Orderby query var
537 * @param string $default_order Order direction
538 * @param array $query_vars Query vars
539 * @return array
540 */
541 public function parse_orderby( $orderby, $default_order, $query_vars ) {
542 /**
543 * More params to support
544 *
545 * @todo Need to support:
546 *
547 * include
548 * login__in
549 * nicename__in
550 * post_count
551 */
552
553 if ( ! is_array( $orderby ) ) {
554 $orderby = explode( ' ', $orderby );
555 }
556
557 $sort = [];
558
559 if ( empty( $orderby ) ) {
560 return $sort;
561 }
562
563 $unsupported_clauses = [ 'rand', 'include', 'login__in', 'nicename__in', 'post_count' ];
564
565 foreach ( $orderby as $key => $value ) {
566 if ( is_string( $key ) ) {
567 $orderby_clause = $key;
568 $order = $value;
569 } else {
570 $orderby_clause = $value;
571 $order = $default_order;
572 }
573
574 if ( empty( $orderby_clause ) || in_array( $orderby_clause, $unsupported_clauses, true ) ) {
575 continue;
576 }
577
578 switch ( $orderby_clause ) {
579 case 'relevance':
580 $orderby_field = '_score';
581 break;
582
583 case 'user_login':
584 case 'login':
585 $orderby_field = 'user_login.raw';
586 break;
587
588 case 'ID':
589 case 'id':
590 $orderby_field = 'ID';
591 break;
592
593 case 'display_name':
594 case 'name':
595 $orderby_field = 'display_name.sortable';
596 break;
597
598 case 'nicename':
599 case 'user_nicename':
600 $orderby_field = 'user_nicename.raw';
601 break;
602
603 case 'user_email':
604 case 'email':
605 $orderby_field = 'user_email.raw';
606 break;
607
608 case 'user_url':
609 case 'url':
610 $orderby_field = 'user_url.raw';
611 break;
612
613 case 'user_registered':
614 case 'registered':
615 $orderby_field = 'user_registered';
616 break;
617
618 case 'meta_value':
619 if ( ! empty( $query_vars['meta_key'] ) ) {
620 $orderby_field = 'meta.' . $query_vars['meta_key'] . '.raw';
621 }
622 break;
623
624 case 'meta_value_num':
625 if ( ! empty( $query_vars['meta_key'] ) ) {
626 $orderby_field = 'meta.' . $query_vars['meta_key'] . '.long';
627 }
628 break;
629
630 default:
631 $orderby_field = $orderby_clause;
632 break;
633 }
634
635 $sort[] = array(
636 $orderby_field => array(
637 'order' => $order,
638 ),
639 );
640 }
641
642 return $sort;
643 }
644
645 /**
646 * Query DB for users
647 *
648 * @param array $args Query arguments
649 * @since 3.0
650 * @return array
651 */
652 public function query_db( $args ) {
653 global $wpdb;
654
655 $defaults = [
656 'number' => 350,
657 'offset' => 0,
658 'orderby' => 'ID',
659 'order' => 'desc',
660 ];
661
662 if ( isset( $args['per_page'] ) ) {
663 $args['number'] = $args['per_page'];
664 }
665
666 /**
667 * Filter query database arguments for user indexable
668 *
669 * @hook ep_user_query_db_args
670 * @param {array} $args Database query arguments
671 * @since 3.0
672 * @return {array} New arguments
673 */
674 $args = apply_filters( 'ep_user_query_db_args', wp_parse_args( $args, $defaults ) );
675
676 $args['order'] = trim( strtolower( $args['order'] ) );
677
678 if ( ! in_array( $args['order'], [ 'asc', 'desc' ], true ) ) {
679 $args['order'] = 'desc';
680 }
681
682 $orderby_args = sanitize_sql_orderby( "{$args['orderby']} {$args['order']}" );
683 $orderby = $orderby_args ? sprintf( 'ORDER BY %s', $orderby_args ) : '';
684
685 /**
686 * WP_User_Query doesn't let us get users across all blogs easily. This is the best
687 * way to do that.
688 */
689 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
690 $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'] ) );
691
692 return [
693 'objects' => $objects,
694 'total_objects' => ( 0 === count( $objects ) ) ? 0 : (int) $wpdb->get_var( 'SELECT FOUND_ROWS()' ),
695 ];
696 }
697
698 /**
699 * Generate the mapping array
700 *
701 * @since 3.6.0
702 * @return array
703 */
704 public function generate_mapping() {
705 $es_version = Elasticsearch::factory()->get_elasticsearch_version();
706 if ( empty( $es_version ) ) {
707 /**
708 * Filter fallback Elasticsearch version
709 *
710 * @hook ep_fallback_elasticsearch_version
711 * @param {string} $version Fall back Elasticsearch version
712 * @return {string} New version
713 */
714 $es_version = apply_filters( 'ep_fallback_elasticsearch_version', '2.0' );
715 }
716
717 $mapping_file = 'initial.php';
718
719 if ( version_compare( $es_version, '5.0', '<' ) ) {
720 $mapping_file = 'pre-5-0.php';
721 } elseif ( version_compare( $es_version, '7.0', '>=' ) ) {
722 $mapping_file = '7-0.php';
723 }
724
725 /**
726 * Filter user indexable mapping file
727 *
728 * @hook ep_user_mapping_file
729 * @param {string} $file Path to file
730 * @since 3.0
731 * @return {string} New file path
732 */
733 $mapping = require apply_filters( 'ep_user_mapping_file', __DIR__ . '/../../../mappings/user/' . $mapping_file );
734
735 /**
736 * Filter user indexable mapping
737 *
738 * @hook ep_user_mapping
739 * @param {array} $mapping Mapping
740 * @since 3.0
741 * @return {array} New mapping
742 */
743 $mapping = apply_filters( 'ep_user_mapping', $mapping );
744
745 return $mapping;
746 }
747
748 /**
749 * Prepare a user document for indexing
750 *
751 * @param int $user_id User id
752 * @since 3.0
753 * @return array
754 */
755 public function prepare_document( $user_id ) {
756 $user = get_user_by( 'ID', $user_id );
757
758 if ( empty( $user ) ) {
759 return false;
760 }
761
762 $user_args = [
763 'ID' => $user_id,
764 'user_login' => $user->user_login,
765 'user_email' => $user->user_email,
766 'user_nicename' => $user->user_nicename,
767 'spam' => $user->spam,
768 'deleted' => $user->spam,
769 'user_status' => $user->user_status,
770 'display_name' => $user->display_name,
771 'user_registered' => $user->user_registered,
772 'user_url' => $user->user_url,
773 'capabilities' => $this->prepare_capabilities( $user_id ),
774 'meta' => $this->prepare_meta_types( $this->prepare_meta( $user_id ) ),
775 ];
776
777 /**
778 * Filter prepared user document before index
779 *
780 * @hook ep_user_sync_args
781 * @param {array} $user_args Document
782 * @param {int} $user_id User ID
783 * @since 3.0
784 * @return {array} New document
785 */
786 $user_args = apply_filters( 'ep_user_sync_args', $user_args, $user_id );
787
788 return $user_args;
789 }
790
791 /**
792 * Prepare capabilities for indexing
793 *
794 * @param int $user_id User ID
795 * @since 3.0
796 * @return array
797 */
798 public function prepare_capabilities( $user_id ) {
799 global $wpdb;
800
801 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
802 $sites = Utils\get_sites();
803 } else {
804 $sites = [
805 [
806 'blog_id' => (int) get_current_blog_id(),
807 ],
808 ];
809 }
810
811 $prepared_roles = [];
812
813 foreach ( $sites as $site ) {
814 $roles = (array) get_user_meta( $user_id, $wpdb->get_blog_prefix( $site['blog_id'] ) . 'capabilities', true );
815
816 if ( ! empty( $roles ) ) {
817 $prepared_roles[ (int) $site['blog_id'] ] = [
818 'roles' => array_keys( (array) $roles ),
819 ];
820 }
821 }
822
823 return $prepared_roles;
824 }
825
826 /**
827 * Prepare meta to send to ES
828 *
829 * @param int $user_id User id
830 * @since 3.0
831 * @return array
832 */
833 public function prepare_meta( $user_id ) {
834 /**
835 * Filter pre-prepare meta for a user
836 *
837 * @hook ep_prepare_user_meta_data
838 * @param {array} $meta Meta data
839 * @param {int} $user_id User ID
840 * @return {array} New meta
841 */
842 $meta = apply_filters( 'ep_prepare_user_meta_data', (array) get_user_meta( $user_id ), $user_id );
843
844 if ( empty( $meta ) ) {
845 /**
846 * Filter final list of prepared user meta.
847 *
848 * @hook ep_prepared_user_meta
849 * @param {array} $prepared_meta Prepared meta
850 * @param {integer} $user_id User ID
851 * @since 3.4
852 * @return {array} Prepared meta
853 */
854 return apply_filters( 'ep_prepared_user_meta', [], $user_id );
855 }
856
857 $prepared_meta = [];
858
859 /**
860 * Filter indexable private meta for users
861 *
862 * @hook ep_prepare_user_meta_allowed_protected_keys
863 * @param {array} $meta Meta keys
864 * @param {int} $user_id User ID
865 * @since 3.0
866 * @return {array} New meta array
867 */
868 $allowed_protected_keys = apply_filters( 'ep_prepare_user_meta_allowed_protected_keys', [], $user_id );
869
870 /**
871 * Filter out excluded indexable public meta keys for users
872 *
873 * @hook ep_prepare_user_meta_excluded_public_keys
874 * @param {array} $meta Meta keys
875 * @param {int} $user_id User ID
876 * @since 3.0
877 * @return {array} New meta array
878 */
879 $excluded_public_keys = apply_filters(
880 'ep_prepare_user_meta_excluded_public_keys',
881 [
882 'session_tokens',
883 ],
884 $user_id
885 );
886
887 foreach ( $meta as $key => $value ) {
888
889 $allow_index = false;
890
891 if ( is_protected_meta( $key ) ) {
892
893 if ( true === $allowed_protected_keys || in_array( $key, $allowed_protected_keys, true ) ) {
894 $allow_index = true;
895 }
896 } else {
897
898 if ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
899 $allow_index = true;
900 }
901 }
902
903 /**
904 * Filter whether to whitelist a specific user meta key
905 *
906 * @hookep_prepare_user_meta_whitelist_key
907 * @param {bool} $index True to force index
908 * @param {string} $key User meta key
909 * @param {int} $user_id User ID
910 * @since 3.0
911 * @return {bool} New index value
912 */
913 if ( true === $allow_index || apply_filters( 'ep_prepare_user_meta_whitelist_key', false, $key, $user_id ) ) {
914 $prepared_meta[ $key ] = maybe_unserialize( $value );
915 }
916 }
917
918 /**
919 * Filter final list of prepared user meta.
920 *
921 * @hook ep_prepared_user_meta
922 * @param {array} $prepared_meta Prepared meta
923 * @param {integer} $user_id User ID
924 * @since 3.4
925 * @return {array} Prepared meta
926 */
927 return apply_filters( 'ep_prepared_user_meta', $prepared_meta, $user_id );
928 }
929 }
930