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 / acf-field-functions.php
acf-field-functions.php
1,614 lines 37.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Register store.
4 acf_register_store( 'fields' )->prop( 'multisite', true );
5
6 /**
7 * acf_get_field
8 *
9 * Retrieves a field for the given identifier.
10 *
11 * @date 17/1/19
12 * @since 5.7.10
13 *
14 * @param (int|string) $id The field ID, key or name.
15 * @return (array|false) The field array.
16 */
17 function acf_get_field( $id = 0 ) {
18
19 // Allow WP_Post to be passed.
20 if ( is_object( $id ) ) {
21 $id = $id->ID;
22 }
23
24 // Check store.
25 $store = acf_get_store( 'fields' );
26 if ( $store->has( $id ) ) {
27 return $store->get( $id );
28 }
29
30 // Check local fields first.
31 if ( acf_is_local_field( $id ) ) {
32 $field = acf_get_local_field( $id );
33
34 // Then check database.
35 } else {
36 $field = acf_get_raw_field( $id );
37 }
38
39 // Bail early if no field.
40 if ( ! $field ) {
41 return false;
42 }
43
44 // Validate field.
45 $field = acf_validate_field( $field );
46
47 // Set input prefix.
48 $field['prefix'] = 'acf';
49
50 /**
51 * Filters the $field array after it has been loaded.
52 *
53 * @date 12/02/2014
54 * @since 5.0.0
55 *
56 * @param array The field array.
57 */
58 $field = apply_filters( 'acf/load_field', $field );
59
60 // Store field using aliasses to also find via key, ID and name.
61 $store->set( $field['key'], $field );
62 $store->alias( $field['key'], $field['ID'], $field['name'] );
63
64 // Return.
65 return $field;
66 }
67
68 // Register variation.
69 acf_add_filter_variations( 'acf/load_field', array( 'type', 'name', 'key' ), 0 );
70
71 /**
72 * acf_get_raw_field
73 *
74 * Retrieves raw field data for the given identifier.
75 *
76 * @date 18/1/19
77 * @since 5.7.10
78 *
79 * @param (int|string) $id The field ID, key or name.
80 * @return (array|false) The field array.
81 */
82 function acf_get_raw_field( $id = 0 ) {
83
84 // Get raw field from database.
85 $post = acf_get_field_post( $id );
86 if ( ! $post ) {
87 return false;
88 }
89
90 // Bail early if incorrect post type.
91 if ( $post->post_type !== 'acf-field' ) {
92 return false;
93 }
94
95 // Unserialize post_content.
96 $field = (array) maybe_unserialize( $post->post_content );
97
98 // update attributes
99 $field['ID'] = $post->ID;
100 $field['key'] = $post->post_name;
101 $field['label'] = $post->post_title;
102 $field['name'] = $post->post_excerpt;
103 $field['menu_order'] = $post->menu_order;
104 $field['parent'] = $post->post_parent;
105
106 // Return field.
107 return $field;
108 }
109
110 /**
111 * acf_get_field_post
112 *
113 * Retrieves the field's WP_Post object.
114 *
115 * @date 18/1/19
116 * @since 5.7.10
117 *
118 * @param (int|string) $id The field ID, key or name.
119 * @return (array|false) The field array.
120 */
121 function acf_get_field_post( $id = 0 ) {
122
123 // Get post if numeric.
124 if ( is_numeric( $id ) ) {
125 return get_post( $id );
126
127 // Search posts if is string.
128 } elseif ( is_string( $id ) ) {
129
130 // Determine id type.
131 $type = acf_is_field_key( $id ) ? 'key' : 'name';
132
133 // Try cache.
134 $cache_key = acf_cache_key( "acf_get_field_post:$type:$id" );
135 $post_id = wp_cache_get( $cache_key, 'acf' );
136 if ( $post_id === false ) {
137
138 // Query posts.
139 $posts = get_posts(
140 array(
141 'posts_per_page' => 1,
142 'post_type' => 'acf-field',
143 'orderby' => 'menu_order title',
144 'order' => 'ASC',
145 'suppress_filters' => false,
146 'cache_results' => true,
147 'update_post_meta_cache' => false,
148 'update_post_term_cache' => false,
149 "acf_field_$type" => $id,
150 )
151 );
152
153 // Update $post_id with a non false value.
154 $post_id = $posts ? $posts[0]->ID : 0;
155
156 // Update cache.
157 wp_cache_set( $cache_key, $post_id, 'acf' );
158 }
159
160 // Check $post_id and return the post when possible.
161 if ( $post_id ) {
162 return get_post( $post_id );
163 }
164 }
165
166 // Return false by default.
167 return false;
168 }
169
170 /**
171 * acf_is_field_key
172 *
173 * Returns true if the given identifier is a field key.
174 *
175 * @date 6/12/2013
176 * @since 5.0.0
177 *
178 * @param string $id The identifier.
179 * @return bool
180 */
181 function acf_is_field_key( $id = '' ) {
182
183 // Check if $id is a string starting with "field_".
184 if ( is_string( $id ) && substr( $id, 0, 6 ) === 'field_' ) {
185 return true;
186 }
187
188 /**
189 * Filters whether the $id is a field key.
190 *
191 * @date 23/1/19
192 * @since 5.7.10
193 *
194 * @param bool $bool The result.
195 * @param string $id The identifier.
196 */
197 return apply_filters( 'acf/is_field_key', false, $id );
198 }
199
200 /**
201 * acf_validate_field
202 *
203 * Ensures the given field valid.
204 *
205 * @date 18/1/19
206 * @since 5.7.10
207 *
208 * @param array $field The field array.
209 * @return array
210 */
211 function acf_validate_field( $field = array() ) {
212
213 // Bail early if already valid.
214 if ( is_array( $field ) && ! empty( $field['_valid'] ) ) {
215 return $field;
216 }
217
218 // Apply defaults.
219 $field = wp_parse_args(
220 $field,
221 array(
222 'ID' => 0,
223 'key' => '',
224 'label' => '',
225 'name' => '',
226 'prefix' => '',
227 'type' => 'text',
228 'value' => null,
229 'menu_order' => 0,
230 'instructions' => '',
231 'required' => false,
232 'id' => '',
233 'class' => '',
234 'conditional_logic' => false,
235 'parent' => 0,
236 'wrapper' => array(),
237 // 'attributes' => array()
238 )
239 );
240
241 // Convert types.
242 $field['ID'] = (int) $field['ID'];
243 $field['menu_order'] = (int) $field['menu_order'];
244
245 // Add backwards compatibility for wrapper attributes.
246 // Todo: Remove need for this.
247 $field['wrapper'] = wp_parse_args(
248 $field['wrapper'],
249 array(
250 'width' => '',
251 'class' => '',
252 'id' => '',
253 )
254 );
255
256 // Store backups.
257 $field['_name'] = $field['name'];
258 $field['_valid'] = 1;
259
260 /**
261 * Filters the $field array to validate settings.
262 *
263 * @date 12/02/2014
264 * @since 5.0.0
265 *
266 * @param array $field The field array.
267 */
268 $field = apply_filters( 'acf/validate_field', $field );
269
270 // return
271 return $field;
272 }
273
274 // Register variation.
275 acf_add_filter_variations( 'acf/validate_field', array( 'type' ), 0 );
276
277 /**
278 * acf_get_valid_field
279 *
280 * Ensures the given field valid.
281 *
282 * @date 28/09/13
283 * @since 5.0.0
284 *
285 * @param array $field The field array.
286 * @return array
287 */
288 function acf_get_valid_field( $field = false ) {
289 return acf_validate_field( $field );
290 }
291
292 /**
293 * acf_translate_field
294 *
295 * Translates a field's settings.
296 *
297 * @date 8/03/2016
298 * @since 5.3.2
299 *
300 * @param array $field The field array.
301 * @return array
302 */
303 function acf_translate_field( $field = array() ) {
304
305 // Get settings.
306 $l10n = acf_get_setting( 'l10n' );
307 $l10n_textdomain = acf_get_setting( 'l10n_textdomain' );
308
309 // Translate field settings if textdomain is set.
310 if ( $l10n && $l10n_textdomain ) {
311
312 $field['label'] = acf_translate( $field['label'] );
313 $field['instructions'] = acf_translate( $field['instructions'] );
314
315 /**
316 * Filters the $field array to translate strings.
317 *
318 * @date 12/02/2014
319 * @since 5.0.0
320 *
321 * @param array $field The field array.
322 */
323 $field = apply_filters( 'acf/translate_field', $field );
324 }
325
326 // Return field.
327 return $field;
328 }
329
330 // Register variation.
331 acf_add_filter_variations( 'acf/translate_field', array( 'type' ), 0 );
332
333 // Translate fields passing through validation.
334 add_action( 'acf/validate_field', 'acf_translate_field' );
335
336 /**
337 * acf_get_fields
338 *
339 * Returns and array of fields for the given $parent.
340 *
341 * @date 30/09/13
342 * @since 5.0.0
343 *
344 * @param (int|string|array) $parent The field group or field settings. Also accepts the field group ID or key.
345 * @return array
346 */
347 function acf_get_fields( $parent ) {
348
349 // Allow field group selector as $parent.
350 if ( ! is_array( $parent ) ) {
351 $parent = acf_get_field_group( $parent );
352 if ( ! $parent ) {
353 return array();
354 }
355 }
356
357 // Vars.
358 $fields = array();
359
360 // Check local fields first.
361 if ( acf_have_local_fields( $parent['key'] ) ) {
362 $raw_fields = acf_get_local_fields( $parent['key'] );
363 foreach ( $raw_fields as $raw_field ) {
364 $fields[] = acf_get_field( $raw_field['key'] );
365 }
366
367 // Then check database.
368 } else {
369 $raw_fields = acf_get_raw_fields( $parent['ID'] );
370 foreach ( $raw_fields as $raw_field ) {
371 $fields[] = acf_get_field( $raw_field['ID'] );
372 }
373 }
374
375 /**
376 * Filters the $fields array.
377 *
378 * @date 12/02/2014
379 * @since 5.0.0
380 *
381 * @param array $fields The array of fields.
382 */
383 $fields = apply_filters( 'acf/load_fields', $fields, $parent );
384
385 // Return fields
386 return $fields;
387 }
388
389 /**
390 * acf_get_raw_fields
391 *
392 * Returns and array of raw field data for the given parent id.
393 *
394 * @date 18/1/19
395 * @since 5.7.10
396 *
397 * @param int $id The field group or field id.
398 * @return array
399 */
400 function acf_get_raw_fields( $id = 0 ) {
401
402 // Try cache.
403 $cache_key = acf_cache_key( "acf_get_field_posts:$id" );
404 $post_ids = wp_cache_get( $cache_key, 'acf' );
405 if ( $post_ids === false ) {
406
407 // Query posts.
408 $posts = get_posts(
409 array(
410 'posts_per_page' => -1,
411 'post_type' => 'acf-field',
412 'orderby' => 'menu_order',
413 'order' => 'ASC',
414 'suppress_filters' => true, // DO NOT allow WPML to modify the query
415 'cache_results' => true,
416 'update_post_meta_cache' => false,
417 'update_post_term_cache' => false,
418 'post_parent' => $id,
419 'post_status' => array( 'publish', 'trash' ),
420 )
421 );
422
423 // Update $post_ids with a non false value.
424 $post_ids = array();
425 foreach ( $posts as $post ) {
426 $post_ids[] = $post->ID;
427 }
428
429 // Update cache.
430 wp_cache_set( $cache_key, $post_ids, 'acf' );
431 }
432
433 // Loop over ids and populate array of fields.
434 $fields = array();
435 foreach ( $post_ids as $post_id ) {
436 $fields[] = acf_get_raw_field( $post_id );
437 }
438
439 // Return fields.
440 return $fields;
441 }
442
443 /**
444 * acf_get_field_count
445 *
446 * Return the number of fields for the given field group.
447 *
448 * @date 17/10/13
449 * @since 5.0.0
450 *
451 * @param array $parent The field group or field array.
452 * @return int
453 */
454 function acf_get_field_count( $parent ) {
455
456 // Check local fields first.
457 if ( acf_have_local_fields( $parent['key'] ) ) {
458 $raw_fields = acf_get_local_fields( $parent['key'] );
459
460 // Then check database.
461 } else {
462 $raw_fields = acf_get_raw_fields( $parent['ID'] );
463 }
464
465 /**
466 * Filters the counted number of fields.
467 *
468 * @date 12/02/2014
469 * @since 5.0.0
470 *
471 * @param int $count The number of fields.
472 * @param array $parent The field group or field array.
473 */
474 return apply_filters( 'acf/get_field_count', count( $raw_fields ), $parent );
475 }
476
477 /**
478 * acf_clone_field
479 *
480 * Allows customization to a field when it is cloned. Used by the clone field.
481 *
482 * @date 8/03/2016
483 * @since 5.3.2
484 *
485 * @param array $field The field being cloned.
486 * @param array $clone_field The clone field.
487 * @return array
488 */
489 function acf_clone_field( $field, $clone_field ) {
490
491 // Add reference to the clone field.
492 $field['_clone'] = $clone_field['key'];
493
494 /**
495 * Filters the $field array when it is being cloned.
496 *
497 * @date 12/02/2014
498 * @since 5.0.0
499 *
500 * @param array $field The field array.
501 * @param array $clone_field The clone field array.
502 */
503 $field = apply_filters( 'acf/clone_field', $field, $clone_field );
504
505 // Return field.
506 return $field;
507 }
508
509 // Register variation.
510 acf_add_filter_variations( 'acf/clone_field', array( 'type' ), 0 );
511
512 /**
513 * acf_prepare_field
514 *
515 * Prepare a field for input.
516 *
517 * @date 20/1/19
518 * @since 5.7.10
519 *
520 * @param array $field The field array.
521 * @return array
522 */
523 function acf_prepare_field( $field ) {
524
525 // Bail early if already prepared.
526 if ( ! empty( $field['_prepare'] ) ) {
527 return $field;
528 }
529
530 // Use field key to override input name.
531 if ( $field['key'] ) {
532 $field['name'] = $field['key'];
533 }
534
535 // Use field prefix to modify input name.
536 if ( ! empty( $field['prefix'] ) ) {
537 $field['name'] = "{$field['prefix']}[{$field['name']}]";
538 }
539
540 // Generate id attribute from name.
541 $field['id'] = acf_idify( $field['name'] );
542
543 // Add state to field.
544 $field['_prepare'] = true;
545
546 /**
547 * Filters the $field array.
548 *
549 * Allows developers to modify field settings or return false to remove field.
550 *
551 * @date 12/02/2014
552 * @since 5.0.0
553 *
554 * @param array $field The field array.
555 */
556 $field = apply_filters( 'acf/prepare_field', $field );
557
558 // return
559 return $field;
560 }
561
562 // Register variation.
563 acf_add_filter_variations( 'acf/prepare_field', array( 'type', 'name', 'key' ), 0 );
564
565 /**
566 * acf_render_fields
567 *
568 * Renders an array of fields. Also loads the field's value.
569 *
570 * @date 8/10/13
571 * @since 5.0.0
572 * @since 5.6.9 Changed parameter order.
573 *
574 * @param array $fields An array of fields.
575 * @param (int|string) $post_id The post ID to load values from.
576 * @param string $element The wrapping element type.
577 * @param string $instruction The instruction render position (label|field).
578 * @return void
579 */
580 function acf_render_fields( $fields, $post_id = 0, $el = 'div', $instruction = 'label' ) {
581
582 // Parameter order changed in ACF 5.6.9.
583 if ( is_array( $post_id ) ) {
584 $args = func_get_args();
585 $fields = $args[1];
586 $post_id = $args[0];
587 }
588
589 /**
590 * Filters the $fields array before they are rendered.
591 *
592 * @date 12/02/2014
593 * @since 5.0.0
594 *
595 * @param array $fields An array of fields.
596 * @param (int|string) $post_id The post ID to load values from.
597 */
598 $fields = apply_filters( 'acf/pre_render_fields', $fields, $post_id );
599
600 // Filter our false results.
601 $fields = array_filter( $fields );
602
603 // Loop over and render fields.
604 if ( $fields ) {
605 foreach ( $fields as $field ) {
606
607 $field = apply_filters( 'acf/pre_render_field', $field, $post_id );
608
609 // Load value if not already loaded.
610 if ( $field['value'] === null ) {
611 $field['value'] = acf_get_value( $post_id, $field );
612 }
613
614 // Render wrap.
615 acf_render_field_wrap( $field, $el, $instruction );
616 }
617 }
618
619 /**
620 * Fires after fields have been rendered.
621 *
622 * @date 12/02/2014
623 * @since 5.0.0
624 *
625 * @param array $fields An array of fields.
626 * @param (int|string) $post_id The post ID to load values from.
627 */
628 do_action( 'acf/render_fields', $fields, $post_id );
629 }
630
631 /**
632 * Render the wrapping element for a given field.
633 *
634 * @since 5.0.0
635 *
636 * @param array $field The field array.
637 * @param string $element The wrapping element type.
638 * @param string $instruction The instruction render position (label|field).
639 * @param bool $field_setting If a field setting is being rendered.
640 * @return void
641 */
642 function acf_render_field_wrap( $field, $element = 'div', $instruction = 'label', $field_setting = false ) {
643
644 // Ensure field is complete (adds all settings).
645 $field = acf_validate_field( $field );
646
647 // Prepare field for input (modifies settings).
648 $field = acf_prepare_field( $field );
649
650 // Allow filters to cancel render.
651 if ( ! $field ) {
652 return;
653 }
654
655 // Determine wrapping element.
656 $elements = array(
657 'div' => 'div',
658 'tr' => 'td',
659 'td' => 'div',
660 'ul' => 'li',
661 'ol' => 'li',
662 'dl' => 'dt',
663 );
664
665 if ( isset( $elements[ $element ] ) ) {
666 $inner_element = $elements[ $element ];
667 } else {
668 $element = $inner_element = 'div';
669 }
670
671 // Generate wrapper attributes.
672 $wrapper = array(
673 'id' => '',
674 'class' => 'acf-field',
675 'width' => '',
676 'style' => '',
677 'data-name' => $field['_name'],
678 'data-type' => $field['type'],
679 'data-key' => $field['key'],
680 );
681
682 // Add field type attributes.
683 $wrapper['class'] .= " acf-field-{$field['type']}";
684
685 // add field key attributes
686 if ( $field['key'] ) {
687 $wrapper['class'] .= " acf-field-{$field['key']}";
688 }
689
690 // Add required attributes.
691 // Todo: Remove data-required
692 if ( $field['required'] ) {
693 $wrapper['class'] .= ' is-required';
694 $wrapper['data-required'] = 1;
695 }
696
697 // Clean up class attribute.
698 $wrapper['class'] = str_replace( '_', '-', $wrapper['class'] );
699 $wrapper['class'] = str_replace( 'field-field-', 'field-', $wrapper['class'] );
700
701 // Merge in field 'wrapper' setting without destroying class and style.
702 if ( $field['wrapper'] ) {
703 $wrapper = acf_merge_attributes( $wrapper, $field['wrapper'] );
704 }
705
706 // Extract wrapper width and generate style.
707 // Todo: Move from $wrapper out into $field.
708 $width = acf_extract_var( $wrapper, 'width' );
709 if ( $width ) {
710 $width = acf_numval( $width );
711 if ( $element !== 'tr' && $element !== 'td' ) {
712 $wrapper['data-width'] = $width;
713 $wrapper['style'] .= " width:{$width}%;";
714 }
715 }
716
717 // Clean up all attributes.
718 $wrapper = array_map( 'trim', $wrapper );
719 $wrapper = array_filter( $wrapper );
720
721 /**
722 * Filters the $wrapper array before rendering.
723 *
724 * @date 21/1/19
725 * @since 5.7.10
726 *
727 * @param array $wrapper The wrapper attributes array.
728 * @param array $field The field array.
729 */
730 $wrapper = apply_filters( 'acf/field_wrapper_attributes', $wrapper, $field );
731
732 // Append conditional logic attributes.
733 if ( ! empty( $field['conditional_logic'] ) ) {
734 $wrapper['data-conditions'] = $field['conditional_logic'];
735 }
736 if ( ! empty( $field['conditions'] ) ) {
737 $wrapper['data-conditions'] = $field['conditions'];
738 }
739
740 // Vars for render.
741 $attributes_html = acf_esc_attr( $wrapper );
742
743 // Render HTML
744 echo "<$element $attributes_html>" . "\n";
745 if ( $element !== 'td' ) {
746 echo "<$inner_element class=\"acf-label\">" . "\n";
747 acf_render_field_label( $field );
748 if ( $instruction == 'label' && 'name' !== $field['_name'] ) {
749 acf_render_field_instructions( $field, $field_setting );
750 }
751 echo "</$inner_element>" . "\n";
752 }
753 echo "<$inner_element class=\"acf-input\">" . "\n";
754 acf_render_field( $field );
755 if ( ! $field_setting && $instruction == 'field' ) {
756 acf_render_field_instructions( $field );
757 }
758 echo "</$inner_element>" . "\n";
759
760 if ( 'name' === $field['_name'] ) {
761 acf_render_field_instructions( $field, $field_setting );
762 }
763
764 if ( $field_setting && $instruction == 'field' ) {
765 acf_render_field_instructions( $field );
766 }
767 echo "</$element>" . "\n";
768 }
769
770 /**
771 * acf_render_field
772 *
773 * Render the input element for a given field.
774 *
775 * @date 21/1/19
776 * @since 5.7.10
777 *
778 * @param array $field The field array.
779 * @return void
780 */
781 function acf_render_field( $field ) {
782
783 // Ensure field is complete (adds all settings).
784 $field = acf_validate_field( $field );
785
786 // Prepare field for input (modifies settings).
787 $field = acf_prepare_field( $field );
788
789 // Allow filters to cancel render.
790 if ( ! $field ) {
791 return;
792 }
793
794 /**
795 * Fires when rendering a field.
796 *
797 * @date 12/02/2014
798 * @since 5.0.0
799 *
800 * @param array $field The field array.
801 */
802 do_action( 'acf/render_field', $field );
803 }
804
805 // Register variation.
806 acf_add_action_variations( 'acf/render_field', array( 'type', 'name', 'key' ), 0 );
807
808 /**
809 * acf_render_field_label
810 *
811 * Renders the field's label.
812 *
813 * @date 19/9/17
814 * @since 5.6.3
815 *
816 * @param array $field The field array.
817 * @return void
818 */
819 function acf_render_field_label( $field ) {
820
821 // Get label.
822 $label = acf_get_field_label( $field );
823
824 // Output label.
825 if ( $label ) {
826 echo '<label' . ( $field['id'] ? ' for="' . esc_attr( $field['id'] ) . '"' : '' ) . '>' . acf_esc_html( $label ) . '</label>';
827 }
828 }
829
830 /**
831 * acf_get_field_label
832 *
833 * Returns the field's label with appropriate required label.
834 *
835 * @date 4/11/2013
836 * @since 5.0.0
837 *
838 * @param array $field The field array.
839 * @param string $context The output context (admin).
840 * @return void
841 */
842 function acf_get_field_label( $field, $context = '' ) {
843
844 // Get label.
845 $label = $field['label'];
846
847 // Display empty text when editing field.
848 if ( $context == 'admin' && $label === '' ) {
849 $label = __( '(no label)', 'acf' );
850 }
851
852 // Add required HTML.
853 if ( $field['required'] ) {
854 $label .= ' <span class="acf-required">*</span>';
855 }
856
857 /**
858 * Filters the field's label HTML.
859 *
860 * @date 21/1/19
861 * @since 5.7.10
862 *
863 * @param string The label HTML.
864 * @param array $field The field array.
865 * @param string $context The output context (admin).
866 */
867 $label = apply_filters( 'acf/get_field_label', $label, $field, $context );
868
869 // Return label.
870 return $label;
871 }
872
873 /**
874 * Renders the field's instructions.
875 *
876 * @since 5.6.3
877 *
878 * @param array $field The field array.
879 * @param boolean $tooltip If the instructions are being rendered as a tooltip.
880 * @return void
881 */
882 function acf_render_field_instructions( $field, $tooltip = false ) {
883 if ( $field['instructions'] ) {
884 $instructions = acf_esc_html( $field['instructions'] );
885
886 if ( $tooltip ) {
887 printf( '<div class="acf-tip"><i tabindex="0" class="acf-icon acf-icon-help acf-js-tooltip" title="%s">?</i></div>', $instructions );
888 } else {
889 printf( '<p class="description">%s</p>', $instructions );
890 }
891 } elseif ( ! empty( $field['hint'] ) ) {
892 $instructions = acf_esc_html( $field['hint'] );
893 printf( '<p class="description">%s</p>', $instructions );
894 }
895 }
896
897 /**
898 * acf_render_field_setting
899 *
900 * Renders a field setting used in the admin edit screen.
901 *
902 * @date 21/1/19
903 * @since 5.7.10
904 *
905 * @param array $field The field array.
906 * @param array $setting The settings field array.
907 * @param bool $global Whether this setting is a global or field type specific one.
908 * @return void
909 */
910 function acf_render_field_setting( $field, $setting, $global = false ) {
911
912 // Validate field.
913 $setting = acf_validate_field( $setting );
914
915 // Add custom attributes to setting wrapper.
916 $setting['wrapper']['data-key'] = $setting['name'];
917 $setting['wrapper']['class'] .= ' acf-field-setting-' . $setting['name'];
918 if ( ! $global ) {
919 $setting['wrapper']['data-setting'] = $field['type'];
920 }
921
922 // Copy across prefix.
923 $setting['prefix'] = $field['prefix'];
924
925 // Find setting value from field.
926 if ( $setting['value'] === null ) {
927
928 // Name.
929 if ( isset( $field[ $setting['name'] ] ) ) {
930 $setting['value'] = $field[ $setting['name'] ];
931
932 // Default value.
933 } elseif ( isset( $setting['default_value'] ) ) {
934 $setting['value'] = $setting['default_value'];
935 }
936 }
937
938 // Add append attribute used by JS to join settings.
939 if ( isset( $setting['_append'] ) ) {
940 $setting['wrapper']['data-append'] = $setting['_append'];
941 }
942
943 // If we're using a hint, set the label location as field so it appears after.
944 $label_location = ! empty( $setting['instructions'] ) ? 'label' : 'field';
945
946 // Render setting.
947 acf_render_field_wrap( $setting, 'div', $label_location, true );
948 }
949
950 /**
951 * acf_update_field
952 *
953 * Updates a field in the database.
954 *
955 * @date 21/1/19
956 * @since 5.7.10
957 *
958 * @param array $field The field array.
959 * @param array $specific An array of specific field attributes to update.
960 * @return array
961 */
962 function acf_update_field( $field, $specific = array() ) {
963
964 // Validate field.
965 $field = acf_validate_field( $field );
966
967 // May have been posted. Remove slashes.
968 $field = wp_unslash( $field );
969
970 // Parse types (converts string '0' to int 0).
971 $field = acf_parse_types( $field );
972
973 // Clean up conditional logic keys.
974 if ( $field['conditional_logic'] ) {
975
976 // Remove empty values and convert to associated array.
977 $field['conditional_logic'] = array_filter( $field['conditional_logic'] );
978 $field['conditional_logic'] = array_values( $field['conditional_logic'] );
979 $field['conditional_logic'] = array_map( 'array_filter', $field['conditional_logic'] );
980 $field['conditional_logic'] = array_map( 'array_values', $field['conditional_logic'] );
981 }
982
983 // Parent may be provided as a field key.
984 if ( $field['parent'] && ! is_numeric( $field['parent'] ) ) {
985 $parent = acf_get_field_post( $field['parent'] );
986 $field['parent'] = $parent ? $parent->ID : 0;
987 }
988
989 /**
990 * Filters the $field array before it is updated.
991 *
992 * @date 12/02/2014
993 * @since 5.0.0
994 *
995 * @param array $field The field array.
996 */
997 $field = apply_filters( 'acf/update_field', $field );
998
999 // Make a backup of field data and remove some args.
1000 $_field = $field;
1001 acf_extract_vars( $_field, array( 'ID', 'key', 'label', 'name', 'prefix', 'value', 'menu_order', 'id', 'class', 'parent', '_name', '_prepare', '_valid' ) );
1002
1003 // Create array of data to save.
1004 $save = array(
1005 'ID' => $field['ID'],
1006 'post_status' => 'publish',
1007 'post_type' => 'acf-field',
1008 'post_title' => $field['label'],
1009 'post_name' => $field['key'],
1010 'post_excerpt' => $field['name'],
1011 'post_content' => maybe_serialize( $_field ),
1012 'post_parent' => $field['parent'],
1013 'menu_order' => $field['menu_order'],
1014 );
1015
1016 // Reduce save data if specific key list is provided.
1017 if ( $specific ) {
1018 $specific[] = 'ID';
1019 $save = acf_get_sub_array( $save, $specific );
1020 }
1021
1022 // Unhook wp_targeted_link_rel() filter from WP 5.1 corrupting serialized data.
1023 remove_filter( 'content_save_pre', 'wp_targeted_link_rel' );
1024
1025 // Slash data.
1026 // WP expects all data to be slashed and will unslash it (fixes '\' character issues).
1027 $save = wp_slash( $save );
1028
1029 // Update or Insert.
1030 if ( $field['ID'] ) {
1031 wp_update_post( $save );
1032 } else {
1033 $field['ID'] = wp_insert_post( $save );
1034 }
1035
1036 // Flush field cache.
1037 acf_flush_field_cache( $field );
1038
1039 /**
1040 * Fires after a field has been updated, and the field cache has been cleaned.
1041 *
1042 * @date 24/1/19
1043 * @since 5.7.10
1044 *
1045 * @param array $field The field array.
1046 */
1047 do_action( 'acf/updated_field', $field );
1048
1049 // Return field.
1050 return $field;
1051 }
1052
1053 // Register variation.
1054 acf_add_filter_variations( 'acf/update_field', array( 'type', 'name', 'key' ), 0 );
1055
1056 /**
1057 * _acf_apply_unique_field_slug
1058 *
1059 * Allows full control over 'acf-field' slugs.
1060 *
1061 * @date 21/1/19
1062 * @since 5.7.10
1063 *
1064 * @param string $slug The post slug.
1065 * @param int $post_ID Post ID.
1066 * @param string $post_status The post status.
1067 * @param string $post_type Post type.
1068 * @param int $post_parent Post parent ID
1069 * @param string $original_slug The original post slug.
1070 */
1071 function _acf_apply_unique_field_slug( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
1072
1073 // Check post type and reset to original value.
1074 if ( $post_type === 'acf-field' ) {
1075 return $original_slug;
1076 }
1077
1078 // Return slug.
1079 return $slug;
1080 }
1081
1082 // Hook into filter.
1083 add_filter( 'wp_unique_post_slug', '_acf_apply_unique_field_slug', 999, 6 );
1084
1085 /**
1086 * acf_flush_field_cache
1087 *
1088 * Deletes all caches for this field.
1089 *
1090 * @date 22/1/19
1091 * @since 5.7.10
1092 *
1093 * @param array $field The field array.
1094 * @return void
1095 */
1096 function acf_flush_field_cache( $field ) {
1097
1098 // Delete stored data.
1099 acf_get_store( 'fields' )->remove( $field['key'] );
1100
1101 // Flush cached post_id for this field's name and key.
1102 wp_cache_delete( acf_cache_key( "acf_get_field_post:name:{$field['name']}" ), 'acf' );
1103 wp_cache_delete( acf_cache_key( "acf_get_field_post:key:{$field['key']}" ), 'acf' );
1104
1105 // Flush cached array of post_ids for this field's parent.
1106 wp_cache_delete( acf_cache_key( "acf_get_field_posts:{$field['parent']}" ), 'acf' );
1107 }
1108
1109 /**
1110 * acf_delete_field
1111 *
1112 * Deletes a field from the database.
1113 *
1114 * @date 21/1/19
1115 * @since 5.7.10
1116 *
1117 * @param (int|string) $id The field ID, key or name.
1118 * @return bool True if field was deleted.
1119 */
1120 function acf_delete_field( $id = 0 ) {
1121
1122 // Get the field.
1123 $field = acf_get_field( $id );
1124
1125 // Bail early if field was not found.
1126 if ( ! $field || ! $field['ID'] ) {
1127 return false;
1128 }
1129
1130 // Delete post.
1131 wp_delete_post( $field['ID'], true );
1132
1133 // Flush field cache.
1134 acf_flush_field_cache( $field );
1135
1136 /**
1137 * Fires immediately after a field has been deleted.
1138 *
1139 * @date 12/02/2014
1140 * @since 5.0.0
1141 *
1142 * @param array $field The field array.
1143 */
1144 do_action( 'acf/delete_field', $field );
1145
1146 // Return true.
1147 return true;
1148 }
1149
1150 // Register variation.
1151 acf_add_action_variations( 'acf/delete_field', array( 'type', 'name', 'key' ), 0 );
1152
1153 /**
1154 * acf_trash_field
1155 *
1156 * Trashes a field from the database.
1157 *
1158 * @date 2/10/13
1159 * @since 5.0.0
1160 *
1161 * @param (int|string) $id The field ID, key or name.
1162 * @return bool True if field was trashed.
1163 */
1164 function acf_trash_field( $id = 0 ) {
1165
1166 // Get the field.
1167 $field = acf_get_field( $id );
1168
1169 // Bail early if field was not found.
1170 if ( ! $field || ! $field['ID'] ) {
1171 return false;
1172 }
1173
1174 // Trash post.
1175 wp_trash_post( $field['ID'], true );
1176
1177 /**
1178 * Fires immediately after a field has been trashed.
1179 *
1180 * @date 12/02/2014
1181 * @since 5.0.0
1182 *
1183 * @param array $field The field array.
1184 */
1185 do_action( 'acf/trash_field', $field );
1186
1187 // Return true.
1188 return true;
1189 }
1190
1191 /**
1192 * acf_untrash_field
1193 *
1194 * Restores a field from the trash.
1195 *
1196 * @date 2/10/13
1197 * @since 5.0.0
1198 *
1199 * @param (int|string) $id The field ID, key or name.
1200 * @return bool True if field was trashed.
1201 */
1202 function acf_untrash_field( $id = 0 ) {
1203
1204 // Get the field.
1205 $field = acf_get_field( $id );
1206
1207 // Bail early if field was not found.
1208 if ( ! $field || ! $field['ID'] ) {
1209 return false;
1210 }
1211
1212 // Untrash post.
1213 wp_untrash_post( $field['ID'], true );
1214
1215 // Flush field cache.
1216 acf_flush_field_cache( $field );
1217
1218 /**
1219 * Fires immediately after a field has been trashed.
1220 *
1221 * @date 12/02/2014
1222 * @since 5.0.0
1223 *
1224 * @param array $field The field array.
1225 */
1226 do_action( 'acf/untrash_field', $field );
1227
1228 // Return true.
1229 return true;
1230 }
1231
1232 /**
1233 * Filter callback which returns the previous post_status instead of "draft" for the "acf-field" post type.
1234 *
1235 * Prior to WordPress 5.6.0, this filter was not needed as restored posts were always assigned their original status.
1236 *
1237 * @since 5.9.5
1238 *
1239 * @param string $new_status The new status of the post being restored.
1240 * @param int $post_id The ID of the post being restored.
1241 * @param string $previous_status The status of the post at the point where it was trashed.
1242 * @return string.
1243 */
1244 function _acf_untrash_field_post_status( $new_status, $post_id, $previous_status ) {
1245 return ( get_post_type( $post_id ) === 'acf-field' ) ? $previous_status : $new_status;
1246 }
1247
1248 add_action( 'wp_untrash_post_status', '_acf_untrash_field_post_status', 10, 3 );
1249
1250 /**
1251 * acf_prefix_fields
1252 *
1253 * Changes the prefix for an array of fields by reference.
1254 *
1255 * @date 5/9/17
1256 * @since 5.6.0
1257 *
1258 * @param array $fields An array of fields.
1259 * @param string $prefix The new prefix.
1260 * @return void
1261 */
1262 function acf_prefix_fields( &$fields, $prefix = 'acf' ) {
1263
1264 // Loopover fields.
1265 foreach ( $fields as &$field ) {
1266
1267 // Replace 'acf' with $prefix.
1268 $field['prefix'] = $prefix . substr( $field['prefix'], 3 );
1269 }
1270 }
1271
1272 /**
1273 * acf_get_sub_field
1274 *
1275 * Searches a field for sub fields matching the given selector.
1276 *
1277 * @date 21/1/19
1278 * @since 5.7.10
1279 *
1280 * @param (int|string) $id The field ID, key or name.
1281 * @param array $field The parent field array.
1282 * @return (array|false)
1283 */
1284 function acf_get_sub_field( $id, $field ) {
1285
1286 // Vars.
1287 $sub_field = false;
1288
1289 // Search sub fields.
1290 if ( isset( $field['sub_fields'] ) ) {
1291 $sub_field = acf_search_fields( $id, $field['sub_fields'] );
1292 }
1293
1294 /**
1295 * Filters the $sub_field found.
1296 *
1297 * @date 12/02/2014
1298 * @since 5.0.0
1299 *
1300 * @param array $sub_field The found sub field array.
1301 * @param string $selector The selector used to search.
1302 * @param array $field The parent field array.
1303 */
1304 $sub_field = apply_filters( 'acf/get_sub_field', $sub_field, $id, $field );
1305
1306 // return
1307 return $sub_field;
1308
1309 }
1310
1311 // Register variation.
1312 acf_add_filter_variations( 'acf/get_sub_field', array( 'type' ), 2 );
1313
1314 /**
1315 * acf_search_fields
1316 *
1317 * Searches an array of fields for one that matches the given identifier.
1318 *
1319 * @date 12/2/19
1320 * @since 5.7.11
1321 *
1322 * @param (int|string) $id The field ID, key or name.
1323 * @param array $haystack The array of fields.
1324 * @return (int|false)
1325 */
1326 function acf_search_fields( $id, $fields ) {
1327
1328 // Loop over searchable keys in order of priority.
1329 // Important to search "name" on all fields before "_name" backup.
1330 foreach ( array( 'key', 'name', '_name', '__name' ) as $key ) {
1331
1332 // Loop over fields and compare.
1333 foreach ( $fields as $field ) {
1334 if ( isset( $field[ $key ] ) && $field[ $key ] === $id ) {
1335 return $field;
1336 }
1337 }
1338 }
1339
1340 // Return not found.
1341 return false;
1342 }
1343
1344 /**
1345 * acf_is_field
1346 *
1347 * Returns true if the given params match a field.
1348 *
1349 * @date 21/1/19
1350 * @since 5.7.10
1351 *
1352 * @param array $field The field array.
1353 * @param mixed $id An optional identifier to search for.
1354 * @return bool
1355 */
1356 function acf_is_field( $field = false, $id = '' ) {
1357 return (
1358 is_array( $field )
1359 && isset( $field['key'] )
1360 && isset( $field['name'] )
1361 );
1362 }
1363
1364 /**
1365 * acf_get_field_ancestors
1366 *
1367 * Returns an array of ancestor field ID's or keys.
1368 *
1369 * @date 22/06/2016
1370 * @since 5.3.8
1371 *
1372 * @param array $field The field array.
1373 * @return array
1374 */
1375 function acf_get_field_ancestors( $field ) {
1376
1377 // Vars.
1378 $ancestors = array();
1379
1380 // Loop over parents.
1381 while ( $field['parent'] && $field = acf_get_field( $field['parent'] ) ) {
1382 $ancestors[] = $field['ID'] ? $field['ID'] : $field['key'];
1383 }
1384
1385 // return
1386 return $ancestors;
1387 }
1388
1389 /**
1390 * acf_duplicate_fields
1391 *
1392 * Duplicate an array of fields.
1393 *
1394 * @date 16/06/2014
1395 * @since 5.0.0
1396 *
1397 * @param array $fields An array of fields.
1398 * @param int $parent_id The new parent ID.
1399 * @return array
1400 */
1401 function acf_duplicate_fields( $fields = array(), $parent_id = 0 ) {
1402
1403 // Generate keys for all new fields
1404 // - Needed to alter conditional logic rules
1405 // - Use usleep() to ensure unique keys.
1406 $keys = array();
1407 foreach ( $fields as $field ) {
1408 usleep( 1 );
1409 $keys[ $field['key'] ] = uniqid( 'field_' );
1410 }
1411 acf_append_data( 'generated_keys', $keys );
1412
1413 $duplicates = array();
1414
1415 // Duplicate fields.
1416 foreach ( $fields as $field ) {
1417 $field_id = $field['ID'] ? $field['ID'] : $field['key'];
1418 $duplicates[] = acf_duplicate_field( $field_id, $parent_id );
1419 }
1420
1421 // Return.
1422 return $duplicates;
1423 }
1424
1425 /**
1426 * acf_duplicate_field
1427 *
1428 * Duplicates a field.
1429 *
1430 * @date 16/06/2014
1431 * @since 5.0.0
1432 *
1433 * @param (int|string) $id The field ID, key or name.
1434 * @param int $parent_id The new parent ID.
1435 * @return bool True if field was duplicated.
1436 */
1437 function acf_duplicate_field( $id = 0, $parent_id = 0 ) {
1438
1439 // Get the field.
1440 $field = acf_get_field( $id );
1441
1442 // Bail early if field was not found.
1443 if ( ! $field ) {
1444 return false;
1445 }
1446
1447 // Remove ID to avoid update.
1448 $field['ID'] = 0;
1449
1450 // Generate key.
1451 $keys = acf_get_data( 'generated_keys' );
1452 if ( isset( $keys[ $field['key'] ] ) ) {
1453 $field['key'] = $keys[ $field['key'] ];
1454 } else {
1455 $field['key'] = uniqid( 'field_' );
1456 }
1457
1458 // Set parent.
1459 if ( $parent_id ) {
1460 $field['parent'] = $parent_id;
1461 }
1462
1463 // Update conditional logic references because field keys have changed.
1464 if ( $field['conditional_logic'] ) {
1465
1466 // Loop over groups
1467 foreach ( $field['conditional_logic'] as $group_i => $group ) {
1468
1469 // Loop over rules
1470 foreach ( $group as $rule_i => $rule ) {
1471 $field['conditional_logic'][ $group_i ][ $rule_i ]['field'] = isset( $keys[ $rule['field'] ] ) ? $keys[ $rule['field'] ] : $rule['field'];
1472 }
1473 }
1474 }
1475
1476 /**
1477 * Filters the $field array after it has been duplicated.
1478 *
1479 * @date 12/02/2014
1480 * @since 5.0.0
1481 *
1482 * @param array $field The field array.
1483 */
1484 $field = apply_filters( 'acf/duplicate_field', $field );
1485
1486 // Update and return.
1487 return acf_update_field( $field );
1488 }
1489
1490 // Register variation.
1491 acf_add_filter_variations( 'acf/duplicate_field', array( 'type' ), 0 );
1492
1493 /**
1494 * acf_prepare_fields_for_export
1495 *
1496 * Returns a modified array of fields ready for export.
1497 *
1498 * @date 11/03/2014
1499 * @since 5.0.0
1500 *
1501 * @param array $fields An array of fields.
1502 * @return array
1503 */
1504 function acf_prepare_fields_for_export( $fields = array() ) {
1505
1506 // Map function and return.
1507 return array_map( 'acf_prepare_field_for_export', $fields );
1508 }
1509
1510 /**
1511 * acf_prepare_field_for_export
1512 *
1513 * Returns a modified field ready for export.
1514 *
1515 * @date 11/03/2014
1516 * @since 5.0.0
1517 *
1518 * @param array $field The field array.
1519 * @return array
1520 */
1521 function acf_prepare_field_for_export( $field ) {
1522
1523 // Remove args.
1524 acf_extract_vars( $field, array( 'ID', 'prefix', 'value', 'menu_order', 'id', 'class', 'parent', '_name', '_prepare', '_valid' ) );
1525
1526 /**
1527 * Filters the $field array before being returned to the export tool.
1528 *
1529 * @date 12/02/2014
1530 * @since 5.0.0
1531 *
1532 * @param array $field The field array.
1533 */
1534 return apply_filters( 'acf/prepare_field_for_export', $field );
1535 }
1536
1537 // Register variation.
1538 acf_add_filter_variations( 'acf/prepare_field_for_export', array( 'type' ), 0 );
1539
1540 /**
1541 * acf_prepare_field_for_import
1542 *
1543 * Returns a modified array of fields ready for import.
1544 *
1545 * @date 11/03/2014
1546 * @since 5.0.0
1547 *
1548 * @param array $fields An array of fields.
1549 * @return array
1550 */
1551 function acf_prepare_fields_for_import( $fields = array() ) {
1552
1553 // Ensure array is sequential.
1554 $fields = array_values( $fields );
1555
1556 // Prepare each field for import making sure to detect additional sub fields.
1557 $i = 0;
1558 while ( $i < count( $fields ) ) {
1559
1560 // Prepare field.
1561 $field = acf_prepare_field_for_import( $fields[ $i ] );
1562
1563 // Update single field.
1564 if ( isset( $field['key'] ) ) {
1565 $fields[ $i ] = $field;
1566
1567 // Insert multiple fields.
1568 } else {
1569 array_splice( $fields, $i, 1, $field );
1570 }
1571
1572 // Iterate.
1573 $i++;
1574 }
1575
1576 /**
1577 * Filters the $fields array before being returned to the import tool.
1578 *
1579 * @date 12/02/2014
1580 * @since 5.0.0
1581 *
1582 * @param array $fields The array of fields.
1583 */
1584 return apply_filters( 'acf/prepare_fields_for_import', $fields );
1585 }
1586
1587 /**
1588 * acf_prepare_field_for_import
1589 *
1590 * Returns a modified field ready for import.
1591 * Allows parent fields to modify themselves and also return sub fields.
1592 *
1593 * @date 11/03/2014
1594 * @since 5.0.0
1595 *
1596 * @param array $field The field array.
1597 * @return array
1598 */
1599 function acf_prepare_field_for_import( $field ) {
1600
1601 /**
1602 * Filters the $field array before being returned to the import tool.
1603 *
1604 * @date 12/02/2014
1605 * @since 5.0.0
1606 *
1607 * @param array $field The field array.
1608 */
1609 return apply_filters( 'acf/prepare_field_for_import', $field );
1610 }
1611
1612 // Register variation.
1613 acf_add_filter_variations( 'acf/prepare_field_for_import', array( 'type' ), 0 );
1614