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