PluginProbe
Advanced Custom Fields (ACF®) / 6.0.0
Advanced Custom Fields (ACF®) v6.0.0
6.8.9 6.8.8 6.8.7 6.8.6 6.8.5 6.8.4 6.8.3 6.8.2 6.8.1 5.8.5 5.8.6 5.8.7 5.8.8 5.8.9 5.9.0 5.9.1 5.9.2 5.9.3 5.9.4 5.9.5 5.9.6 5.9.7 5.9.8 5.9.9 6.0.0 All 230 releases
advanced-custom-fields / includes / fields / class-acf-field-select.php
class-acf-field-select.php
740 lines 17.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! class_exists( 'acf_field_select' ) ) :
4
5 class acf_field_select extends acf_field {
6
7
8 /*
9 * __construct
10 *
11 * This function will setup the field type data
12 *
13 * @type function
14 * @date 5/03/2014
15 * @since 5.0.0
16 *
17 * @param n/a
18 * @return n/a
19 */
20
21 function initialize() {
22
23 // vars
24 $this->name = 'select';
25 $this->label = _x( 'Select', 'noun', 'acf' );
26 $this->category = 'choice';
27 $this->defaults = array(
28 'multiple' => 0,
29 'allow_null' => 0,
30 'choices' => array(),
31 'default_value' => '',
32 'ui' => 0,
33 'ajax' => 0,
34 'placeholder' => '',
35 'return_format' => 'value',
36 );
37
38 // ajax
39 add_action( 'wp_ajax_acf/fields/select/query', array( $this, 'ajax_query' ) );
40 add_action( 'wp_ajax_nopriv_acf/fields/select/query', array( $this, 'ajax_query' ) );
41
42 }
43
44
45 /*
46 * input_admin_enqueue_scripts
47 *
48 * description
49 *
50 * @type function
51 * @date 16/12/2015
52 * @since 5.3.2
53 *
54 * @param $post_id (int)
55 * @return $post_id (int)
56 */
57
58 function input_admin_enqueue_scripts() {
59
60 // bail early if no enqueue
61 if ( ! acf_get_setting( 'enqueue_select2' ) ) {
62 return;
63 }
64
65 // globals
66 global $wp_scripts, $wp_styles;
67
68 // vars
69 $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
70 $major = acf_get_setting( 'select2_version' );
71 $version = '';
72 $script = '';
73 $style = '';
74
75 // attempt to find 3rd party Select2 version
76 // - avoid including v3 CSS when v4 JS is already enququed
77 if ( isset( $wp_scripts->registered['select2'] ) ) {
78
79 $major = (int) $wp_scripts->registered['select2']->ver;
80
81 }
82
83 // v4
84 if ( $major == 4 ) {
85
86 $version = '4.0.13';
87 $script = acf_get_url( "assets/inc/select2/4/select2.full{$min}.js" );
88 $style = acf_get_url( "assets/inc/select2/4/select2{$min}.css" );
89
90 // v3
91 } else {
92
93 $version = '3.5.2';
94 $script = acf_get_url( "assets/inc/select2/3/select2{$min}.js" );
95 $style = acf_get_url( 'assets/inc/select2/3/select2.css' );
96
97 }
98
99 // enqueue
100 wp_enqueue_script( 'select2', $script, array( 'jquery' ), $version );
101 wp_enqueue_style( 'select2', $style, '', $version );
102
103 // localize
104 acf_localize_data(
105 array(
106 'select2L10n' => array(
107 'matches_1' => _x( 'One result is available, press enter to select it.', 'Select2 JS matches_1', 'acf' ),
108 'matches_n' => _x( '%d results are available, use up and down arrow keys to navigate.', 'Select2 JS matches_n', 'acf' ),
109 'matches_0' => _x( 'No matches found', 'Select2 JS matches_0', 'acf' ),
110 'input_too_short_1' => _x( 'Please enter 1 or more characters', 'Select2 JS input_too_short_1', 'acf' ),
111 'input_too_short_n' => _x( 'Please enter %d or more characters', 'Select2 JS input_too_short_n', 'acf' ),
112 'input_too_long_1' => _x( 'Please delete 1 character', 'Select2 JS input_too_long_1', 'acf' ),
113 'input_too_long_n' => _x( 'Please delete %d characters', 'Select2 JS input_too_long_n', 'acf' ),
114 'selection_too_long_1' => _x( 'You can only select 1 item', 'Select2 JS selection_too_long_1', 'acf' ),
115 'selection_too_long_n' => _x( 'You can only select %d items', 'Select2 JS selection_too_long_n', 'acf' ),
116 'load_more' => _x( 'Loading more results&hellip;', 'Select2 JS load_more', 'acf' ),
117 'searching' => _x( 'Searching&hellip;', 'Select2 JS searching', 'acf' ),
118 'load_fail' => _x( 'Loading failed', 'Select2 JS load_fail', 'acf' ),
119 ),
120 )
121 );
122 }
123
124
125 /*
126 * ajax_query
127 *
128 * description
129 *
130 * @type function
131 * @date 24/10/13
132 * @since 5.0.0
133 *
134 * @param $post_id (int)
135 * @return $post_id (int)
136 */
137
138 function ajax_query() {
139
140 // validate
141 if ( ! acf_verify_ajax() ) {
142 die();
143 }
144
145 // get choices
146 $response = $this->get_ajax_query( $_POST );
147
148 // return
149 acf_send_ajax_results( $response );
150
151 }
152
153
154 /*
155 * get_ajax_query
156 *
157 * This function will return an array of data formatted for use in a select2 AJAX response
158 *
159 * @type function
160 * @date 15/10/2014
161 * @since 5.0.9
162 *
163 * @param $options (array)
164 * @return (array)
165 */
166
167 function get_ajax_query( $options = array() ) {
168
169 // defaults
170 $options = acf_parse_args(
171 $options,
172 array(
173 'post_id' => 0,
174 's' => '',
175 'field_key' => '',
176 'paged' => 1,
177 )
178 );
179
180 // load field
181 $field = acf_get_field( $options['field_key'] );
182 if ( ! $field ) {
183 return false;
184 }
185
186 // get choices
187 $choices = acf_get_array( $field['choices'] );
188 if ( empty( $field['choices'] ) ) {
189 return false;
190 }
191
192 // vars
193 $results = array();
194 $s = null;
195
196 // search
197 if ( $options['s'] !== '' ) {
198
199 // strip slashes (search may be integer)
200 $s = strval( $options['s'] );
201 $s = wp_unslash( $s );
202
203 }
204
205 // loop
206 foreach ( $field['choices'] as $k => $v ) {
207
208 // ensure $v is a string
209 $v = strval( $v );
210
211 // if searching, but doesn't exist
212 if ( is_string( $s ) && stripos( $v, $s ) === false ) {
213 continue;
214 }
215
216 // append
217 $results[] = array(
218 'id' => $k,
219 'text' => $v,
220 );
221
222 }
223
224 // vars
225 $response = array(
226 'results' => $results,
227 );
228
229 // return
230 return $response;
231
232 }
233
234
235 /*
236 * render_field()
237 *
238 * Create the HTML interface for your field
239 *
240 * @param $field - an array holding all the field's data
241 *
242 * @type action
243 * @since 3.6
244 * @date 23/01/13
245 */
246
247 function render_field( $field ) {
248
249 // convert
250 $value = acf_get_array( $field['value'] );
251 $choices = acf_get_array( $field['choices'] );
252
253 // placeholder
254 if ( empty( $field['placeholder'] ) ) {
255 $field['placeholder'] = _x( 'Select', 'verb', 'acf' );
256 }
257
258 // add empty value (allows '' to be selected)
259 if ( empty( $value ) ) {
260 $value = array( '' );
261 }
262
263 // prepend empty choice
264 // - only for single selects
265 // - have tried array_merge but this causes keys to re-index if is numeric (post ID's)
266 if ( $field['allow_null'] && ! $field['multiple'] ) {
267 $choices = array( '' => "- {$field['placeholder']} -" ) + $choices;
268 }
269
270 // clean up choices if using ajax
271 if ( $field['ui'] && $field['ajax'] ) {
272 $minimal = array();
273 foreach ( $value as $key ) {
274 if ( isset( $choices[ $key ] ) ) {
275 $minimal[ $key ] = $choices[ $key ];
276 }
277 }
278 $choices = $minimal;
279 }
280
281 // vars
282 $select = array(
283 'id' => $field['id'],
284 'class' => $field['class'],
285 'name' => $field['name'],
286 'data-ui' => $field['ui'],
287 'data-ajax' => $field['ajax'],
288 'data-multiple' => $field['multiple'],
289 'data-placeholder' => $field['placeholder'],
290 'data-allow_null' => $field['allow_null'],
291 );
292
293 // multiple
294 if ( $field['multiple'] ) {
295
296 $select['multiple'] = 'multiple';
297 $select['size'] = 5;
298 $select['name'] .= '[]';
299
300 // Reduce size to single line if UI.
301 if ( $field['ui'] ) {
302 $select['size'] = 1;
303 }
304 }
305
306 // special atts
307 if ( ! empty( $field['readonly'] ) ) {
308 $select['readonly'] = 'readonly';
309 }
310 if ( ! empty( $field['disabled'] ) ) {
311 $select['disabled'] = 'disabled';
312 }
313 if ( ! empty( $field['ajax_action'] ) ) {
314 $select['data-ajax_action'] = $field['ajax_action'];
315 }
316
317 // hidden input is needed to allow validation to see <select> element with no selected value
318 if ( $field['multiple'] || $field['ui'] ) {
319 acf_hidden_input(
320 array(
321 'id' => $field['id'] . '-input',
322 'name' => $field['name'],
323 )
324 );
325 }
326
327 if ( ! empty( $field['query_nonce'] ) ) {
328 $select['data-query-nonce'] = $field['query_nonce'];
329 }
330
331 // append
332 $select['value'] = $value;
333 $select['choices'] = $choices;
334
335 // render
336 acf_select_input( $select );
337
338 }
339
340
341 /*
342 * render_field_settings()
343 *
344 * Create extra options for your field. This is rendered when editing a field.
345 * The value of $field['name'] can be used (like bellow) to save extra data to the $field
346 *
347 * @type action
348 * @since 3.6
349 * @date 23/01/13
350 *
351 * @param $field - an array holding all the field's data
352 */
353
354 function render_field_settings( $field ) {
355
356 // encode choices (convert from array)
357 $field['choices'] = acf_encode_choices( $field['choices'] );
358 $field['default_value'] = acf_encode_choices( $field['default_value'], false );
359
360 // choices
361 acf_render_field_setting(
362 $field,
363 array(
364 'label' => __( 'Choices', 'acf' ),
365 'hint' => __( 'Enter each choice on a new line.', 'acf' ) . '<br />' . __( 'For more control, you may specify both a value and label like this:', 'acf' ) . '<br /><span class="acf-field-setting-example">' . __( 'red : Red', 'acf' ) . '</span>',
366 'name' => 'choices',
367 'type' => 'textarea',
368 )
369 );
370
371 // default_value
372 acf_render_field_setting(
373 $field,
374 array(
375 'label' => __( 'Default Value', 'acf' ),
376 'instructions' => __( 'Enter each default value on a new line', 'acf' ),
377 'name' => 'default_value',
378 'type' => 'textarea',
379 )
380 );
381
382 // return_format
383 acf_render_field_setting(
384 $field,
385 array(
386 'label' => __( 'Return Format', 'acf' ),
387 'instructions' => __( 'Specify the value returned', 'acf' ),
388 'type' => 'radio',
389 'name' => 'return_format',
390 'layout' => 'horizontal',
391 'choices' => array(
392 'value' => __( 'Value', 'acf' ),
393 'label' => __( 'Label', 'acf' ),
394 'array' => __( 'Both (Array)', 'acf' ),
395 ),
396 )
397 );
398
399 acf_render_field_setting(
400 $field,
401 array(
402 'label' => __( 'Select multiple values?', 'acf' ),
403 'instructions' => '',
404 'name' => 'multiple',
405 'type' => 'true_false',
406 'ui' => 1,
407 )
408 );
409
410 }
411
412 /**
413 * Renders the field settings used in the "Validation" tab.
414 *
415 * @since 6.0
416 *
417 * @param array $field The field settings array.
418 * @return void
419 */
420 function render_field_validation_settings( $field ) {
421 acf_render_field_setting(
422 $field,
423 array(
424 'label' => __( 'Allow Null?', 'acf' ),
425 'instructions' => '',
426 'name' => 'allow_null',
427 'type' => 'true_false',
428 'ui' => 1,
429 )
430 );
431 }
432
433 /**
434 * Renders the field settings used in the "Presentation" tab.
435 *
436 * @since 6.0
437 *
438 * @param array $field The field settings array.
439 * @return void
440 */
441 function render_field_presentation_settings( $field ) {
442 acf_render_field_setting(
443 $field,
444 array(
445 'label' => __( 'Stylized UI', 'acf' ),
446 'instructions' => __( 'Use a stylized checkbox using select2', 'acf' ),
447 'name' => 'ui',
448 'type' => 'true_false',
449 'ui' => 1,
450 )
451 );
452
453 acf_render_field_setting(
454 $field,
455 array(
456 'label' => __( 'Use AJAX to lazy load choices?', 'acf' ),
457 'instructions' => '',
458 'name' => 'ajax',
459 'type' => 'true_false',
460 'ui' => 1,
461 'conditions' => array(
462 'field' => 'ui',
463 'operator' => '==',
464 'value' => 1,
465 ),
466 )
467 );
468 }
469
470 /*
471 * load_value()
472 *
473 * This filter is applied to the $value after it is loaded from the db
474 *
475 * @type filter
476 * @since 3.6
477 * @date 23/01/13
478 *
479 * @param $value (mixed) the value found in the database
480 * @param $post_id (mixed) the $post_id from which the value was loaded
481 * @param $field (array) the field array holding all the field options
482 * @return $value
483 */
484 function load_value( $value, $post_id, $field ) {
485
486 // Return an array when field is set for multiple.
487 if ( $field['multiple'] ) {
488 if ( acf_is_empty( $value ) ) {
489 return array();
490 }
491 return acf_array( $value );
492 }
493
494 // Otherwise, return a single value.
495 return acf_unarray( $value );
496 }
497
498
499 /*
500 * update_field()
501 *
502 * This filter is appied to the $field before it is saved to the database
503 *
504 * @type filter
505 * @since 3.6
506 * @date 23/01/13
507 *
508 * @param $field - the field array holding all the field options
509 * @param $post_id - the field group ID (post_type = acf)
510 *
511 * @return $field - the modified field
512 */
513
514 function update_field( $field ) {
515
516 // decode choices (convert to array)
517 $field['choices'] = acf_decode_choices( $field['choices'] );
518 $field['default_value'] = acf_decode_choices( $field['default_value'], true );
519
520 // Convert back to string for single selects.
521 if ( ! $field['multiple'] ) {
522 $field['default_value'] = acf_unarray( $field['default_value'] );
523 }
524
525 // return
526 return $field;
527 }
528
529
530 /*
531 * update_value()
532 *
533 * This filter is appied to the $value before it is updated in the db
534 *
535 * @type filter
536 * @since 3.6
537 * @date 23/01/13
538 *
539 * @param $value - the value which will be saved in the database
540 * @param $post_id - the $post_id of which the value will be saved
541 * @param $field - the field array holding all the field options
542 *
543 * @return $value - the modified value
544 */
545
546 function update_value( $value, $post_id, $field ) {
547
548 // Bail early if no value.
549 if ( empty( $value ) ) {
550 return $value;
551 }
552
553 // Format array of values.
554 // - Parse each value as string for SQL LIKE queries.
555 if ( is_array( $value ) ) {
556 $value = array_map( 'strval', $value );
557 }
558
559 // return
560 return $value;
561 }
562
563
564 /*
565 * translate_field
566 *
567 * This function will translate field settings
568 *
569 * @type function
570 * @date 8/03/2016
571 * @since 5.3.2
572 *
573 * @param $field (array)
574 * @return $field
575 */
576
577 function translate_field( $field ) {
578
579 // translate
580 $field['choices'] = acf_translate( $field['choices'] );
581
582 // return
583 return $field;
584
585 }
586
587
588 /*
589 * format_value()
590 *
591 * This filter is appied to the $value after it is loaded from the db and before it is returned to the template
592 *
593 * @type filter
594 * @since 3.6
595 * @date 23/01/13
596 *
597 * @param $value (mixed) the value which was loaded from the database
598 * @param $post_id (mixed) the $post_id from which the value was loaded
599 * @param $field (array) the field array holding all the field options
600 *
601 * @return $value (mixed) the modified value
602 */
603 function format_value( $value, $post_id, $field ) {
604 if ( is_array( $value ) ) {
605 foreach ( $value as $i => $val ) {
606 $value[ $i ] = $this->format_value_single( $val, $post_id, $field );
607 }
608 } else {
609 $value = $this->format_value_single( $value, $post_id, $field );
610 }
611 return $value;
612 }
613
614
615 function format_value_single( $value, $post_id, $field ) {
616
617 // bail early if is empty
618 if ( acf_is_empty( $value ) ) {
619 return $value;
620 }
621
622 // vars
623 $label = acf_maybe_get( $field['choices'], $value, $value );
624
625 // value
626 if ( $field['return_format'] == 'value' ) {
627
628 // do nothing
629
630 // label
631 } elseif ( $field['return_format'] == 'label' ) {
632
633 $value = $label;
634
635 // array
636 } elseif ( $field['return_format'] == 'array' ) {
637
638 $value = array(
639 'value' => $value,
640 'label' => $label,
641 );
642
643 }
644
645 // return
646 return $value;
647
648 }
649
650 /**
651 * Validates select fields updated via the REST API.
652 *
653 * @param bool $valid
654 * @param int $value
655 * @param array $field
656 *
657 * @return bool|WP_Error
658 */
659 public function validate_rest_value( $valid, $value, $field ) {
660 // rest_validate_request_arg() handles the other types, we just worry about strings.
661 if ( is_null( $value ) || is_array( $value ) ) {
662 return $valid;
663 }
664
665 $option_keys = array_diff(
666 array_keys( $field['choices'] ),
667 array_values( $field['choices'] )
668 );
669
670 $allowed = empty( $option_keys ) ? $field['choices'] : $option_keys;
671
672 if ( ! in_array( $value, $allowed ) ) {
673 $param = sprintf( '%s[%s]', $field['prefix'], $field['name'] );
674 $data = array(
675 'param' => $param,
676 'value' => $value,
677 );
678 $error = sprintf(
679 __( '%1$s is not one of %2$s', 'acf' ),
680 $param,
681 implode( ', ', $allowed )
682 );
683
684 return new WP_Error( 'rest_invalid_param', $error, $data );
685 }
686
687 return $valid;
688 }
689
690 /**
691 * Return the schema array for the REST API.
692 *
693 * @param array $field
694 * @return array
695 */
696 public function get_rest_schema( array $field ) {
697 /**
698 * If a user has defined keys for the select options,
699 * we should use the keys for the available options to POST to,
700 * since they are what is displayed in GET requests.
701 */
702 $option_keys = array_diff(
703 array_keys( $field['choices'] ),
704 array_values( $field['choices'] )
705 );
706
707 $schema = array(
708 'type' => array( 'string', 'array', 'null' ),
709 'required' => ! empty( $field['required'] ),
710 'items' => array(
711 'type' => array( 'string' ),
712 'enum' => empty( $option_keys ) ? $field['choices'] : $option_keys,
713 ),
714 );
715
716 if ( empty( $field['allow_null'] ) ) {
717 $schema['minItems'] = 1;
718 }
719
720 if ( empty( $field['multiple'] ) ) {
721 $schema['maxItems'] = 1;
722 }
723
724 if ( isset( $field['default_value'] ) && '' !== $field['default_value'] ) {
725 $schema['default'] = $field['default_value'];
726 }
727
728 return $schema;
729 }
730
731 }
732
733
734 // initialize
735 acf_register_field_type( 'acf_field_select' );
736
737 endif; // class_exists check
738
739
740