PluginProbe
Advanced Custom Fields (ACF®) / 5.6.6
Advanced Custom Fields (ACF®) v5.6.6
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 / api / api-field.php
api-field.php
2,024 lines 34.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * acf_is_field_key
5 *
6 * This function will return true or false for the given $field_key parameter
7 *
8 * @type function
9 * @date 6/12/2013
10 * @since 5.0.0
11 *
12 * @param $field_key (string)
13 * @return (boolean)
14 */
15
16 function acf_is_field_key( $key = '' ) {
17
18 // bail early if not string
19 if( !is_string($key) ) return false;
20
21
22 // bail early if is numeric (could be numeric string '123')
23 if( is_numeric($key) ) return false;
24
25
26 // default - starts with 'field_'
27 if( substr($key, 0, 6) === 'field_' ) return true;
28
29
30 // special - allow local field key to be any string
31 if( acf_is_local_field_key($key) ) return true;
32
33
34 // return
35 return false;
36
37 }
38
39
40 /*
41 * acf_get_valid_field
42 *
43 * This function will fill in any missing keys to the $field array making it valid
44 *
45 * @type function
46 * @date 28/09/13
47 * @since 5.0.0
48 *
49 * @param $field (array)
50 * @return $field (array)
51 */
52
53 function acf_get_valid_field( $field = false ) {
54
55 // $field must be an array
56 if( !is_array($field) ) $field = array();
57
58
59 // bail ealry if already valid
60 if( !empty($field['_valid']) ) return $field;
61
62
63 // defaults
64 $field = wp_parse_args($field, array(
65 'ID' => 0,
66 'key' => '',
67 'label' => '',
68 'name' => '',
69 'prefix' => '',
70 'type' => 'text',
71 'value' => null,
72 'menu_order' => 0,
73 'instructions' => '',
74 'required' => 0,
75 'id' => '',
76 'class' => '',
77 'conditional_logic' => 0,
78 'parent' => 0,
79 'wrapper' => array(),
80 '_name' => '',
81 '_prepare' => 0,
82 '_valid' => 0,
83 ));
84
85 $field['wrapper'] = wp_parse_args($field['wrapper'], array(
86 'width' => '',
87 'class' => '',
88 'id' => ''
89 ));
90
91
92 // _name
93 $field['_name'] = $field['name'];
94
95
96 // field is now valid
97 $field['_valid'] = 1;
98
99
100 // field specific defaults
101 $field = apply_filters( "acf/validate_field", $field );
102 $field = apply_filters( "acf/validate_field/type={$field['type']}", $field );
103
104
105 // translate
106 $field = acf_translate_field( $field );
107
108
109 // return
110 return $field;
111
112 }
113
114
115 /*
116 * acf_translate_field
117 *
118 * This function will translate field's settings
119 *
120 * @type function
121 * @date 8/03/2016
122 * @since 5.3.2
123 *
124 * @param $field (array)
125 * @return $field
126 */
127
128 function acf_translate_field( $field ) {
129
130 // vars
131 $l10n = acf_get_setting('l10n');
132 $l10n_textdomain = acf_get_setting('l10n_textdomain');
133
134
135 // if
136 if( $l10n && $l10n_textdomain ) {
137
138 // translate
139 $field['label'] = acf_translate( $field['label'] );
140 $field['instructions'] = acf_translate( $field['instructions'] );
141
142
143 // filters
144 $field = apply_filters( "acf/translate_field", $field );
145 $field = apply_filters( "acf/translate_field/type={$field['type']}", $field );
146
147 }
148
149
150 // return
151 return $field;
152
153 }
154
155
156 /*
157 * acf_clone_field
158 *
159 * This function will allow customization to a field when it is cloned
160 * Cloning a field is the act of mimicing another. Some settings may need to be altered
161 *
162 * @type function
163 * @date 8/03/2016
164 * @since 5.3.2
165 *
166 * @param $field (array)
167 * @return $field
168 */
169
170 function acf_clone_field( $field, $clone_field ) {
171
172 // add reference
173 $field['_clone'] = $clone_field['key'];
174
175
176 // filters
177 $field = apply_filters( "acf/clone_field", $field, $clone_field );
178 $field = apply_filters( "acf/clone_field/type={$field['type']}", $field, $clone_field );
179
180
181 // return
182 return $field;
183
184 }
185
186
187 /*
188 * acf_prepare_field
189 *
190 * This function will prepare the field for input
191 *
192 * @type function
193 * @date 12/02/2014
194 * @since 5.0.0
195 *
196 * @param $field (array)
197 * @return $field (array)
198 */
199
200 function acf_prepare_field( $field ) {
201
202 // bail early if already prepared
203 if( $field['_prepare'] ) return $field;
204
205
206 // key overrides name
207 if( $field['key'] ) $field['name'] = $field['key'];
208
209
210 // prefix
211 if( $field['prefix'] ) $field['name'] = $field['prefix'] . '[' . $field['name'] . ']';
212
213
214 // field is now prepared
215 $field['_prepare'] = 1;
216
217
218 // filter to 3rd party customization
219 $field = apply_filters( "acf/prepare_field", $field );
220 $field = apply_filters( "acf/prepare_field/type={$field['type']}", $field );
221 $field = apply_filters( "acf/prepare_field/name={$field['_name']}", $field );
222 $field = apply_filters( "acf/prepare_field/key={$field['key']}", $field );
223
224
225 // bail ealry if no field
226 if( !$field ) return false;
227
228
229 // id attr is generated from name
230 $field['id'] = str_replace(array('][', '[', ']'), array('-', '-', ''), $field['name']);
231
232
233 // return
234 return $field;
235
236 }
237
238
239 /*
240 * acf_is_sub_field
241 *
242 * This function will return true if the field is a sub field
243 *
244 * @type function
245 * @date 17/05/2014
246 * @since 5.0.0
247 *
248 * @param $field (array)
249 * @return (boolean)
250 */
251
252 function acf_is_sub_field( $field ) {
253
254 // local field uses a field instead of ID
255 if( acf_is_field_key($field['parent']) ) return true;
256
257
258 // attempt to load parent field
259 if( acf_get_field($field['parent']) ) return true;
260
261
262 // return
263 return false;
264
265 }
266
267
268 /*
269 * acf_get_field_label
270 *
271 * This function will return the field label with appropriate required label
272 *
273 * @type function
274 * @date 4/11/2013
275 * @since 5.0.0
276 *
277 * @param $field (array)
278 * @return $label (string)
279 */
280
281 function acf_get_field_label( $field ) {
282
283 // vars
284 $label = $field['label'];
285
286
287 // required
288 if( $field['required'] ) {
289 $label .= ' <span class="acf-required">*</span>';
290 }
291
292
293 // filter for 3rd party customization
294 $label = apply_filters("acf/get_field_label", $label, $field);
295
296
297 // return
298 return $label;
299
300 }
301
302 function acf_the_field_label( $field ) {
303
304 echo acf_get_field_label( $field );
305
306 }
307
308
309 /*
310 * acf_render_fields
311 *
312 * This function will render an array of fields for a given form.
313 * Becasue the $field's values have not been loaded yet, this function will also load values
314 *
315 * @type function
316 * @date 8/10/13
317 * @since 5.0.0
318 *
319 * @param $post_id (int) the post to load values from
320 * @param $fields (array) the fields to render
321 * @param $el (string) the wrapping element type
322 * @param $instruction (int) the instructions position
323 * @return n/a
324 */
325
326 function acf_render_fields( $post_id = 0, $fields, $el = 'div', $instruction = 'label' ) {
327
328 // bail early if no fields
329 if( empty($fields) ) return false;
330
331
332 // remove corrupt fields
333 $fields = array_filter($fields);
334
335
336 // loop through fields
337 foreach( $fields as $field ) {
338
339 // load value
340 if( $field['value'] === null ) {
341
342 $field['value'] = acf_get_value( $post_id, $field );
343
344 }
345
346
347 // render
348 acf_render_field_wrap( $field, $el, $instruction );
349
350 }
351
352 }
353
354
355 /*
356 * acf_render_field
357 *
358 * This function will render a field input
359 *
360 * @type function
361 * @date 28/09/13
362 * @since 5.0.0
363 *
364 * @param $field (array)
365 * @return n/a
366 */
367
368 function acf_render_field( $field = false ) {
369
370 // get valid field
371 $field = acf_get_valid_field( $field );
372
373
374 // prepare field for input
375 $field = acf_prepare_field( $field );
376
377
378 // bail ealry if no field
379 if( !$field ) return;
380
381
382 // create field specific html
383 do_action( "acf/render_field", $field );
384 do_action( "acf/render_field/type={$field['type']}", $field );
385
386 }
387
388
389 /*
390 * acf_render_field_wrap
391 *
392 * This function will render the complete HTML wrap with label & field
393 *
394 * @type function
395 * @date 28/09/13
396 * @since 5.0.0
397 *
398 * @param $field (array) must be a valid ACF field array
399 * @param $el (string) modifys the rendered wrapping elements. Default to 'div', but can be 'tr', 'ul', 'ol', 'dt' or custom
400 * @param $instruction (string) specifys the placement of the instructions. Default to 'label', but can be 'field'
401 * @param $atts (array) an array of custom attributes to render on the $el
402 * @return N/A
403 */
404
405 function acf_render_field_wrap( $field, $el = 'div', $instruction = 'label' ) {
406
407 // get valid field
408 $field = acf_get_valid_field( $field );
409
410
411 // prepare field for input
412 $field = acf_prepare_field( $field );
413
414
415 // bail ealry if no field
416 if( !$field ) return;
417
418
419 // elements
420 $elements = array(
421 'div' => 'div',
422 'tr' => 'td',
423 'ul' => 'li',
424 'ol' => 'li',
425 'dl' => 'dt',
426 'td' => 'div' // special case for sub field!
427 );
428
429
430 // vars
431 $el = isset($elements[ $el ]) ? $el : 'div';
432 $el2 = $elements[ $el ];
433 $show_label = ($el !== 'td') ? true : false;
434
435
436 // wrapper
437 $wrapper = array(
438 'id' => '',
439 'class' => 'acf-field',
440 'width' => '',
441 'style' => '',
442 'data-name' => $field['_name'],
443 'data-type' => $field['type'],
444 'data-key' => '',
445 );
446
447
448 // add required
449 if( $field['required'] ) {
450 $wrapper['data-required'] = 1;
451 }
452
453
454 // add type
455 $wrapper['class'] .= " acf-field-{$field['type']}";
456
457
458 // add key
459 if( $field['key'] ) {
460
461 $wrapper['class'] .= " acf-field-{$field['key']}";
462 $wrapper['data-key'] = $field['key'];
463
464 }
465
466
467 // replace
468 $wrapper['class'] = str_replace('_', '-', $wrapper['class']);
469 $wrapper['class'] = str_replace('field-field-', 'field-', $wrapper['class']);
470
471
472 // wrap classes have changed (5.2.7)
473 if( acf_get_compatibility('field_wrapper_class') ) {
474
475 $wrapper['class'] .= " field_type-{$field['type']}";
476
477 if( $field['key'] ) {
478
479 $wrapper['class'] .= " field_key-{$field['key']}";
480
481 }
482
483 }
484
485
486 // merge in atts
487 $wrapper = acf_merge_atts( $wrapper, $field['wrapper'] );
488
489
490 // add width
491 $width = (int) acf_extract_var( $wrapper, 'width' );
492
493 if( $el == 'tr' || $el == 'td' ) {
494
495 // do nothing
496
497 } elseif( $width > 0 && $width < 100 ) {
498
499 $wrapper['data-width'] = $width;
500 $wrapper['style'] .= " width:{$width}%;";
501
502 }
503
504
505 // remove empty attributes
506 $wrapper = array_filter($wrapper);
507
508
509 // html
510 ?>
511 <<?php echo $el; ?> <?php acf_esc_attr_e($wrapper); ?>>
512 <?php if( $show_label ): ?>
513 <<?php echo $el2; ?> class="acf-label"><?php
514
515 acf_render_field_label( $field );
516
517 if( $instruction == 'label' ) acf_render_field_instructions( $field );
518
519 ?></<?php echo $el2; ?>>
520 <?php endif; ?>
521 <<?php echo $el2; ?> class="acf-input">
522 <?php acf_render_field( $field ); ?>
523 <?php if( $instruction == 'field' ) acf_render_field_instructions( $field ); ?>
524 <?php if( !empty($field['conditional_logic']) ): ?>
525 <script type="text/javascript">
526 if( typeof acf !== 'undefined' ) {
527 acf.conditional_logic.add( '<?php echo esc_js($field['key']); ?>', <?php echo wp_json_encode( $field['conditional_logic'] ); ?> );
528 }
529 </script>
530 <?php endif; ?>
531 </<?php echo $el2; ?>>
532 </<?php echo $el; ?>>
533 <?php
534
535 }
536
537
538 /**
539 * acf_render_field_label
540 *
541 * This function will maybe output the field's label
542 *
543 * @date 19/9/17
544 * @since 5.6.3
545 *
546 * @param array $field
547 * @return n/a
548 */
549
550 function acf_render_field_label( $field ) {
551
552 // vars
553 $label = acf_get_field_label( $field );
554
555
556 // check
557 if( $label ) {
558 echo '<label' . ($field['id'] ? ' for="' . esc_attr($field['id']) . '"' : '' ) . '>' . acf_esc_html($label) . '</label>';
559 }
560
561 }
562
563
564 /* depreciated since 5.6.5 */
565 function acf_render_field_wrap_label( $field ) {
566 acf_render_field_label( $field );
567 }
568
569
570 /**
571 * acf_render_field_instructions
572 *
573 * This function will maybe output the field's instructions
574 *
575 * @date 19/9/17
576 * @since 5.6.3
577 *
578 * @param array $field
579 * @return n/a
580 */
581
582 function acf_render_field_instructions( $field ) {
583
584 // vars
585 $instructions = $field['instructions'];
586
587
588 // check
589 if( $instructions ) {
590 echo '<p class="description">' . acf_esc_html($instructions) . '</p>';
591 }
592
593 }
594
595
596 /* depreciated since 5.6.5 */
597 function acf_render_field_wrap_description( $field ) {
598 acf_render_field_instructions( $field );
599 }
600
601
602 /*
603 * acf_render_field_setting
604 *
605 * This function will render a tr element containing a label and field cell, but also setting the tr data attribute for AJAX
606 *
607 * @type function
608 * @date 28/09/13
609 * @since 5.0.0
610 *
611 * @param $field (array) the origional field being edited
612 * @param $setting (array) the settings field to create
613 * @return n/a
614 */
615
616 function acf_render_field_setting( $field, $setting, $global = false ) {
617
618 // validate
619 $setting = acf_get_valid_field( $setting );
620
621
622 // specific
623 if( !$global ) {
624
625 $setting['wrapper']['data-setting'] = $field['type'];
626
627 }
628
629
630 // class
631 $setting['wrapper']['class'] .= ' acf-field-setting-' . $setting['name'];
632
633
634 // copy across prefix
635 $setting['prefix'] = $field['prefix'];
636
637
638 // attempt find value
639 if( $setting['value'] === null ) {
640
641 // name
642 if( isset($field[ $setting['name'] ]) ) {
643
644 $setting['value'] = $field[ $setting['name'] ];
645
646 // default
647 } elseif( isset($setting['default_value']) ) {
648
649 $setting['value'] = $setting['default_value'];
650
651 }
652
653 }
654
655
656 // append (used by JS to join settings)
657 if( isset($setting['_append']) ) {
658
659 $setting['wrapper']['data-append'] = $setting['_append'];
660
661 }
662
663
664 // render
665 acf_render_field_wrap( $setting, 'tr', 'label' );
666
667 }
668
669
670 /*
671 * acf_get_fields
672 *
673 * This function will return an array of fields for the given $parent
674 *
675 * @type function
676 * @date 30/09/13
677 * @since 5.0.0
678 *
679 * @param $parent (array) a field or field group
680 * @return (array)
681 */
682
683 function acf_get_fields( $parent = false ) {
684
685 // allow $parent to be a field group ID
686 if( !is_array($parent) ) {
687
688 $parent = acf_get_field_group( $parent );
689
690 }
691
692
693 // bail early if no parent
694 if( !$parent ) return false;
695
696
697 // vars
698 $fields = array();
699
700
701 // try JSON before DB to save query time
702 if( acf_have_local_fields( $parent['key'] ) ) {
703
704 $fields = acf_get_local_fields( $parent['key'] );
705
706 } else {
707
708 $fields = acf_get_fields_by_id( $parent['ID'] );
709
710 }
711
712
713 // filter
714 $fields = apply_filters('acf/get_fields', $fields, $parent);
715
716
717 // return
718 return $fields;
719
720 }
721
722
723 /*
724 * acf_get_fields_by_id
725 *
726 * This function will get all fields for the given parent
727 *
728 * @type function
729 * @date 27/02/2014
730 * @since 5.0.0
731 *
732 * @param $post_id (int)
733 * @return $fields (array)
734 */
735
736 function acf_get_fields_by_id( $parent_id = 0 ) {
737
738 // bail early if no ID
739 if( !$parent_id ) return false;
740
741
742 // vars
743 $fields = array();
744 $post_ids = array();
745 $cache_key = "get_fields/ID={$parent_id}";
746
747
748 // check cache for child ids
749 if( acf_isset_cache($cache_key) ) {
750
751 $post_ids = acf_get_cache($cache_key);
752
753 // query DB for child ids
754 } else {
755
756 // query
757 $posts = get_posts(array(
758 'posts_per_page' => -1,
759 'post_type' => 'acf-field',
760 'orderby' => 'menu_order',
761 'order' => 'ASC',
762 'suppress_filters' => true, // DO NOT allow WPML to modify the query
763 'post_parent' => $parent_id,
764 'post_status' => 'publish, trash', // 'any' won't get trashed fields
765 'update_post_meta_cache' => false
766 ));
767
768
769 // loop
770 if( $posts ) {
771
772 foreach( $posts as $post ) {
773
774 $post_ids[] = $post->ID;
775
776 }
777
778 }
779
780
781 // update cache
782 acf_set_cache($cache_key, $post_ids);
783
784 }
785
786
787 // bail early if no children
788 if( empty($post_ids) ) return false;
789
790
791 // load fields
792 foreach( $post_ids as $post_id ) {
793
794 $fields[] = acf_get_field( $post_id );
795
796 }
797
798
799 // return
800 return $fields;
801
802 }
803
804
805 /*
806 * acf_get_field
807 *
808 * This function will return a field for the given selector.
809 *
810 * @type function
811 * @date 30/09/13
812 * @since 5.0.0
813 *
814 * @param $selector (mixed) identifyer of field. Can be an ID, key, name or post object
815 * @param $db_only (boolean) return $field in it's raw form without filters or cache
816 * @return $field (array)
817 */
818
819 function acf_get_field( $selector = null, $db_only = false ) {
820
821 // vars
822 $field = false;
823 $type = 'ID';
824
825
826 // ID
827 if( is_numeric($selector) ) {
828
829 // do nothing
830
831 // object
832 } elseif( is_object($selector) ) {
833
834 $selector = $selector->ID;
835
836 // string
837 } elseif( is_string($selector) ) {
838
839 $type = acf_is_field_key($selector) ? 'key' : 'name';
840
841 // other
842 } else {
843
844 return false;
845
846 }
847
848
849 // return early if cache is found
850 $cache_key = "get_field/{$type}={$selector}";
851
852 if( !$db_only && acf_isset_cache($cache_key) ) {
853
854 return acf_get_cache($cache_key);
855
856 }
857
858
859 // ID
860 if( $type == 'ID' ) {
861
862 $field = _acf_get_field_by_id( $selector, $db_only );
863
864 // key
865 } elseif( $type == 'key' ) {
866
867 $field = _acf_get_field_by_key( $selector, $db_only );
868
869 // name (rare case)
870 } else {
871
872 $field = _acf_get_field_by_name( $selector, $db_only );
873
874 }
875
876
877 // bail early if no field
878 if( !$field ) return false;
879
880
881 // validate
882 $field = acf_get_valid_field( $field );
883
884
885 // set prefix (acf fields save with prefix 'acf')
886 $field['prefix'] = 'acf';
887
888
889 // bail early if db only value (no need to update cache)
890 if( $db_only ) return $field;
891
892
893 // filter for 3rd party customization
894 $field = apply_filters( "acf/load_field", $field);
895 $field = apply_filters( "acf/load_field/type={$field['type']}", $field );
896 $field = apply_filters( "acf/load_field/name={$field['name']}", $field );
897 $field = apply_filters( "acf/load_field/key={$field['key']}", $field );
898
899
900 // update cache
901 // - Use key instead of ID for best compatibility (not all fields exist in the DB)
902 $cache_key = acf_set_cache("get_field/key={$field['key']}", $field);
903
904
905 // update cache reference
906 // - allow cache to return if using an ID selector
907 acf_set_cache_reference("get_field/ID={$field['ID']}", $cache_key);
908
909
910 // return
911 return $field;
912
913 }
914
915
916 /*
917 * _acf_get_field_by_id
918 *
919 * This function will get a field via its ID
920 *
921 * @type function
922 * @date 27/02/2014
923 * @since 5.0.0
924 *
925 * @param $post_id (int)
926 * @return $field (array)
927 */
928
929 function _acf_get_field_by_id( $post_id = 0, $db_only = false ) {
930
931 // get post
932 $post = get_post( $post_id );
933
934
935 // bail early if no post, or is not a field
936 if( empty($post) || $post->post_type != 'acf-field' ) return false;
937
938
939 // unserialize
940 $field = maybe_unserialize( $post->post_content );
941
942
943 // update attributes
944 $field['ID'] = $post->ID;
945 $field['key'] = $post->post_name;
946 $field['label'] = $post->post_title;
947 $field['name'] = $post->post_excerpt;
948 $field['menu_order'] = $post->menu_order;
949 $field['parent'] = $post->post_parent;
950
951
952 // override with JSON
953 if( !$db_only && acf_is_local_field($field['key']) ) {
954
955 // load JSON field
956 $local = acf_get_local_field( $field['key'] );
957
958
959 // override IDs
960 $local['ID'] = $field['ID'];
961 $local['parent'] = $field['parent'];
962
963
964 // return
965 return $local;
966
967 }
968
969
970 // return
971 return $field;
972
973 }
974
975
976 /*
977 * _acf_get_field_by_key
978 *
979 * This function will get a field via its key
980 *
981 * @type function
982 * @date 27/02/2014
983 * @since 5.0.0
984 *
985 * @param $key (string)
986 * @return $field (array)
987 */
988
989 function _acf_get_field_by_key( $key = '', $db_only = false ) {
990
991 // try JSON before DB to save query time
992 if( !$db_only && acf_is_local_field( $key ) ) {
993
994 return acf_get_local_field( $key );
995
996 }
997
998
999 // vars
1000 $post_id = acf_get_field_id( $key );
1001
1002
1003 // bail early if no post_id
1004 if( !$post_id ) return false;
1005
1006
1007 // return
1008 return _acf_get_field_by_id( $post_id, $db_only );
1009
1010 }
1011
1012
1013 /*
1014 * _acf_get_field_by_name
1015 *
1016 * This function will get a field via its name
1017 *
1018 * @type function
1019 * @date 27/02/2014
1020 * @since 5.0.0
1021 *
1022 * @param $key (string)
1023 * @return $field (array)
1024 */
1025
1026 function _acf_get_field_by_name( $name = '', $db_only = false ) {
1027
1028 // try JSON before DB to save query time
1029 if( !$db_only && acf_is_local_field( $name ) ) {
1030
1031 return acf_get_local_field( $name );
1032
1033 }
1034
1035
1036 // vars
1037 $args = array(
1038 'posts_per_page' => 1,
1039 'post_type' => 'acf-field',
1040 'orderby' => 'menu_order title',
1041 'order' => 'ASC',
1042 'suppress_filters' => false,
1043 'acf_field_name' => $name
1044 );
1045
1046
1047 // load posts
1048 $posts = get_posts( $args );
1049
1050
1051 // bail early if no posts
1052 if( empty($posts) ) return false;
1053
1054
1055 // return
1056 return _acf_get_field_by_id( $posts[0]->ID, $db_only );
1057
1058 }
1059
1060
1061 /*
1062 * acf_maybe_get_field
1063 *
1064 * This function will return a field for the given selector.
1065 * It will also review the field_reference to ensure the correct field is returned which makes it useful for the template API
1066 *
1067 * @type function
1068 * @date 4/08/2015
1069 * @since 5.2.3
1070 *
1071 * @param $selector (mixed) identifyer of field. Can be an ID, key, name or post object
1072 * @param $post_id (mixed) the post_id of which the value is saved against
1073 * @param $strict (boolean) if true, return a field only when a field key is found.
1074 * @return $field (array)
1075 */
1076
1077 function acf_maybe_get_field( $selector, $post_id = false, $strict = true ) {
1078
1079 // init
1080 acf_init();
1081
1082
1083 // bail early if is field key
1084 if( acf_is_field_key($selector) ) {
1085
1086 return acf_get_field( $selector );
1087
1088 }
1089
1090
1091 // save selector as field_name (could be sub field name 'images_0_image')
1092 $field_name = $selector;
1093
1094
1095 // get valid post_id
1096 $post_id = acf_get_valid_post_id( $post_id );
1097
1098
1099 // get reference
1100 $field_key = acf_get_field_reference( $selector, $post_id );
1101
1102
1103 // update selector
1104 if( $field_key ) {
1105
1106 $selector = $field_key;
1107
1108 // bail early if no reference
1109 } elseif( $strict ) {
1110
1111 return false;
1112
1113 }
1114
1115
1116 // get field
1117 $field = acf_get_field( $selector );
1118
1119
1120 // update name
1121 if( $field ) $field['name'] = $field_name;
1122
1123
1124 // return
1125 return $field;
1126
1127 }
1128
1129
1130 /*
1131 * acf_get_field_id
1132 *
1133 * This function will lookup a field's ID from the DB
1134 * Useful for local fields to find DB sibling
1135 *
1136 * @type function
1137 * @date 25/06/2015
1138 * @since 5.2.3
1139 *
1140 * @param $key (string)
1141 * @return $post_id (int)
1142 */
1143
1144 function acf_get_field_id( $key = '' ) {
1145
1146 // vars
1147 $args = array(
1148 'posts_per_page' => 1,
1149 'post_type' => 'acf-field',
1150 'orderby' => 'menu_order title',
1151 'order' => 'ASC',
1152 'suppress_filters' => false,
1153 'acf_field_key' => $key
1154 );
1155
1156
1157 // load posts
1158 $posts = get_posts( $args );
1159
1160
1161 // validate
1162 if( empty($posts) ) return 0;
1163
1164
1165 // return
1166 return $posts[0]->ID;
1167
1168 }
1169
1170
1171 /*
1172 * acf_update_field
1173 *
1174 * This function will update a field into the DB.
1175 * The returned field will always contain an ID
1176 *
1177 * @type function
1178 * @date 1/10/13
1179 * @since 5.0.0
1180 *
1181 * @param $field (array)
1182 * @return $field (array)
1183 */
1184
1185 function acf_update_field( $field = false, $specific = false ) {
1186
1187 // $field must be an array
1188 if( !is_array($field) ) return false;
1189
1190
1191 // validate
1192 $field = acf_get_valid_field( $field );
1193
1194
1195 // may have been posted. Remove slashes
1196 $field = wp_unslash( $field );
1197
1198
1199 // parse types (converts string '0' to int 0)
1200 $field = acf_parse_types( $field );
1201
1202
1203 // clean up conditional logic keys
1204 if( !empty($field['conditional_logic']) ) {
1205
1206 // extract groups
1207 $groups = acf_extract_var( $field, 'conditional_logic' );
1208
1209
1210 // clean array
1211 $groups = array_filter($groups);
1212 $groups = array_values($groups);
1213
1214
1215 // clean rules
1216 foreach( array_keys($groups) as $i ) {
1217
1218 $groups[ $i ] = array_filter($groups[ $i ]);
1219 $groups[ $i ] = array_values($groups[ $i ]);
1220
1221 }
1222
1223
1224 // reset conditional logic
1225 $field['conditional_logic'] = $groups;
1226
1227 }
1228
1229
1230 // parent may be a field key
1231 // - lookup parent ID
1232 if( acf_is_field_key($field['parent']) ) {
1233
1234 $field['parent'] = acf_get_field_id( $field['parent'] );
1235
1236 }
1237
1238
1239 // filter for 3rd party customization
1240 $field = apply_filters( "acf/update_field", $field);
1241 $field = apply_filters( "acf/update_field/type={$field['type']}", $field );
1242 $field = apply_filters( "acf/update_field/name={$field['name']}", $field );
1243 $field = apply_filters( "acf/update_field/key={$field['key']}", $field );
1244
1245
1246 // store origional field for return
1247 $data = $field;
1248
1249
1250 // extract some args
1251 $extract = acf_extract_vars($data, array(
1252 'ID',
1253 'key',
1254 'label',
1255 'name',
1256 'prefix',
1257 'value',
1258 'menu_order',
1259 'id',
1260 'class',
1261 'parent',
1262 '_name',
1263 '_prepare',
1264 '_valid',
1265 ));
1266
1267
1268 // serialize for DB
1269 $data = maybe_serialize( $data );
1270
1271
1272 // save
1273 $save = array(
1274 'ID' => $extract['ID'],
1275 'post_status' => 'publish',
1276 'post_type' => 'acf-field',
1277 'post_title' => $extract['label'],
1278 'post_name' => $extract['key'],
1279 'post_excerpt' => $extract['name'],
1280 'post_content' => $data,
1281 'post_parent' => $extract['parent'],
1282 'menu_order' => $extract['menu_order'],
1283 );
1284
1285
1286 // specific
1287 if( acf_is_array($specific) ) {
1288
1289 // append ID
1290 $specific[] = 'ID';
1291
1292
1293 // get sub array
1294 $save = acf_get_sub_array( $save, $specific );
1295
1296 }
1297
1298
1299 // allow fields to contain the same name
1300 add_filter( 'wp_unique_post_slug', 'acf_update_field_wp_unique_post_slug', 999, 6 );
1301
1302
1303 // slash data
1304 // - WP expects all data to be slashed and will unslash it (fixes '\' character issues)
1305 $save = wp_slash( $save );
1306
1307
1308 // update the field and update the ID
1309 if( $field['ID'] ) {
1310
1311 wp_update_post( $save );
1312
1313 } else {
1314
1315 $field['ID'] = wp_insert_post( $save );
1316
1317 }
1318
1319
1320 // clear cache
1321 acf_delete_cache("get_field/key={$field['key']}");
1322
1323
1324 // return
1325 return $field;
1326
1327 }
1328
1329 function acf_update_field_wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
1330
1331 if( $post_type == 'acf-field' ) {
1332
1333 $slug = $original_slug;
1334
1335 }
1336
1337 // return
1338 return $slug;
1339
1340 }
1341
1342
1343 /*
1344 * acf_duplicate_fields
1345 *
1346 * This function will duplicate an array of fields and update conditional logic references
1347 *
1348 * @type function
1349 * @date 16/06/2014
1350 * @since 5.0.0
1351 *
1352 * @param $fields (array)
1353 * @param $new_parent (int)
1354 * @return n/a
1355 */
1356
1357 function acf_duplicate_fields( $fields, $new_parent = 0 ) {
1358
1359 // bail early if no fields
1360 if( empty($fields) ) return;
1361
1362
1363 // create new field keys (for conditional logic fixes)
1364 foreach( $fields as $field ) {
1365
1366 // ensure a delay for unique ID
1367 usleep(1);
1368
1369 acf_update_setting( 'duplicate_key_' . $field['key'] , uniqid('field_') );
1370
1371 }
1372
1373
1374 // duplicate fields
1375 foreach( $fields as $field ) {
1376
1377 // duplicate
1378 acf_duplicate_field( $field['ID'], $new_parent );
1379
1380 }
1381
1382 }
1383
1384
1385 /*
1386 * acf_duplicate_field
1387 *
1388 * This function will duplicate a field and attach it to the given field group ID
1389 *
1390 * @type function
1391 * @date 17/10/13
1392 * @since 5.0.0
1393 *
1394 * @param $selector (int)
1395 * @param $new_parent (int)
1396 * @return $field (array) the new field
1397 */
1398
1399 function acf_duplicate_field( $selector = 0, $new_parent = 0 ){
1400
1401 // disable filters to ensure ACF loads raw data from DB
1402 acf_disable_filters();
1403
1404
1405 // load the origional field
1406 $field = acf_get_field( $selector );
1407
1408
1409 // bail early if field did not load correctly
1410 if( empty($field) ) {
1411
1412 return false;
1413
1414 }
1415
1416
1417 // update ID
1418 $field['ID'] = false;
1419
1420
1421 // try duplicate keys
1422 $field['key'] = acf_get_setting( 'duplicate_key_' . $field['key'] );
1423
1424
1425 // default key
1426 if( empty($field['key']) ) {
1427
1428 $field['key'] = uniqid('field_');
1429
1430 }
1431
1432
1433 // update parent
1434 if( $new_parent ) {
1435
1436 $field['parent'] = $new_parent;
1437
1438 }
1439
1440
1441 // update conditional logic references (because field keys have changed)
1442 if( !empty($field['conditional_logic']) ) {
1443
1444 // extract groups
1445 $groups = acf_extract_var( $field, 'conditional_logic' );
1446
1447
1448 // loop over groups
1449 foreach( array_keys($groups) as $g ) {
1450
1451 // extract group
1452 $group = acf_extract_var( $groups, $g );
1453
1454
1455 // bail early if empty
1456 if( empty($group) ) {
1457
1458 continue;
1459
1460 }
1461
1462
1463 // loop over rules
1464 foreach( array_keys($group) as $r ) {
1465
1466 // extract rule
1467 $rule = acf_extract_var( $group, $r );
1468
1469
1470 // vars
1471 $new_key = acf_get_setting( 'duplicate_key_' . $rule['field'] );
1472
1473
1474 // update rule with new key
1475 if( $new_key ) {
1476
1477 $rule['field'] = $new_key;
1478
1479 }
1480
1481
1482 // append to group
1483 $group[ $r ] = $rule;
1484
1485 }
1486
1487
1488 // append to groups
1489 $groups[ $g ] = $group;
1490
1491 }
1492
1493
1494 // update conditional logic
1495 $field['conditional_logic'] = $groups;
1496
1497
1498 }
1499
1500
1501 // filter for 3rd party customization
1502 $field = apply_filters( "acf/duplicate_field", $field);
1503 $field = apply_filters( "acf/duplicate_field/type={$field['type']}", $field );
1504
1505
1506 // save
1507 return acf_update_field( $field );
1508
1509 }
1510
1511
1512 /*
1513 * acf_delete_field
1514 *
1515 * This function will delete a field from the databse
1516 *
1517 * @type function
1518 * @date 2/10/13
1519 * @since 5.0.0
1520 *
1521 * @param $id (int)
1522 * @return (boolean)
1523 */
1524
1525 function acf_delete_field( $selector = 0 ) {
1526
1527 // disable filters to ensure ACF loads raw data from DB
1528 acf_disable_filters();
1529
1530
1531 // load the origional field gorup
1532 $field = acf_get_field( $selector );
1533
1534
1535 // bail early if field did not load correctly
1536 if( empty($field) ) return false;
1537
1538
1539 // delete field
1540 wp_delete_post( $field['ID'], true );
1541
1542
1543 // action for 3rd party customisation
1544 do_action( "acf/delete_field", $field);
1545 do_action( "acf/delete_field/type={$field['type']}", $field );
1546
1547
1548 // clear cache
1549 acf_delete_cache("get_field/key={$field['key']}");
1550
1551
1552 // return
1553 return true;
1554
1555 }
1556
1557
1558 /*
1559 * acf_trash_field
1560 *
1561 * This function will trash a field from the databse
1562 *
1563 * @type function
1564 * @date 2/10/13
1565 * @since 5.0.0
1566 *
1567 * @param $id (int)
1568 * @return (boolean)
1569 */
1570
1571 function acf_trash_field( $selector = 0 ) {
1572
1573 // disable filters to ensure ACF loads raw data from DB
1574 acf_disable_filters();
1575
1576
1577 // load the origional field gorup
1578 $field = acf_get_field( $selector );
1579
1580
1581 // bail early if field did not load correctly
1582 if( empty($field) ) return false;
1583
1584
1585 // delete field
1586 wp_trash_post( $field['ID'] );
1587
1588
1589 // action for 3rd party customisation
1590 do_action( 'acf/trash_field', $field );
1591
1592
1593 // return
1594 return true;
1595
1596 }
1597
1598
1599 /*
1600 * acf_untrash_field
1601 *
1602 * This function will restore a field from the trash
1603 *
1604 * @type function
1605 * @date 2/10/13
1606 * @since 5.0.0
1607 *
1608 * @param $id (int)
1609 * @return (boolean)
1610 */
1611
1612 function acf_untrash_field( $selector = 0 ) {
1613
1614 // disable filters to ensure ACF loads raw data from DB
1615 acf_disable_filters();
1616
1617
1618 // load the origional field gorup
1619 $field = acf_get_field( $selector );
1620
1621
1622 // bail early if field did not load correctly
1623 if( empty($field) ) return false;
1624
1625
1626 // delete field
1627 wp_untrash_post( $field['ID'] );
1628
1629
1630 // action for 3rd party customisation
1631 do_action( 'acf/untrash_field', $field );
1632
1633
1634 // return
1635 return true;
1636 }
1637
1638
1639 /*
1640 * acf_prepare_fields_for_export
1641 *
1642 * description
1643 *
1644 * @type function
1645 * @date 11/03/2014
1646 * @since 5.0.0
1647 *
1648 * @param $post_id (int)
1649 * @return $post_id (int)
1650 */
1651
1652 function acf_prepare_fields_for_export( $fields = false ) {
1653
1654 // validate
1655 if( empty($fields) ) return $fields;
1656
1657
1658 // format
1659 foreach( array_keys($fields) as $i ) {
1660
1661 // prepare
1662 $fields[ $i ] = acf_prepare_field_for_export( $fields[ $i ] );
1663
1664 }
1665
1666
1667 // return
1668 return $fields;
1669
1670 }
1671
1672
1673 /*
1674 * acf_prepare_field_for_export
1675 *
1676 * description
1677 *
1678 * @type function
1679 * @date 11/03/2014
1680 * @since 5.0.0
1681 *
1682 * @param $post_id (int)
1683 * @return $post_id (int)
1684 */
1685
1686 function acf_prepare_field_for_export( $field ) {
1687
1688 // extract some args
1689 $extract = acf_extract_vars($field, array(
1690 'ID',
1691 'prefix',
1692 'value',
1693 'menu_order',
1694 'id',
1695 'class',
1696 'parent',
1697 '_name',
1698 '_prepare',
1699 '_valid',
1700 ));
1701
1702
1703 // filter for 3rd party customization
1704 $field = apply_filters( "acf/prepare_field_for_export", $field );
1705 $field = apply_filters( "acf/prepare_field_for_export/type={$field['type']}", $field );
1706
1707
1708 // return
1709 return $field;
1710 }
1711
1712
1713 /*
1714 * acf_prepare_fields_for_import
1715 *
1716 * description
1717 *
1718 * @type function
1719 * @date 11/03/2014
1720 * @since 5.0.0
1721 *
1722 * @param $post_id (int)
1723 * @return $post_id (int)
1724 */
1725
1726 function acf_prepare_fields_for_import( $fields = false ) {
1727
1728 // validate
1729 if( empty($fields) ) return array();
1730
1731
1732 // re-index array
1733 $fields = array_values($fields);
1734
1735
1736 // vars
1737 $i = 0;
1738
1739
1740 // format
1741 while( $i < count($fields) ) {
1742
1743 // prepare field
1744 $field = acf_prepare_field_for_import( $fields[ $i ] );
1745
1746
1747 // allow multiple fields to be returned ($field + $sub_fields)
1748 if( !isset($field['key']) && isset($field[0]) ) {
1749
1750 // merge in $field (1 or more fields)
1751 array_splice($fields, $i, 1, $field);
1752
1753 }
1754
1755
1756 // $i
1757 $i++;
1758
1759 }
1760
1761
1762 // filter for 3rd party customization
1763 $fields = apply_filters('acf/prepare_fields_for_import', $fields);
1764
1765
1766 // return
1767 return $fields;
1768
1769 }
1770
1771
1772 /*
1773 * acf_prepare_field_for_import
1774 *
1775 * description
1776 *
1777 * @type function
1778 * @date 11/03/2014
1779 * @since 5.0.0
1780 *
1781 * @param $post_id (int)
1782 * @return $post_id (int)
1783 */
1784
1785 function acf_prepare_field_for_import( $field ) {
1786
1787 // extract some args
1788 $extract = acf_extract_vars($field, array(
1789 'value',
1790 'id',
1791 'class',
1792 '_name',
1793 '_prepare',
1794 '_valid',
1795 ));
1796
1797
1798 // filter for 3rd party customization
1799 $field = apply_filters( "acf/prepare_field_for_import", $field );
1800 $field = apply_filters( "acf/prepare_field_for_import/type={$field['type']}", $field );
1801
1802
1803 // return
1804 return $field;
1805 }
1806
1807
1808 /*
1809 * acf_get_sub_field
1810 *
1811 * This function will return a field for the given selector, and $field (parent).
1812 *
1813 * @type function
1814 * @date 30/09/13
1815 * @since 5.0.0
1816 *
1817 * @param $selector (string)
1818 * @param $field (mixed)
1819 * @return $field (array)
1820 */
1821
1822 function acf_get_sub_field( $selector, $field ) {
1823
1824 // vars
1825 $sub_field = false;
1826
1827
1828 // check sub_fields
1829 if( isset($field['sub_fields']) ) {
1830
1831 // loop
1832 foreach( $field['sub_fields'] as $_sub_field ) {
1833
1834 // check name and key
1835 if( acf_is_field($_sub_field, $selector) ) {
1836
1837 $sub_field = $_sub_field;
1838 break;
1839
1840 }
1841
1842 }
1843
1844 }
1845
1846
1847 // filter for 3rd party customization
1848 $sub_field = apply_filters( "acf/get_sub_field", $sub_field, $selector, $field );
1849 $sub_field = apply_filters( "acf/get_sub_field/type={$field['type']}", $sub_field, $selector, $field );
1850
1851
1852 // return
1853 return $sub_field;
1854
1855 }
1856
1857
1858 /*
1859 * acf_is_field
1860 *
1861 * This function will compare a $selector against a $field array
1862 *
1863 * @type function
1864 * @date 1/7/17
1865 * @since 5.6.0
1866 *
1867 * @param $post_id (int)
1868 * @return $post_id (int)
1869 */
1870
1871 function acf_is_field( $field, $selector = '' ) {
1872
1873 // vars
1874 $keys = array(
1875 'ID',
1876 'name',
1877 'key',
1878 '_name',
1879 '__name',
1880 );
1881
1882
1883 // loop
1884 foreach( $keys as $k ) {
1885
1886 if( isset($field[ $k ]) && $field[ $k ] === $selector ) return true;
1887
1888 }
1889
1890
1891 // return
1892 return false;
1893
1894 }
1895
1896
1897 /*
1898 * acf_get_field_ancestors
1899 *
1900 * This function will return an array of all ancestor fields
1901 *
1902 * @type function
1903 * @date 22/06/2016
1904 * @since 5.3.8
1905 *
1906 * @param $field (array)
1907 * @return (array)
1908 */
1909
1910 function acf_get_field_ancestors( $field ) {
1911
1912 // get field
1913 $ancestors = array();
1914
1915
1916 // loop
1917 while( $field && acf_is_field_key($field['parent']) ) {
1918
1919 $ancestors[] = $field['parent'];
1920 $field = acf_get_field($field['parent']);
1921
1922 }
1923
1924
1925 // return
1926 return $ancestors;
1927
1928 }
1929
1930
1931 /*
1932 * acf_maybe_get_sub_field
1933 *
1934 * This function will attempt to find a sub field
1935 *
1936 * @type function
1937 * @date 3/10/2016
1938 * @since 5.4.0
1939 *
1940 * @param $post_id (int)
1941 * @return $post_id (int)
1942 */
1943
1944 function acf_maybe_get_sub_field( $selectors, $post_id = false, $strict = true ) {
1945
1946 // bail ealry if not enough selectors
1947 if( !is_array($selectors) || count($selectors) < 3 ) return false;
1948
1949
1950 // vars
1951 $offset = acf_get_setting('row_index_offset');
1952 $selector = acf_extract_var( $selectors, 0 );
1953 $selectors = array_values( $selectors ); // reset keys
1954
1955
1956 // attempt get field
1957 $field = acf_maybe_get_field( $selector, $post_id, $strict );
1958
1959
1960 // bail early if no field
1961 if( !$field ) return false;
1962
1963
1964 // loop
1965 for( $j = 0; $j < count($selectors); $j+=2 ) {
1966
1967 // vars
1968 $sub_i = $selectors[ $j ];
1969 $sub_s = $selectors[ $j+1 ];
1970 $field_name = $field['name'];
1971
1972
1973 // find sub field
1974 $field = acf_get_sub_field( $sub_s, $field );
1975
1976
1977 // bail early if no sub field
1978 if( !$field ) return false;
1979
1980
1981 // add to name
1982 $field['name'] = $field_name . '_' . ($sub_i-$offset) . '_' . $field['name'];
1983
1984 }
1985
1986
1987 // return
1988 return $field;
1989
1990
1991 }
1992
1993
1994 /*
1995 * acf_prefix_fields
1996 *
1997 * This funtion will safely change the prefix for an array of fields
1998 * Needed to allow clone field to continue working on nave menu item and widget forms
1999 *
2000 * @type function
2001 * @date 5/9/17
2002 * @since 5.6.0
2003 *
2004 * @param $post_id (int)
2005 * @return $post_id (int)
2006 */
2007
2008 function acf_prefix_fields( &$fields, $prefix = 'acf' ) {
2009
2010 // loop
2011 foreach( $fields as &$field ) {
2012
2013 // replace 'acf' with $prefix
2014 $field['prefix'] = substr_replace($field['prefix'], $prefix, 0, 3);
2015
2016 }
2017
2018
2019 // return
2020 return $fields;
2021
2022 }
2023
2024 ?>