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

900 lines 21.7 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 $from_to = [
558 'relevance' => '_score',
559 'user_login' => 'user_login.raw',
560 'login' => 'user_login.raw',
561 'id' => 'ID',
562 'display_name' => 'display_name.sortable',
563 'name' => 'display_name.sortable',
564 'nicename' => 'user_nicename.raw',
565 'user_nicename' => 'user_nicename.raw',
566 'user_email' => 'user_email.raw',
567 'email' => 'user_email.raw',
568 'user_url' => 'user_url.raw',
569 'url' => 'user_url.raw',
570 'registered' => 'user_registered',
571 ];
572
573 $sort = [];
574
575 if ( empty( $orderby ) ) {
576 return $sort;
577 }
578
579 $unsupported_clauses = [ 'rand', 'include', 'login__in', 'nicename__in', 'post_count' ];
580
581 foreach ( $orderby as $key => $value ) {
582 if ( is_string( $key ) ) {
583 $orderby_clause = $key;
584 $order = $value;
585 } else {
586 $orderby_clause = $value;
587 $order = $default_order;
588 }
589
590 if ( empty( $orderby_clause ) || in_array( $orderby_clause, $unsupported_clauses, true ) ) {
591 continue;
592 }
593
594 if ( in_array( $orderby_clause, [ 'meta_value', 'meta_value_num' ], true ) ) {
595 if ( empty( $args['meta_key'] ) ) {
596 continue;
597 } else {
598 $from_to['meta_value'] = 'meta.' . $args['meta_key'] . '.raw';
599 $from_to['meta_value_num'] = 'meta.' . $args['meta_key'] . '.long';
600 }
601 }
602
603 $orderby_clause = $from_to[ $orderby_clause ] ?? $orderby_clause;
604
605 $sort[] = array(
606 $orderby_clause => array(
607 'order' => $order,
608 ),
609 );
610 }
611
612 return $sort;
613 }
614
615 /**
616 * Query DB for users
617 *
618 * @param array $args Query arguments
619 * @since 3.0
620 * @return array
621 */
622 public function query_db( $args ) {
623 global $wpdb;
624
625 $defaults = [
626 'number' => 350,
627 'offset' => 0,
628 'orderby' => 'ID',
629 'order' => 'desc',
630 ];
631
632 if ( isset( $args['per_page'] ) ) {
633 $args['number'] = $args['per_page'];
634 }
635
636 /**
637 * Filter query database arguments for user indexable
638 *
639 * @hook ep_user_query_db_args
640 * @param {array} $args Database query arguments
641 * @since 3.0
642 * @return {array} New arguments
643 */
644 $args = apply_filters( 'ep_user_query_db_args', wp_parse_args( $args, $defaults ) );
645
646 $args['order'] = trim( strtolower( $args['order'] ) );
647
648 if ( ! in_array( $args['order'], [ 'asc', 'desc' ], true ) ) {
649 $args['order'] = 'desc';
650 }
651
652 $orderby_args = sanitize_sql_orderby( "{$args['orderby']} {$args['order']}" );
653 $orderby = $orderby_args ? sprintf( 'ORDER BY %s', $orderby_args ) : '';
654
655 /**
656 * WP_User_Query doesn't let us get users across all blogs easily. This is the best
657 * way to do that.
658 */
659 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
660 $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'] ) );
661
662 return [
663 'objects' => $objects,
664 'total_objects' => ( 0 === count( $objects ) ) ? 0 : (int) $wpdb->get_var( 'SELECT FOUND_ROWS()' ),
665 ];
666 }
667
668 /**
669 * Generate the mapping array
670 *
671 * @since 3.6.0
672 * @return array
673 */
674 public function generate_mapping() {
675 $es_version = Elasticsearch::factory()->get_elasticsearch_version();
676 if ( empty( $es_version ) ) {
677 /**
678 * Filter fallback Elasticsearch version
679 *
680 * @hook ep_fallback_elasticsearch_version
681 * @param {string} $version Fall back Elasticsearch version
682 * @return {string} New version
683 */
684 $es_version = apply_filters( 'ep_fallback_elasticsearch_version', '2.0' );
685 }
686
687 $mapping_file = 'initial.php';
688
689 if ( version_compare( $es_version, '5.0', '<' ) ) {
690 $mapping_file = 'pre-5-0.php';
691 } elseif ( version_compare( $es_version, '7.0', '>=' ) ) {
692 $mapping_file = '7-0.php';
693 }
694
695 /**
696 * Filter user indexable mapping file
697 *
698 * @hook ep_user_mapping_file
699 * @param {string} $file Path to file
700 * @since 3.0
701 * @return {string} New file path
702 */
703 $mapping = require apply_filters( 'ep_user_mapping_file', __DIR__ . '/../../../mappings/user/' . $mapping_file );
704
705 /**
706 * Filter user indexable mapping
707 *
708 * @hook ep_user_mapping
709 * @param {array} $mapping Mapping
710 * @since 3.0
711 * @return {array} New mapping
712 */
713 $mapping = apply_filters( 'ep_user_mapping', $mapping );
714
715 return $mapping;
716 }
717
718 /**
719 * Prepare a user document for indexing
720 *
721 * @param int $user_id User id
722 * @since 3.0
723 * @return array
724 */
725 public function prepare_document( $user_id ) {
726 $user = get_user_by( 'ID', $user_id );
727
728 if ( empty( $user ) ) {
729 return false;
730 }
731
732 $user_args = [
733 'ID' => $user_id,
734 'user_login' => $user->user_login,
735 'user_email' => $user->user_email,
736 'user_nicename' => $user->user_nicename,
737 'spam' => $user->spam,
738 'deleted' => $user->spam,
739 'user_status' => $user->user_status,
740 'display_name' => $user->display_name,
741 'user_registered' => $user->user_registered,
742 'user_url' => $user->user_url,
743 'capabilities' => $this->prepare_capabilities( $user_id ),
744 'meta' => $this->prepare_meta_types( $this->prepare_meta( $user_id ) ),
745 ];
746
747 /**
748 * Filter prepared user document before index
749 *
750 * @hook ep_user_sync_args
751 * @param {array} $user_args Document
752 * @param {int} $user_id User ID
753 * @since 3.0
754 * @return {array} New document
755 */
756 $user_args = apply_filters( 'ep_user_sync_args', $user_args, $user_id );
757
758 return $user_args;
759 }
760
761 /**
762 * Prepare capabilities for indexing
763 *
764 * @param int $user_id User ID
765 * @since 3.0
766 * @return array
767 */
768 public function prepare_capabilities( $user_id ) {
769 global $wpdb;
770
771 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
772 $sites = Utils\get_sites();
773 } else {
774 $sites = [
775 [
776 'blog_id' => (int) get_current_blog_id(),
777 ],
778 ];
779 }
780
781 $prepared_roles = [];
782
783 foreach ( $sites as $site ) {
784 $roles = (array) get_user_meta( $user_id, $wpdb->get_blog_prefix( $site['blog_id'] ) . 'capabilities', true );
785
786 if ( ! empty( $roles ) ) {
787 $prepared_roles[ (int) $site['blog_id'] ] = [
788 'roles' => array_keys( (array) $roles ),
789 ];
790 }
791 }
792
793 return $prepared_roles;
794 }
795
796 /**
797 * Prepare meta to send to ES
798 *
799 * @param int $user_id User id
800 * @since 3.0
801 * @return array
802 */
803 public function prepare_meta( $user_id ) {
804 /**
805 * Filter pre-prepare meta for a user
806 *
807 * @hook ep_prepare_user_meta_data
808 * @param {array} $meta Meta data
809 * @param {int} $user_id User ID
810 * @return {array} New meta
811 */
812 $meta = apply_filters( 'ep_prepare_user_meta_data', (array) get_user_meta( $user_id ), $user_id );
813
814 if ( empty( $meta ) ) {
815 /**
816 * Filter final list of prepared user meta.
817 *
818 * @hook ep_prepared_user_meta
819 * @param {array} $prepared_meta Prepared meta
820 * @param {integer} $user_id User ID
821 * @since 3.4
822 * @return {array} Prepared meta
823 */
824 return apply_filters( 'ep_prepared_user_meta', [], $user_id );
825 }
826
827 $prepared_meta = [];
828
829 /**
830 * Filter indexable private meta for users
831 *
832 * @hook ep_prepare_user_meta_allowed_protected_keys
833 * @param {array} $meta Meta keys
834 * @param {int} $user_id User ID
835 * @since 3.0
836 * @return {array} New meta array
837 */
838 $allowed_protected_keys = apply_filters( 'ep_prepare_user_meta_allowed_protected_keys', [], $user_id );
839
840 /**
841 * Filter out excluded indexable public meta keys for users
842 *
843 * @hook ep_prepare_user_meta_excluded_public_keys
844 * @param {array} $meta Meta keys
845 * @param {int} $user_id User ID
846 * @since 3.0
847 * @return {array} New meta array
848 */
849 $excluded_public_keys = apply_filters(
850 'ep_prepare_user_meta_excluded_public_keys',
851 [
852 'session_tokens',
853 ],
854 $user_id
855 );
856
857 foreach ( $meta as $key => $value ) {
858
859 $allow_index = false;
860
861 if ( is_protected_meta( $key ) ) {
862
863 if ( true === $allowed_protected_keys || in_array( $key, $allowed_protected_keys, true ) ) {
864 $allow_index = true;
865 }
866 } else {
867
868 if ( true !== $excluded_public_keys && ! in_array( $key, $excluded_public_keys, true ) ) {
869 $allow_index = true;
870 }
871 }
872
873 /**
874 * Filter whether to whitelist a specific user meta key
875 *
876 * @hookep_prepare_user_meta_whitelist_key
877 * @param {bool} $index True to force index
878 * @param {string} $key User meta key
879 * @param {int} $user_id User ID
880 * @since 3.0
881 * @return {bool} New index value
882 */
883 if ( true === $allow_index || apply_filters( 'ep_prepare_user_meta_whitelist_key', false, $key, $user_id ) ) {
884 $prepared_meta[ $key ] = maybe_unserialize( $value );
885 }
886 }
887
888 /**
889 * Filter final list of prepared user meta.
890 *
891 * @hook ep_prepared_user_meta
892 * @param {array} $prepared_meta Prepared meta
893 * @param {integer} $user_id User ID
894 * @since 3.4
895 * @return {array} Prepared meta
896 */
897 return apply_filters( 'ep_prepared_user_meta', $prepared_meta, $user_id );
898 }
899 }
900