PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.4.0-beta2
Secure Custom Fields v6.4.0-beta2
6.9.5 6.9.4 6.9.3 6.9.2 6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / includes / fields / class-acf-field-user.php
secure-custom-fields / includes / fields Last commit date
class-acf-field-accordion.php 1 year ago class-acf-field-button-group.php 1 year ago class-acf-field-checkbox.php 1 year ago class-acf-field-color_picker.php 1 year ago class-acf-field-date_picker.php 1 year ago class-acf-field-date_time_picker.php 1 year ago class-acf-field-email.php 1 year ago class-acf-field-file.php 1 year ago class-acf-field-google-map.php 1 year ago class-acf-field-group.php 1 year ago class-acf-field-icon_picker.php 1 year ago class-acf-field-image.php 1 year ago class-acf-field-link.php 1 year ago class-acf-field-message.php 1 year ago class-acf-field-number.php 1 year ago class-acf-field-oembed.php 1 year ago class-acf-field-output.php 1 year ago class-acf-field-page_link.php 1 year ago class-acf-field-password.php 1 year ago class-acf-field-post_object.php 1 year ago class-acf-field-radio.php 1 year ago class-acf-field-range.php 1 year ago class-acf-field-relationship.php 1 year ago class-acf-field-select.php 1 year ago class-acf-field-separator.php 1 year ago class-acf-field-tab.php 1 year ago class-acf-field-taxonomy.php 1 year ago class-acf-field-text.php 1 year ago class-acf-field-textarea.php 1 year ago class-acf-field-time_picker.php 1 year ago class-acf-field-true_false.php 1 year ago class-acf-field-url.php 1 year ago class-acf-field-user.php 1 year ago class-acf-field-wysiwyg.php 1 year ago class-acf-field.php 1 year ago index.php 1 year ago
class-acf-field-user.php
662 lines
1 <?php
2
3 if ( ! class_exists( 'ACF_Field_User' ) ) :
4
5 class ACF_Field_User extends ACF_Field {
6
7 /**
8 * Initializes the field type.
9 *
10 * @date 5/03/2014
11 * @since 5.0.0
12 */
13 function initialize() {
14 $this->name = 'user';
15 $this->label = __( 'User', 'acf' );
16 $this->category = 'relational';
17 $this->description = __( 'Allows the selection of one or more users which can be used to create relationships between data objects.', 'acf' );
18 $this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-user.png';
19 $this->doc_url = 'https://www.advancedcustomfields.com/resources/user/';
20 $this->defaults = array(
21 'role' => '',
22 'multiple' => 0,
23 'allow_null' => 0,
24 'return_format' => 'array',
25 'bidirectional_target' => array(),
26 );
27
28 // Register filter variations.
29 acf_add_filter_variations( 'acf/fields/user/query', array( 'name', 'key' ), 1 );
30 acf_add_filter_variations( 'acf/fields/user/result', array( 'name', 'key' ), 2 );
31 acf_add_filter_variations( 'acf/fields/user/search_columns', array( 'name', 'key' ), 3 );
32 add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_user_conditional_choices' ), 10, 3 );
33
34 // Add AJAX query.
35 add_action( 'wp_ajax_acf/fields/user/query', array( $this, 'ajax_query' ) );
36 add_action( 'wp_ajax_nopriv_acf/fields/user/query', array( $this, 'ajax_query' ) );
37 }
38
39 /**
40 * Filters choices in user conditions.
41 *
42 * @since 6.3
43 *
44 * @param array $choices The selected choice.
45 * @param array $conditional_field The conditional field settings object.
46 * @param string $rule_value The rule value.
47 * @return array
48 */
49 public function render_field_user_conditional_choices( $choices, $conditional_field, $rule_value ) {
50 if ( ! is_array( $conditional_field ) || $conditional_field['type'] !== 'user' ) {
51 return $choices;
52 }
53 if ( ! empty( $rule_value ) ) {
54 $user = acf_get_users(
55 array(
56 'include' => array( $rule_value ),
57 )
58 );
59
60 $user_result = acf_get_user_result( $user[0] );
61 $choices = array( $user_result['id'] => $user_result['text'] );
62 }
63
64 return $choices;
65 }
66
67 /**
68 * Renders the field settings HTML.
69 *
70 * @date 23/01/13
71 * @since 3.6.0
72 *
73 * @param array $field The ACF field.
74 * @return void
75 */
76 function render_field_settings( $field ) {
77 acf_render_field_setting(
78 $field,
79 array(
80 'label' => __( 'Filter by Role', 'acf' ),
81 'instructions' => '',
82 'type' => 'select',
83 'name' => 'role',
84 'choices' => acf_get_user_role_labels(),
85 'multiple' => 1,
86 'ui' => 1,
87 'allow_null' => 1,
88 'placeholder' => __( 'All user roles', 'acf' ),
89 )
90 );
91
92 acf_render_field_setting(
93 $field,
94 array(
95 'label' => __( 'Return Format', 'acf' ),
96 'instructions' => '',
97 'type' => 'radio',
98 'name' => 'return_format',
99 'choices' => array(
100 'array' => __( 'User Array', 'acf' ),
101 'object' => __( 'User Object', 'acf' ),
102 'id' => __( 'User ID', 'acf' ),
103 ),
104 'layout' => 'horizontal',
105 )
106 );
107
108 acf_render_field_setting(
109 $field,
110 array(
111 'label' => __( 'Select Multiple', 'acf' ),
112 'instructions' => 'Allow content editors to select multiple values',
113 'name' => 'multiple',
114 'type' => 'true_false',
115 'ui' => 1,
116 )
117 );
118 }
119
120 /**
121 * Renders the field settings used in the "Validation" tab.
122 *
123 * @since 6.0
124 *
125 * @param array $field The field settings array.
126 * @return void
127 */
128 function render_field_validation_settings( $field ) {
129 acf_render_field_setting(
130 $field,
131 array(
132 'label' => __( 'Allow Null', 'acf' ),
133 'instructions' => '',
134 'name' => 'allow_null',
135 'type' => 'true_false',
136 'ui' => 1,
137 )
138 );
139 }
140
141 /**
142 * Renders the field settings used in the "Advanced" tab.
143 *
144 * @since 6.2
145 *
146 * @param array $field The field settings array.
147 * @return void
148 */
149 public function render_field_advanced_settings( $field ) {
150 acf_render_bidirectional_field_settings( $field );
151 }
152
153 /**
154 * Renders the field input HTML.
155 *
156 * @since 3.6.0
157 *
158 * @param array $field The ACF field.
159 * @return void
160 */
161 public function render_field( $field ) {
162 // Change Field into a select.
163 $field['type'] = 'select';
164 $field['ui'] = 1;
165 $field['ajax'] = 1;
166 $field['choices'] = array();
167 $field['nonce'] = wp_create_nonce( $field['key'] );
168
169 // Populate choices.
170 if ( $field['value'] ) {
171
172 // Clean value into an array of IDs.
173 $user_ids = array_map( 'intval', acf_array( $field['value'] ) );
174
175 // Find users in database (ensures all results are real).
176 $users = acf_get_users(
177 array(
178 'include' => $user_ids,
179 )
180 );
181
182 // Append.
183 if ( $users ) {
184 foreach ( $users as $user ) {
185 $field['choices'][ $user->ID ] = $this->get_result( $user, $field );
186 }
187 }
188 }
189
190 // Render.
191 acf_render_field( $field );
192 }
193
194 /**
195 * Returns the result text for a given WP_User object.
196 *
197 * @date 1/11/2013
198 * @since 5.0.0
199 *
200 * @param WP_User $user The WP_User object.
201 * @param array $field The ACF field related to this query.
202 * @param (int|string) $post_id The post_id being edited.
203 * @return string
204 */
205 function get_result( $user, $field, $post_id = 0 ) {
206
207 // Get user result item.
208 $item = acf_get_user_result( $user );
209
210 // Default $post_id to current post being edited.
211 $post_id = $post_id ? $post_id : acf_get_form_data( 'post_id' );
212
213 /**
214 * Filters the result text.
215 *
216 * @date 21/5/19
217 * @since 5.8.1
218 *
219 * @param array $args The query args.
220 * @param array $field The ACF field related to this query.
221 * @param (int|string) $post_id The post_id being edited.
222 */
223 return apply_filters( 'acf/fields/user/result', $item['text'], $user, $field, $post_id );
224 }
225
226 /**
227 * Filters the field value after it is loaded from the database.
228 *
229 * @date 23/01/13
230 * @since 3.6.0
231 *
232 * @param mixed $value The field value.
233 * @param mixed $post_id The post ID where the value is saved.
234 * @param array $field The field array containing all settings.
235 * @return mixed
236 */
237 function load_value( $value, $post_id, $field ) {
238
239 // Add compatibility for version 4.
240 if ( $value === 'null' ) {
241 return false;
242 }
243 return $value;
244 }
245
246 /**
247 * Filters the field value after it is loaded from the database but before it is returned to the front-end API.
248 *
249 * @date 23/01/13
250 * @since 3.6.0
251 *
252 * @param mixed $value The field value.
253 * @param mixed $post_id The post ID where the value is saved.
254 * @param array $field The field array containing all settings.
255 * @return mixed
256 */
257 function format_value( $value, $post_id, $field ) {
258
259 // Bail early if no value.
260 if ( ! $value ) {
261 return false;
262 }
263
264 // Clean value into an array of IDs.
265 $user_ids = array_map( 'intval', acf_array( $value ) );
266
267 // Find users in database (ensures all results are real).
268 $users = acf_get_users(
269 array(
270 'include' => $user_ids,
271 )
272 );
273
274 // Bail early if no users found.
275 if ( ! $users ) {
276 return false;
277 }
278
279 // Format values using field settings.
280 $value = array();
281 foreach ( $users as $user ) {
282
283 // Return object.
284 if ( $field['return_format'] == 'object' ) {
285 $item = $user;
286
287 // Return array.
288 } elseif ( $field['return_format'] == 'array' ) {
289 $item = array(
290 'ID' => $user->ID,
291 'user_firstname' => $user->user_firstname,
292 'user_lastname' => $user->user_lastname,
293 'nickname' => $user->nickname,
294 'user_nicename' => $user->user_nicename,
295 'display_name' => $user->display_name,
296 'user_email' => $user->user_email,
297 'user_url' => $user->user_url,
298 'user_registered' => $user->user_registered,
299 'user_description' => $user->user_description,
300 'user_avatar' => get_avatar( $user->ID ),
301 );
302
303 // Return ID.
304 } else {
305 $item = $user->ID;
306 }
307
308 // Append item
309 $value[] = $item;
310 }
311
312 // Convert to single.
313 if ( ! $field['multiple'] ) {
314 $value = array_shift( $value );
315 }
316
317 // Return.
318 return $value;
319 }
320
321 /**
322 * Filters the field value before it is saved into the database.
323 *
324 * @since 3.6.0
325 *
326 * @param mixed $value The field value.
327 * @param mixed $post_id The post ID where the value is saved.
328 * @param array $field The field array containing all settings.
329 * @return mixed $value The modified value.
330 */
331 public function update_value( $value, $post_id, $field ) {
332
333 // Bail early if no value.
334 if ( empty( $value ) ) {
335 acf_update_bidirectional_values( array(), $post_id, $field, 'user' );
336 return $value;
337 }
338
339 // Format array of values.
340 // - ensure each value is an id.
341 // - Parse each id as string for SQL LIKE queries.
342 if ( acf_is_sequential_array( $value ) ) {
343 $value = array_map( 'acf_idval', $value );
344 $value = array_map( 'strval', $value );
345
346 // Parse single value for id.
347 } else {
348 $value = acf_idval( $value );
349 }
350
351 acf_update_bidirectional_values( acf_get_array( $value ), $post_id, $field, 'user' );
352
353 // Return value.
354 return $value;
355 }
356
357 /**
358 * Callback for the AJAX query request.
359 *
360 * @date 24/10/13
361 * @since 5.0.0
362 *
363 * @param void
364 * @return void
365 */
366 function ajax_query() {
367
368 // phpcs:disable WordPress.Security.NonceVerification.Recommended
369 // Modify Request args.
370 if ( isset( $_REQUEST['s'] ) ) {
371 $_REQUEST['search'] = sanitize_text_field( $_REQUEST['s'] );
372 }
373 if ( isset( $_REQUEST['paged'] ) ) {
374 $_REQUEST['page'] = absint( $_REQUEST['paged'] );
375 }
376 // phpcs:enable WordPress.Security.NonceVerification.Recommended
377
378 // Add query hooks.
379 add_action( 'acf/ajax/query_users/init', array( $this, 'ajax_query_init' ), 10, 2 );
380 add_filter( 'acf/ajax/query_users/args', array( $this, 'ajax_query_args' ), 10, 3 );
381 add_filter( 'acf/ajax/query_users/result', array( $this, 'ajax_query_result' ), 10, 3 );
382 add_filter( 'acf/ajax/query_users/search_columns', array( $this, 'ajax_query_search_columns' ), 10, 4 );
383 // Simulate AJAX request.
384 acf_get_instance( 'ACF_Ajax_Query_Users' )->request();
385 }
386
387 /**
388 * Runs during the AJAX query initialization.
389 *
390 * @date 9/3/20
391 * @since 5.8.8
392 *
393 * @param array $request The query request.
394 * @param ACF_Ajax_Query $query The query object.
395 * @return void
396 */
397 function ajax_query_init( $request, $query ) {
398 // Require field and make sure it's a user field.
399 if ( ! $query->field || $query->field['type'] !== $this->name ) {
400 $query->send( new WP_Error( 'acf_missing_field', __( 'Error loading field.', 'acf' ), array( 'status' => 404 ) ) );
401 }
402
403 // Verify that this is a legitimate request using a separate nonce from the main AJAX nonce.
404 $nonce = acf_request_arg( 'nonce', '' );
405 $key = acf_request_arg( 'field_key', '' );
406
407 if ( ! acf_verify_ajax( $nonce, $key ) ) {
408 $query->send( new WP_Error( 'acf_invalid_request', __( 'Invalid request.', 'acf' ), array( 'status' => 404 ) ) );
409 }
410 }
411
412 /**
413 * Filters the AJAX query args.
414 *
415 * @date 9/3/20
416 * @since 5.8.8
417 *
418 * @param array $args The query args.
419 * @param array $request The query request.
420 * @param ACF_Ajax_Query $query The query object.
421 * @return array
422 */
423 function ajax_query_args( $args, $request, $query ) {
424
425 // Add specific roles.
426 if ( $query->field['role'] ) {
427 $args['role__in'] = acf_array( $query->field['role'] );
428 }
429
430 /**
431 * Filters the query args.
432 *
433 * @date 21/5/19
434 * @since 5.8.1
435 *
436 * @param array $args The query args.
437 * @param array $field The ACF field related to this query.
438 * @param (int|string) $post_id The post_id being edited.
439 */
440 return apply_filters( 'acf/fields/user/query', $args, $query->field, $query->post_id );
441 }
442
443 /**
444 * Filters the WP_User_Query search columns.
445 *
446 * @date 9/3/20
447 * @since 5.8.8
448 *
449 * @param array $columns An array of column names to be searched.
450 * @param string $search The search term.
451 * @param WP_User_Query $WP_User_Query The WP_User_Query instance.
452 * @return array
453 */
454 function ajax_query_search_columns( $columns, $search, $WP_User_Query, $query ) {
455
456 /**
457 * Filters the column names to be searched.
458 *
459 * @date 21/5/19
460 * @since 5.8.1
461 *
462 * @param array $columns An array of column names to be searched.
463 * @param string $search The search term.
464 * @param WP_User_Query $WP_User_Query The WP_User_Query instance.
465 * @param array $field The ACF field related to this query.
466 */
467 return apply_filters( 'acf/fields/user/search_columns', $columns, $search, $WP_User_Query, $query->field );
468 }
469
470 /**
471 * Filters the AJAX Query result.
472 *
473 * @date 9/3/20
474 * @since 5.8.8
475 *
476 * @param array $item The choice id and text.
477 * @param WP_User $user The user object.
478 * @param ACF_Ajax_Query $query The query object.
479 * @return array
480 */
481 function ajax_query_result( $item, $user, $query ) {
482
483 /**
484 * Filters the result text.
485 *
486 * @date 21/5/19
487 * @since 5.8.1
488 *
489 * @param string The result text.
490 * @param WP_User $user The user object.
491 * @param array $field The ACF field related to this query.
492 * @param (int|string) $post_id The post_id being edited.
493 */
494 $item['text'] = apply_filters( 'acf/fields/user/result', $item['text'], $user, $query->field, $query->post_id );
495 return $item;
496 }
497
498 /**
499 * Return an array of data formatted for use in a select2 AJAX response.
500 *
501 * @date 15/10/2014
502 * @since 5.0.9
503 * @deprecated 5.8.9
504 *
505 * @param array $args An array of query args.
506 * @return array
507 */
508 function get_ajax_query( $options = array() ) {
509 _deprecated_function( __FUNCTION__, '5.8.9' );
510 return array();
511 }
512
513 /**
514 * Filters the WP_User_Query search columns.
515 *
516 * @date 15/10/2014
517 * @since 5.0.9
518 * @deprecated 5.8.9
519 *
520 * @param array $columns An array of column names to be searched.
521 * @param string $search The search term.
522 * @param WP_User_Query $WP_User_Query The WP_User_Query instance.
523 * @return array
524 */
525 function user_search_columns( $columns, $search, $WP_User_Query ) {
526 _deprecated_function( __FUNCTION__, '5.8.9' );
527 return $columns;
528 }
529
530 /**
531 * Validates user fields updated via the REST API.
532 *
533 * @param boolean $valid The current validity booleean
534 * @param integer $value The value of the field
535 * @param array $field The field array
536 * @return boolean|WP_Error
537 */
538 public function validate_rest_value( $valid, $value, $field ) {
539 if ( is_null( $value ) ) {
540 return $valid;
541 }
542
543 $param = sprintf( '%s[%s]', $field['prefix'], $field['name'] );
544 $data = array( 'param' => $param );
545 $value = is_array( $value ) ? $value : array( $value );
546
547 $invalid_users = array();
548 $insufficient_roles = array();
549
550 foreach ( $value as $user_id ) {
551 $user_data = get_userdata( $user_id );
552 if ( ! $user_data ) {
553 $invalid_users[] = $user_id;
554 continue;
555 }
556
557 if ( empty( $field['role'] ) ) {
558 continue;
559 }
560
561 $has_roles = count( array_intersect( $field['role'], $user_data->roles ) );
562 if ( ! $has_roles ) {
563 $insufficient_roles[] = $user_id;
564 }
565 }
566
567 if ( count( $invalid_users ) ) {
568 $error = sprintf(
569 __( '%1$s must have a valid user ID.', 'acf' ),
570 $param
571 );
572 $data['value'] = $invalid_users;
573 return new WP_Error( 'rest_invalid_param', $error, $data );
574 }
575
576 if ( count( $insufficient_roles ) ) {
577 $error = sprintf(
578 _n(
579 '%1$s must have a user with the %2$s role.',
580 '%1$s must have a user with one of the following roles: %2$s',
581 count( $field['role'] ),
582 'acf'
583 ),
584 $param,
585 count( $field['role'] ) > 1 ? implode( ', ', $field['role'] ) : $field['role'][0]
586 );
587 $data['value'] = $insufficient_roles;
588 return new WP_Error( 'rest_invalid_param', $error, $data );
589 }
590
591 return $valid;
592 }
593
594 /**
595 * Return the schema array for the REST API.
596 *
597 * @param array $field
598 * @return array
599 */
600 public function get_rest_schema( array $field ) {
601 $schema = array(
602 'type' => array( 'integer', 'array', 'null' ),
603 'required' => ! empty( $field['required'] ),
604 'items' => array(
605 'type' => 'integer',
606 ),
607 );
608
609 if ( empty( $field['allow_null'] ) ) {
610 $schema['minItems'] = 1;
611 }
612
613 if ( empty( $field['multiple'] ) ) {
614 $schema['maxItems'] = 1;
615 }
616
617 return $schema;
618 }
619
620 /**
621 * @see \acf_field::get_rest_links()
622 * @param mixed $value The raw (unformatted) field value.
623 * @param integer|string $post_id
624 * @param array $field
625 * @return array
626 */
627 public function get_rest_links( $value, $post_id, array $field ) {
628 $links = array();
629
630 if ( empty( $value ) ) {
631 return $links;
632 }
633
634 foreach ( (array) $value as $object_id ) {
635 $links[] = array(
636 'rel' => 'acf:user',
637 'href' => rest_url( '/wp/v2/users/' . $object_id ),
638 'embeddable' => true,
639 );
640 }
641
642 return $links;
643 }
644
645 /**
646 * Apply basic formatting to prepare the value for default REST output.
647 *
648 * @param mixed $value
649 * @param string|integer $post_id
650 * @param array $field
651 * @return mixed
652 */
653 public function format_value_for_rest( $value, $post_id, array $field ) {
654 return acf_format_numerics( $value );
655 }
656 }
657
658
659 // initialize
660 acf_register_field_type( 'ACF_Field_User' );
661 endif; // class_exists check
662