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