PluginProbe
Advanced Custom Fields (ACF®) / 5.8.6
Advanced Custom Fields (ACF®) v5.8.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-helpers.php
api-helpers.php
4,848 lines 82.4 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_array
5 *
6 * This function will return true for a non empty array
7 *
8 * @type function
9 * @date 6/07/2016
10 * @since 5.4.0
11 *
12 * @param $array (array)
13 * @return (boolean)
14 */
15
16 function acf_is_array( $array ) {
17
18 return ( is_array($array) && !empty($array) );
19
20 }
21
22 /**
23 * acf_has_setting
24 *
25 * alias of acf()->has_setting()
26 *
27 * @date 2/2/18
28 * @since 5.6.5
29 *
30 * @param n/a
31 * @return n/a
32 */
33
34 function acf_has_setting( $name = '' ) {
35 return acf()->has_setting( $name );
36 }
37
38
39 /**
40 * acf_raw_setting
41 *
42 * alias of acf()->get_setting()
43 *
44 * @date 2/2/18
45 * @since 5.6.5
46 *
47 * @param n/a
48 * @return n/a
49 */
50
51 function acf_raw_setting( $name = '' ) {
52 return acf()->get_setting( $name );
53 }
54
55
56 /*
57 * acf_update_setting
58 *
59 * alias of acf()->update_setting()
60 *
61 * @type function
62 * @date 28/09/13
63 * @since 5.0.0
64 *
65 * @param $name (string)
66 * @param $value (mixed)
67 * @return n/a
68 */
69
70 function acf_update_setting( $name, $value ) {
71
72 // validate name
73 $name = acf_validate_setting( $name );
74
75 // update
76 return acf()->update_setting( $name, $value );
77 }
78
79
80 /**
81 * acf_validate_setting
82 *
83 * Returns the changed setting name if available.
84 *
85 * @date 2/2/18
86 * @since 5.6.5
87 *
88 * @param n/a
89 * @return n/a
90 */
91
92 function acf_validate_setting( $name = '' ) {
93 return apply_filters( "acf/validate_setting", $name );
94 }
95
96
97 /*
98 * acf_get_setting
99 *
100 * alias of acf()->get_setting()
101 *
102 * @type function
103 * @date 28/09/13
104 * @since 5.0.0
105 *
106 * @param n/a
107 * @return n/a
108 */
109
110 function acf_get_setting( $name, $value = null ) {
111
112 // validate name
113 $name = acf_validate_setting( $name );
114
115 // check settings
116 if( acf_has_setting($name) ) {
117 $value = acf_raw_setting( $name );
118 }
119
120 // filter
121 $value = apply_filters( "acf/settings/{$name}", $value );
122
123 // return
124 return $value;
125 }
126
127
128 /*
129 * acf_append_setting
130 *
131 * This function will add a value into the settings array found in the acf object
132 *
133 * @type function
134 * @date 28/09/13
135 * @since 5.0.0
136 *
137 * @param $name (string)
138 * @param $value (mixed)
139 * @return n/a
140 */
141
142 function acf_append_setting( $name, $value ) {
143
144 // vars
145 $setting = acf_raw_setting( $name );
146
147 // bail ealry if not array
148 if( !is_array($setting) ) {
149 $setting = array();
150 }
151
152 // append
153 $setting[] = $value;
154
155 // update
156 return acf_update_setting( $name, $setting );
157 }
158
159
160 /**
161 * acf_get_data
162 *
163 * Returns data.
164 *
165 * @date 28/09/13
166 * @since 5.0.0
167 *
168 * @param string $name
169 * @return mixed
170 */
171
172 function acf_get_data( $name ) {
173 return acf()->get_data( $name );
174 }
175
176
177 /**
178 * acf_set_data
179 *
180 * Sets data.
181 *
182 * @date 28/09/13
183 * @since 5.0.0
184 *
185 * @param string $name
186 * @param mixed $value
187 * @return n/a
188 */
189
190 function acf_set_data( $name, $value ) {
191 return acf()->set_data( $name, $value );
192 }
193
194 /*
195 * acf_init
196 *
197 * alias of acf()->init()
198 *
199 * @type function
200 * @date 28/09/13
201 * @since 5.0.0
202 *
203 * @param n/a
204 * @return n/a
205 */
206
207 function acf_init() {
208
209 acf()->init();
210
211 }
212
213
214 /*
215 * acf_has_done
216 *
217 * This function will return true if this action has already been done
218 *
219 * @type function
220 * @date 16/12/2015
221 * @since 5.3.2
222 *
223 * @param $name (string)
224 * @return (boolean)
225 */
226
227 function acf_has_done( $name ) {
228
229 // return true if already done
230 if( acf_raw_setting("has_done_{$name}") ) {
231 return true;
232 }
233
234 // update setting and return
235 acf_update_setting("has_done_{$name}", true);
236 return false;
237 }
238
239
240
241
242 /*
243 * acf_get_external_path
244 *
245 * This function will return the path to a file within an external folder
246 *
247 * @type function
248 * @date 22/2/17
249 * @since 5.5.8
250 *
251 * @param $file (string)
252 * @param $path (string)
253 * @return (string)
254 */
255
256 function acf_get_external_path( $file, $path = '' ) {
257
258 return plugin_dir_path( $file ) . $path;
259
260 }
261
262
263 /*
264 * acf_get_external_dir
265 *
266 * This function will return the url to a file within an external folder
267 *
268 * @type function
269 * @date 22/2/17
270 * @since 5.5.8
271 *
272 * @param $file (string)
273 * @param $path (string)
274 * @return (string)
275 */
276
277 function acf_get_external_dir( $file, $path = '' ) {
278
279 return acf_plugin_dir_url( $file ) . $path;
280
281 }
282
283
284 /**
285 * acf_plugin_dir_url
286 *
287 * This function will calculate the url to a plugin folder.
288 * Different to the WP plugin_dir_url(), this function can calculate for urls outside of the plugins folder (theme include).
289 *
290 * @date 13/12/17
291 * @since 5.6.8
292 *
293 * @param type $var Description. Default.
294 * @return type Description.
295 */
296
297 function acf_plugin_dir_url( $file ) {
298
299 // vars
300 $path = plugin_dir_path( $file );
301 $path = wp_normalize_path( $path );
302
303
304 // check plugins
305 $check_path = wp_normalize_path( realpath(WP_PLUGIN_DIR) );
306 if( strpos($path, $check_path) === 0 ) {
307 return str_replace( $check_path, plugins_url(), $path );
308 }
309
310 // check wp-content
311 $check_path = wp_normalize_path( realpath(WP_CONTENT_DIR) );
312 if( strpos($path, $check_path) === 0 ) {
313 return str_replace( $check_path, content_url(), $path );
314 }
315
316 // check root
317 $check_path = wp_normalize_path( realpath(ABSPATH) );
318 if( strpos($path, $check_path) === 0 ) {
319 return str_replace( $check_path, site_url('/'), $path );
320 }
321
322
323 // return
324 return plugin_dir_url( $file );
325
326 }
327
328
329 /*
330 * acf_parse_args
331 *
332 * This function will merge together 2 arrays and also convert any numeric values to ints
333 *
334 * @type function
335 * @date 18/10/13
336 * @since 5.0.0
337 *
338 * @param $args (array)
339 * @param $defaults (array)
340 * @return $args (array)
341 */
342
343 function acf_parse_args( $args, $defaults = array() ) {
344
345 // parse args
346 $args = wp_parse_args( $args, $defaults );
347
348
349 // parse types
350 $args = acf_parse_types( $args );
351
352
353 // return
354 return $args;
355
356 }
357
358
359 /*
360 * acf_parse_types
361 *
362 * This function will convert any numeric values to int and trim strings
363 *
364 * @type function
365 * @date 18/10/13
366 * @since 5.0.0
367 *
368 * @param $var (mixed)
369 * @return $var (mixed)
370 */
371
372 function acf_parse_types( $array ) {
373 return array_map( 'acf_parse_type', $array );
374 }
375
376
377 /*
378 * acf_parse_type
379 *
380 * description
381 *
382 * @type function
383 * @date 11/11/2014
384 * @since 5.0.9
385 *
386 * @param $post_id (int)
387 * @return $post_id (int)
388 */
389
390 function acf_parse_type( $v ) {
391
392 // Check if is string.
393 if( is_string($v) ) {
394
395 // Trim ("Word " = "Word").
396 $v = trim( $v );
397
398 // Convert int strings to int ("123" = 123).
399 if( is_numeric($v) && strval(intval($v)) === $v ) {
400 $v = intval( $v );
401 }
402 }
403
404 // return.
405 return $v;
406 }
407
408
409 /*
410 * acf_get_view
411 *
412 * This function will load in a file from the 'admin/views' folder and allow variables to be passed through
413 *
414 * @type function
415 * @date 28/09/13
416 * @since 5.0.0
417 *
418 * @param $view_name (string)
419 * @param $args (array)
420 * @return n/a
421 */
422
423 function acf_get_view( $path = '', $args = array() ) {
424
425 // allow view file name shortcut
426 if( substr($path, -4) !== '.php' ) {
427
428 $path = acf_get_path("includes/admin/views/{$path}.php");
429
430 }
431
432
433 // include
434 if( file_exists($path) ) {
435
436 extract( $args );
437 include( $path );
438
439 }
440
441 }
442
443
444 /*
445 * acf_merge_atts
446 *
447 * description
448 *
449 * @type function
450 * @date 2/11/2014
451 * @since 5.0.9
452 *
453 * @param $post_id (int)
454 * @return $post_id (int)
455 */
456
457 function acf_merge_atts( $atts, $extra = array() ) {
458
459 // bail ealry if no $extra
460 if( empty($extra) ) return $atts;
461
462
463 // trim
464 $extra = array_map('trim', $extra);
465 $extra = array_filter($extra);
466
467
468 // merge in new atts
469 foreach( $extra as $k => $v ) {
470
471 // append
472 if( $k == 'class' || $k == 'style' ) {
473
474 $atts[ $k ] .= ' ' . $v;
475
476 // merge
477 } else {
478
479 $atts[ $k ] = $v;
480
481 }
482
483 }
484
485
486 // return
487 return $atts;
488
489 }
490
491
492 /*
493 * acf_nonce_input
494 *
495 * This function will create a basic nonce input
496 *
497 * @type function
498 * @date 24/5/17
499 * @since 5.6.0
500 *
501 * @param $post_id (int)
502 * @return $post_id (int)
503 */
504
505 function acf_nonce_input( $nonce = '' ) {
506
507 echo '<input type="hidden" name="_acf_nonce" value="' . wp_create_nonce( $nonce ) . '" />';
508
509 }
510
511
512 /*
513 * acf_extract_var
514 *
515 * This function will remove the var from the array, and return the var
516 *
517 * @type function
518 * @date 2/10/13
519 * @since 5.0.0
520 *
521 * @param $array (array)
522 * @param $key (string)
523 * @return (mixed)
524 */
525
526 function acf_extract_var( &$array, $key, $default = null ) {
527
528 // check if exists
529 // - uses array_key_exists to extract NULL values (isset will fail)
530 if( is_array($array) && array_key_exists($key, $array) ) {
531
532 // store value
533 $v = $array[ $key ];
534
535
536 // unset
537 unset( $array[ $key ] );
538
539
540 // return
541 return $v;
542
543 }
544
545
546 // return
547 return $default;
548 }
549
550
551 /*
552 * acf_extract_vars
553 *
554 * This function will remove the vars from the array, and return the vars
555 *
556 * @type function
557 * @date 8/10/13
558 * @since 5.0.0
559 *
560 * @param $post_id (int)
561 * @return $post_id (int)
562 */
563
564 function acf_extract_vars( &$array, $keys ) {
565
566 $r = array();
567
568 foreach( $keys as $key ) {
569
570 $r[ $key ] = acf_extract_var( $array, $key );
571
572 }
573
574 return $r;
575 }
576
577
578 /*
579 * acf_get_sub_array
580 *
581 * This function will return a sub array of data
582 *
583 * @type function
584 * @date 15/03/2016
585 * @since 5.3.2
586 *
587 * @param $post_id (int)
588 * @return $post_id (int)
589 */
590
591 function acf_get_sub_array( $array, $keys ) {
592
593 $r = array();
594
595 foreach( $keys as $key ) {
596
597 $r[ $key ] = $array[ $key ];
598
599 }
600
601 return $r;
602
603 }
604
605
606 /**
607 * acf_get_post_types
608 *
609 * Returns an array of post type names.
610 *
611 * @date 7/10/13
612 * @since 5.0.0
613 *
614 * @param array $args Optional. An array of key => value arguments to match against the post type objects. Default empty array.
615 * @return array A list of post type names.
616 */
617
618 function acf_get_post_types( $args = array() ) {
619
620 // vars
621 $post_types = array();
622
623 // extract special arg
624 $exclude = acf_extract_var( $args, 'exclude', array() );
625 $exclude[] = 'acf-field';
626 $exclude[] = 'acf-field-group';
627
628 // get post type objects
629 $objects = get_post_types( $args, 'objects' );
630
631 // loop
632 foreach( $objects as $i => $object ) {
633
634 // bail early if is exclude
635 if( in_array($i, $exclude) ) continue;
636
637 // bail early if is builtin (WP) private post type
638 // - nav_menu_item, revision, customize_changeset, etc
639 if( $object->_builtin && !$object->public ) continue;
640
641 // append
642 $post_types[] = $i;
643 }
644
645 // filter
646 $post_types = apply_filters('acf/get_post_types', $post_types, $args);
647
648 // return
649 return $post_types;
650 }
651
652 function acf_get_pretty_post_types( $post_types = array() ) {
653
654 // get post types
655 if( empty($post_types) ) {
656
657 // get all custom post types
658 $post_types = acf_get_post_types();
659
660 }
661
662
663 // get labels
664 $ref = array();
665 $r = array();
666
667 foreach( $post_types as $post_type ) {
668
669 // vars
670 $label = acf_get_post_type_label($post_type);
671
672
673 // append to r
674 $r[ $post_type ] = $label;
675
676
677 // increase counter
678 if( !isset($ref[ $label ]) ) {
679
680 $ref[ $label ] = 0;
681
682 }
683
684 $ref[ $label ]++;
685 }
686
687
688 // get slugs
689 foreach( array_keys($r) as $i ) {
690
691 // vars
692 $post_type = $r[ $i ];
693
694 if( $ref[ $post_type ] > 1 ) {
695
696 $r[ $i ] .= ' (' . $i . ')';
697
698 }
699
700 }
701
702
703 // return
704 return $r;
705
706 }
707
708
709
710 /*
711 * acf_get_post_type_label
712 *
713 * This function will return a pretty label for a specific post_type
714 *
715 * @type function
716 * @date 5/07/2016
717 * @since 5.4.0
718 *
719 * @param $post_type (string)
720 * @return (string)
721 */
722
723 function acf_get_post_type_label( $post_type ) {
724
725 // vars
726 $label = $post_type;
727
728
729 // check that object exists
730 // - case exists when importing field group from another install and post type does not exist
731 if( post_type_exists($post_type) ) {
732
733 $obj = get_post_type_object($post_type);
734 $label = $obj->labels->singular_name;
735
736 }
737
738
739 // return
740 return $label;
741
742 }
743
744
745 /*
746 * acf_verify_nonce
747 *
748 * This function will look at the $_POST['_acf_nonce'] value and return true or false
749 *
750 * @type function
751 * @date 15/10/13
752 * @since 5.0.0
753 *
754 * @param $nonce (string)
755 * @return (boolean)
756 */
757
758 function acf_verify_nonce( $value) {
759
760 // vars
761 $nonce = acf_maybe_get_POST('_acf_nonce');
762
763
764 // bail early nonce does not match (post|user|comment|term)
765 if( !$nonce || !wp_verify_nonce($nonce, $value) ) return false;
766
767
768 // reset nonce (only allow 1 save)
769 $_POST['_acf_nonce'] = false;
770
771
772 // return
773 return true;
774
775 }
776
777
778 /*
779 * acf_verify_ajax
780 *
781 * This function will return true if the current AJAX request is valid
782 * It's action will also allow WPML to set the lang and avoid AJAX get_posts issues
783 *
784 * @type function
785 * @date 7/08/2015
786 * @since 5.2.3
787 *
788 * @param n/a
789 * @return (boolean)
790 */
791
792 function acf_verify_ajax() {
793
794 // vars
795 $nonce = isset($_REQUEST['nonce']) ? $_REQUEST['nonce'] : '';
796
797 // bail early if not acf nonce
798 if( !$nonce || !wp_verify_nonce($nonce, 'acf_nonce') ) {
799 return false;
800 }
801
802 // action for 3rd party customization
803 do_action('acf/verify_ajax');
804
805 // return
806 return true;
807 }
808
809
810 /*
811 * acf_get_image_sizes
812 *
813 * This function will return an array of available image sizes
814 *
815 * @type function
816 * @date 23/10/13
817 * @since 5.0.0
818 *
819 * @param n/a
820 * @return (array)
821 */
822
823 function acf_get_image_sizes() {
824
825 // vars
826 $sizes = array(
827 'thumbnail' => __("Thumbnail",'acf'),
828 'medium' => __("Medium",'acf'),
829 'large' => __("Large",'acf')
830 );
831
832
833 // find all sizes
834 $all_sizes = get_intermediate_image_sizes();
835
836
837 // add extra registered sizes
838 if( !empty($all_sizes) ) {
839
840 foreach( $all_sizes as $size ) {
841
842 // bail early if already in array
843 if( isset($sizes[ $size ]) ) {
844
845 continue;
846
847 }
848
849
850 // append to array
851 $label = str_replace('-', ' ', $size);
852 $label = ucwords( $label );
853 $sizes[ $size ] = $label;
854
855 }
856
857 }
858
859
860 // add sizes
861 foreach( array_keys($sizes) as $s ) {
862
863 // vars
864 $data = acf_get_image_size($s);
865
866
867 // append
868 if( $data['width'] && $data['height'] ) {
869
870 $sizes[ $s ] .= ' (' . $data['width'] . ' x ' . $data['height'] . ')';
871
872 }
873
874 }
875
876
877 // add full end
878 $sizes['full'] = __("Full Size",'acf');
879
880
881 // filter for 3rd party customization
882 $sizes = apply_filters( 'acf/get_image_sizes', $sizes );
883
884
885 // return
886 return $sizes;
887
888 }
889
890 function acf_get_image_size( $s = '' ) {
891
892 // global
893 global $_wp_additional_image_sizes;
894
895
896 // rename for nicer code
897 $_sizes = $_wp_additional_image_sizes;
898
899
900 // vars
901 $data = array(
902 'width' => isset($_sizes[$s]['width']) ? $_sizes[$s]['width'] : get_option("{$s}_size_w"),
903 'height' => isset($_sizes[$s]['height']) ? $_sizes[$s]['height'] : get_option("{$s}_size_h")
904 );
905
906
907 // return
908 return $data;
909
910 }
911
912 /**
913 * acf_version_compare
914 *
915 * Similar to the version_compare() function but with extra functionality.
916 *
917 * @date 21/11/16
918 * @since 5.5.0
919 *
920 * @param string $left The left version number.
921 * @param string $compare The compare operator.
922 * @param string $right The right version number.
923 * @return bool
924 */
925 function acf_version_compare( $left = '', $compare = '>', $right = '' ) {
926
927 // Detect 'wp' placeholder.
928 if( $left === 'wp' ) {
929 global $wp_version;
930 $left = $wp_version;
931 }
932
933 // Return result.
934 return version_compare( $left, $right, $compare );
935 }
936
937
938 /*
939 * acf_get_full_version
940 *
941 * This function will remove any '-beta1' or '-RC1' strings from a version
942 *
943 * @type function
944 * @date 24/11/16
945 * @since 5.5.0
946 *
947 * @param $version (string)
948 * @return (string)
949 */
950
951 function acf_get_full_version( $version = '1' ) {
952
953 // remove '-beta1' or '-RC1'
954 if( $pos = strpos($version, '-') ) {
955
956 $version = substr($version, 0, $pos);
957
958 }
959
960
961 // return
962 return $version;
963
964 }
965
966
967 /*
968 * acf_get_terms
969 *
970 * This function is a wrapper for the get_terms() function
971 *
972 * @type function
973 * @date 28/09/2016
974 * @since 5.4.0
975 *
976 * @param $args (array)
977 * @return (array)
978 */
979
980 function acf_get_terms( $args ) {
981
982 // defaults
983 $args = wp_parse_args($args, array(
984 'taxonomy' => null,
985 'hide_empty' => false,
986 'update_term_meta_cache' => false,
987 ));
988
989 // parameters changed in version 4.5
990 if( acf_version_compare('wp', '<', '4.5') ) {
991 return get_terms( $args['taxonomy'], $args );
992 }
993
994 // return
995 return get_terms( $args );
996 }
997
998
999 /*
1000 * acf_get_taxonomy_terms
1001 *
1002 * This function will return an array of available taxonomy terms
1003 *
1004 * @type function
1005 * @date 7/10/13
1006 * @since 5.0.0
1007 *
1008 * @param $taxonomies (array)
1009 * @return (array)
1010 */
1011
1012 function acf_get_taxonomy_terms( $taxonomies = array() ) {
1013
1014 // force array
1015 $taxonomies = acf_get_array( $taxonomies );
1016
1017
1018 // get pretty taxonomy names
1019 $taxonomies = acf_get_pretty_taxonomies( $taxonomies );
1020
1021
1022 // vars
1023 $r = array();
1024
1025
1026 // populate $r
1027 foreach( array_keys($taxonomies) as $taxonomy ) {
1028
1029 // vars
1030 $label = $taxonomies[ $taxonomy ];
1031 $is_hierarchical = is_taxonomy_hierarchical( $taxonomy );
1032 $terms = acf_get_terms(array(
1033 'taxonomy' => $taxonomy,
1034 'hide_empty' => false
1035 ));
1036
1037
1038 // bail early i no terms
1039 if( empty($terms) ) continue;
1040
1041
1042 // sort into hierachial order!
1043 if( $is_hierarchical ) {
1044
1045 $terms = _get_term_children( 0, $terms, $taxonomy );
1046
1047 }
1048
1049
1050 // add placeholder
1051 $r[ $label ] = array();
1052
1053
1054 // add choices
1055 foreach( $terms as $term ) {
1056
1057 $k = "{$taxonomy}:{$term->slug}";
1058 $r[ $label ][ $k ] = acf_get_term_title( $term );
1059
1060 }
1061
1062 }
1063
1064
1065 // return
1066 return $r;
1067
1068 }
1069
1070
1071 /*
1072 * acf_decode_taxonomy_terms
1073 *
1074 * This function decodes the $taxonomy:$term strings into a nested array
1075 *
1076 * @type function
1077 * @date 27/02/2014
1078 * @since 5.0.0
1079 *
1080 * @param $terms (array)
1081 * @return (array)
1082 */
1083
1084 function acf_decode_taxonomy_terms( $strings = false ) {
1085
1086 // bail early if no terms
1087 if( empty($strings) ) return false;
1088
1089
1090 // vars
1091 $terms = array();
1092
1093
1094 // loop
1095 foreach( $strings as $string ) {
1096
1097 // vars
1098 $data = acf_decode_taxonomy_term( $string );
1099 $taxonomy = $data['taxonomy'];
1100 $term = $data['term'];
1101
1102
1103 // create empty array
1104 if( !isset($terms[ $taxonomy ]) ) {
1105
1106 $terms[ $taxonomy ] = array();
1107
1108 }
1109
1110
1111 // append
1112 $terms[ $taxonomy ][] = $term;
1113
1114 }
1115
1116
1117 // return
1118 return $terms;
1119
1120 }
1121
1122
1123 /*
1124 * acf_decode_taxonomy_term
1125 *
1126 * This function will return the taxonomy and term slug for a given value
1127 *
1128 * @type function
1129 * @date 31/03/2014
1130 * @since 5.0.0
1131 *
1132 * @param $string (string)
1133 * @return (array)
1134 */
1135
1136 function acf_decode_taxonomy_term( $value ) {
1137
1138 // vars
1139 $data = array(
1140 'taxonomy' => '',
1141 'term' => ''
1142 );
1143
1144
1145 // int
1146 if( is_numeric($value) ) {
1147
1148 $data['term'] = $value;
1149
1150 // string
1151 } elseif( is_string($value) ) {
1152
1153 $value = explode(':', $value);
1154 $data['taxonomy'] = isset($value[0]) ? $value[0] : '';
1155 $data['term'] = isset($value[1]) ? $value[1] : '';
1156
1157 // error
1158 } else {
1159
1160 return false;
1161
1162 }
1163
1164
1165 // allow for term_id (Used by ACF v4)
1166 if( is_numeric($data['term']) ) {
1167
1168 // global
1169 global $wpdb;
1170
1171
1172 // find taxonomy
1173 if( !$data['taxonomy'] ) {
1174
1175 $data['taxonomy'] = $wpdb->get_var( $wpdb->prepare("SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d LIMIT 1", $data['term']) );
1176
1177 }
1178
1179
1180 // find term (may have numeric slug '123')
1181 $term = get_term_by( 'slug', $data['term'], $data['taxonomy'] );
1182
1183
1184 // attempt get term via ID (ACF4 uses ID)
1185 if( !$term ) $term = get_term( $data['term'], $data['taxonomy'] );
1186
1187
1188 // bail early if no term
1189 if( !$term ) return false;
1190
1191
1192 // update
1193 $data['taxonomy'] = $term->taxonomy;
1194 $data['term'] = $term->slug;
1195
1196 }
1197
1198
1199 // return
1200 return $data;
1201
1202 }
1203
1204 /**
1205 * acf_array
1206 *
1207 * Casts the value into an array.
1208 *
1209 * @date 9/1/19
1210 * @since 5.7.10
1211 *
1212 * @param mixed $val The value to cast.
1213 * @return array
1214 */
1215 function acf_array( $val = array() ) {
1216 return (array) $val;
1217 }
1218
1219 /*
1220 * acf_get_array
1221 *
1222 * This function will force a variable to become an array
1223 *
1224 * @type function
1225 * @date 4/02/2014
1226 * @since 5.0.0
1227 *
1228 * @param $var (mixed)
1229 * @return (array)
1230 */
1231
1232 function acf_get_array( $var = false, $delimiter = '' ) {
1233
1234 // array
1235 if( is_array($var) ) {
1236 return $var;
1237 }
1238
1239
1240 // bail early if empty
1241 if( acf_is_empty($var) ) {
1242 return array();
1243 }
1244
1245
1246 // string
1247 if( is_string($var) && $delimiter ) {
1248 return explode($delimiter, $var);
1249 }
1250
1251
1252 // place in array
1253 return (array) $var;
1254
1255 }
1256
1257
1258 /*
1259 * acf_get_numeric
1260 *
1261 * This function will return numeric values
1262 *
1263 * @type function
1264 * @date 15/07/2016
1265 * @since 5.4.0
1266 *
1267 * @param $value (mixed)
1268 * @return (mixed)
1269 */
1270
1271 function acf_get_numeric( $value = '' ) {
1272
1273 // vars
1274 $numbers = array();
1275 $is_array = is_array($value);
1276
1277
1278 // loop
1279 foreach( (array) $value as $v ) {
1280
1281 if( is_numeric($v) ) $numbers[] = (int) $v;
1282
1283 }
1284
1285
1286 // bail early if is empty
1287 if( empty($numbers) ) return false;
1288
1289
1290 // convert array
1291 if( !$is_array ) $numbers = $numbers[0];
1292
1293
1294 // return
1295 return $numbers;
1296
1297 }
1298
1299
1300 /**
1301 * acf_get_posts
1302 *
1303 * Similar to the get_posts() function but with extra functionality.
1304 *
1305 * @date 3/03/15
1306 * @since 5.1.5
1307 *
1308 * @param array $args The query args.
1309 * @return array
1310 */
1311 function acf_get_posts( $args = array() ) {
1312
1313 // Vars.
1314 $posts = array();
1315
1316 // Apply default args.
1317 $args = wp_parse_args($args, array(
1318 'posts_per_page' => -1,
1319 'post_type' => '',
1320 'post_status' => 'any',
1321 'update_post_meta_cache' => false,
1322 'update_post_term_cache' => false
1323 ));
1324
1325 // Avoid default 'post' post_type by providing all public types.
1326 if( !$args['post_type'] ) {
1327 $args['post_type'] = acf_get_post_types();
1328 }
1329
1330 // Check if specifc post ID's have been provided.
1331 if( $args['post__in'] ) {
1332
1333 // Clean value into an array of IDs.
1334 $args['post__in'] = array_map('intval', acf_array($args['post__in']));
1335 }
1336
1337 // Query posts.
1338 $posts = get_posts( $args );
1339
1340 // Remove any potential empty results.
1341 $posts = array_filter( $posts );
1342
1343 // Manually order results.
1344 if( $posts && $args['post__in'] ) {
1345 $order = array();
1346 foreach( $posts as $i => $post ) {
1347 $order[ $i ] = array_search( $post->ID, $args['post__in'] );
1348 }
1349 array_multisort($order, $posts);
1350 }
1351
1352 // Return posts.
1353 return $posts;
1354 }
1355
1356
1357 /*
1358 * _acf_query_remove_post_type
1359 *
1360 * This function will remove the 'wp_posts.post_type' WHERE clause completely
1361 * When using 'post__in', this clause is unneccessary and slow.
1362 *
1363 * @type function
1364 * @date 4/03/2015
1365 * @since 5.1.5
1366 *
1367 * @param $sql (string)
1368 * @return $sql
1369 */
1370
1371 function _acf_query_remove_post_type( $sql ) {
1372
1373 // global
1374 global $wpdb;
1375
1376
1377 // bail ealry if no 'wp_posts.ID IN'
1378 if( strpos($sql, "$wpdb->posts.ID IN") === false ) {
1379
1380 return $sql;
1381
1382 }
1383
1384
1385 // get bits
1386 $glue = 'AND';
1387 $bits = explode($glue, $sql);
1388
1389
1390 // loop through $where and remove any post_type queries
1391 foreach( $bits as $i => $bit ) {
1392
1393 if( strpos($bit, "$wpdb->posts.post_type") !== false ) {
1394
1395 unset( $bits[ $i ] );
1396
1397 }
1398
1399 }
1400
1401
1402 // join $where back together
1403 $sql = implode($glue, $bits);
1404
1405
1406 // return
1407 return $sql;
1408
1409 }
1410
1411
1412 /*
1413 * acf_get_grouped_posts
1414 *
1415 * This function will return all posts grouped by post_type
1416 * This is handy for select settings
1417 *
1418 * @type function
1419 * @date 27/02/2014
1420 * @since 5.0.0
1421 *
1422 * @param $args (array)
1423 * @return (array)
1424 */
1425
1426 function acf_get_grouped_posts( $args ) {
1427
1428 // vars
1429 $data = array();
1430
1431
1432 // defaults
1433 $args = wp_parse_args( $args, array(
1434 'posts_per_page' => -1,
1435 'paged' => 0,
1436 'post_type' => 'post',
1437 'orderby' => 'menu_order title',
1438 'order' => 'ASC',
1439 'post_status' => 'any',
1440 'suppress_filters' => false,
1441 'update_post_meta_cache' => false,
1442 ));
1443
1444
1445 // find array of post_type
1446 $post_types = acf_get_array( $args['post_type'] );
1447 $post_types_labels = acf_get_pretty_post_types($post_types);
1448 $is_single_post_type = ( count($post_types) == 1 );
1449
1450
1451 // attachment doesn't work if it is the only item in an array
1452 if( $is_single_post_type ) {
1453 $args['post_type'] = reset($post_types);
1454 }
1455
1456
1457 // add filter to orderby post type
1458 if( !$is_single_post_type ) {
1459 add_filter('posts_orderby', '_acf_orderby_post_type', 10, 2);
1460 }
1461
1462
1463 // get posts
1464 $posts = get_posts( $args );
1465
1466
1467 // remove this filter (only once)
1468 if( !$is_single_post_type ) {
1469 remove_filter('posts_orderby', '_acf_orderby_post_type', 10, 2);
1470 }
1471
1472
1473 // loop
1474 foreach( $post_types as $post_type ) {
1475
1476 // vars
1477 $this_posts = array();
1478 $this_group = array();
1479
1480
1481 // populate $this_posts
1482 foreach( $posts as $post ) {
1483 if( $post->post_type == $post_type ) {
1484 $this_posts[] = $post;
1485 }
1486 }
1487
1488
1489 // bail early if no posts for this post type
1490 if( empty($this_posts) ) continue;
1491
1492
1493 // sort into hierachial order!
1494 // this will fail if a search has taken place because parents wont exist
1495 if( is_post_type_hierarchical($post_type) && empty($args['s'])) {
1496
1497 // vars
1498 $post_id = $this_posts[0]->ID;
1499 $parent_id = acf_maybe_get($args, 'post_parent', 0);
1500 $offset = 0;
1501 $length = count($this_posts);
1502
1503
1504 // get all posts from this post type
1505 $all_posts = get_posts(array_merge($args, array(
1506 'posts_per_page' => -1,
1507 'paged' => 0,
1508 'post_type' => $post_type
1509 )));
1510
1511
1512 // find starting point (offset)
1513 foreach( $all_posts as $i => $post ) {
1514 if( $post->ID == $post_id ) {
1515 $offset = $i;
1516 break;
1517 }
1518 }
1519
1520
1521 // order posts
1522 $ordered_posts = get_page_children($parent_id, $all_posts);
1523
1524
1525 // compare aray lengths
1526 // if $ordered_posts is smaller than $all_posts, WP has lost posts during the get_page_children() function
1527 // this is possible when get_post( $args ) filter out parents (via taxonomy, meta and other search parameters)
1528 if( count($ordered_posts) == count($all_posts) ) {
1529 $this_posts = array_slice($ordered_posts, $offset, $length);
1530 }
1531
1532 }
1533
1534
1535 // populate $this_posts
1536 foreach( $this_posts as $post ) {
1537 $this_group[ $post->ID ] = $post;
1538 }
1539
1540
1541 // group by post type
1542 $label = $post_types_labels[ $post_type ];
1543 $data[ $label ] = $this_group;
1544
1545 }
1546
1547
1548 // return
1549 return $data;
1550
1551 }
1552
1553
1554 function _acf_orderby_post_type( $ordeby, $wp_query ) {
1555
1556 // global
1557 global $wpdb;
1558
1559
1560 // get post types
1561 $post_types = $wp_query->get('post_type');
1562
1563
1564 // prepend SQL
1565 if( is_array($post_types) ) {
1566
1567 $post_types = implode("','", $post_types);
1568 $ordeby = "FIELD({$wpdb->posts}.post_type,'$post_types')," . $ordeby;
1569
1570 }
1571
1572
1573 // return
1574 return $ordeby;
1575
1576 }
1577
1578
1579 function acf_get_post_title( $post = 0, $is_search = false ) {
1580
1581 // vars
1582 $post = get_post($post);
1583 $title = '';
1584 $prepend = '';
1585 $append = '';
1586
1587
1588 // bail early if no post
1589 if( !$post ) return '';
1590
1591
1592 // title
1593 $title = get_the_title( $post->ID );
1594
1595
1596 // empty
1597 if( $title === '' ) {
1598
1599 $title = __('(no title)', 'acf');
1600
1601 }
1602
1603
1604 // status
1605 if( get_post_status( $post->ID ) != "publish" ) {
1606
1607 $append .= ' (' . get_post_status( $post->ID ) . ')';
1608
1609 }
1610
1611
1612 // ancestors
1613 if( $post->post_type !== 'attachment' ) {
1614
1615 // get ancestors
1616 $ancestors = get_ancestors( $post->ID, $post->post_type );
1617 $prepend .= str_repeat('- ', count($ancestors));
1618
1619
1620 // add parent
1621 /*
1622 removed in 5.6.5 as not used by the UI
1623 if( $is_search && !empty($ancestors) ) {
1624
1625 // reverse
1626 $ancestors = array_reverse($ancestors);
1627
1628
1629 // convert id's into titles
1630 foreach( $ancestors as $i => $id ) {
1631
1632 $ancestors[ $i ] = get_the_title( $id );
1633
1634 }
1635
1636
1637 // append
1638 $append .= ' | ' . __('Parent', 'acf') . ': ' . implode(' / ', $ancestors);
1639
1640 }
1641 */
1642
1643 }
1644
1645
1646 // merge
1647 $title = $prepend . $title . $append;
1648
1649
1650 // return
1651 return $title;
1652
1653 }
1654
1655
1656 function acf_order_by_search( $array, $search ) {
1657
1658 // vars
1659 $weights = array();
1660 $needle = strtolower( $search );
1661
1662
1663 // add key prefix
1664 foreach( array_keys($array) as $k ) {
1665
1666 $array[ '_' . $k ] = acf_extract_var( $array, $k );
1667
1668 }
1669
1670
1671 // add search weight
1672 foreach( $array as $k => $v ) {
1673
1674 // vars
1675 $weight = 0;
1676 $haystack = strtolower( $v );
1677 $strpos = strpos( $haystack, $needle );
1678
1679
1680 // detect search match
1681 if( $strpos !== false ) {
1682
1683 // set eright to length of match
1684 $weight = strlen( $search );
1685
1686
1687 // increase weight if match starts at begining of string
1688 if( $strpos == 0 ) {
1689
1690 $weight++;
1691
1692 }
1693
1694 }
1695
1696
1697 // append to wights
1698 $weights[ $k ] = $weight;
1699
1700 }
1701
1702
1703 // sort the array with menu_order ascending
1704 array_multisort( $weights, SORT_DESC, $array );
1705
1706
1707 // remove key prefix
1708 foreach( array_keys($array) as $k ) {
1709
1710 $array[ substr($k,1) ] = acf_extract_var( $array, $k );
1711
1712 }
1713
1714
1715 // return
1716 return $array;
1717 }
1718
1719
1720 /*
1721 * acf_get_pretty_user_roles
1722 *
1723 * description
1724 *
1725 * @type function
1726 * @date 23/02/2016
1727 * @since 5.3.2
1728 *
1729 * @param $post_id (int)
1730 * @return $post_id (int)
1731 */
1732
1733 function acf_get_pretty_user_roles( $allowed = false ) {
1734
1735 // vars
1736 $editable_roles = get_editable_roles();
1737 $allowed = acf_get_array($allowed);
1738 $roles = array();
1739
1740
1741 // loop
1742 foreach( $editable_roles as $role_name => $role_details ) {
1743
1744 // bail early if not allowed
1745 if( !empty($allowed) && !in_array($role_name, $allowed) ) continue;
1746
1747
1748 // append
1749 $roles[ $role_name ] = translate_user_role( $role_details['name'] );
1750
1751 }
1752
1753
1754 // return
1755 return $roles;
1756
1757 }
1758
1759
1760 /*
1761 * acf_get_grouped_users
1762 *
1763 * This function will return all users grouped by role
1764 * This is handy for select settings
1765 *
1766 * @type function
1767 * @date 27/02/2014
1768 * @since 5.0.0
1769 *
1770 * @param $args (array)
1771 * @return (array)
1772 */
1773
1774 function acf_get_grouped_users( $args = array() ) {
1775
1776 // vars
1777 $r = array();
1778
1779
1780 // defaults
1781 $args = wp_parse_args( $args, array(
1782 'users_per_page' => -1,
1783 'paged' => 0,
1784 'role' => '',
1785 'orderby' => 'login',
1786 'order' => 'ASC',
1787 ));
1788
1789
1790 // offset
1791 $i = 0;
1792 $min = 0;
1793 $max = 0;
1794 $users_per_page = acf_extract_var($args, 'users_per_page');
1795 $paged = acf_extract_var($args, 'paged');
1796
1797 if( $users_per_page > 0 ) {
1798
1799 // prevent paged from being -1
1800 $paged = max(0, $paged);
1801
1802
1803 // set min / max
1804 $min = (($paged-1) * $users_per_page) + 1; // 1, 11
1805 $max = ($paged * $users_per_page); // 10, 20
1806
1807 }
1808
1809
1810 // find array of post_type
1811 $user_roles = acf_get_pretty_user_roles($args['role']);
1812
1813
1814 // fix role
1815 if( is_array($args['role']) ) {
1816
1817 // global
1818 global $wp_version, $wpdb;
1819
1820
1821 // vars
1822 $roles = acf_extract_var($args, 'role');
1823
1824
1825 // new WP has role__in
1826 if( version_compare($wp_version, '4.4', '>=' ) ) {
1827
1828 $args['role__in'] = $roles;
1829
1830 // old WP doesn't have role__in
1831 } else {
1832
1833 // vars
1834 $blog_id = get_current_blog_id();
1835 $meta_query = array( 'relation' => 'OR' );
1836
1837
1838 // loop
1839 foreach( $roles as $role ) {
1840
1841 $meta_query[] = array(
1842 'key' => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
1843 'value' => '"' . $role . '"',
1844 'compare' => 'LIKE',
1845 );
1846
1847 }
1848
1849
1850 // append
1851 $args['meta_query'] = $meta_query;
1852
1853 }
1854
1855 }
1856
1857
1858 // get posts
1859 $users = get_users( $args );
1860
1861
1862 // loop
1863 foreach( $user_roles as $user_role_name => $user_role_label ) {
1864
1865 // vars
1866 $this_users = array();
1867 $this_group = array();
1868
1869
1870 // populate $this_posts
1871 foreach( array_keys($users) as $key ) {
1872
1873 // bail ealry if not correct role
1874 if( !in_array($user_role_name, $users[ $key ]->roles) ) continue;
1875
1876
1877 // extract user
1878 $user = acf_extract_var( $users, $key );
1879
1880
1881 // increase
1882 $i++;
1883
1884
1885 // bail ealry if too low
1886 if( $min && $i < $min ) continue;
1887
1888
1889 // bail early if too high (don't bother looking at any more users)
1890 if( $max && $i > $max ) break;
1891
1892
1893 // group by post type
1894 $this_users[ $user->ID ] = $user;
1895
1896
1897 }
1898
1899
1900 // bail early if no posts for this post type
1901 if( empty($this_users) ) continue;
1902
1903
1904 // append
1905 $r[ $user_role_label ] = $this_users;
1906
1907 }
1908
1909
1910 // return
1911 return $r;
1912
1913 }
1914
1915 /**
1916 * acf_json_encode
1917 *
1918 * Returns json_encode() ready for file / database use.
1919 *
1920 * @date 29/4/19
1921 * @since 5.0.0
1922 *
1923 * @param array $json The array of data to encode.
1924 * @return string
1925 */
1926 function acf_json_encode( $json ) {
1927 return json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
1928 }
1929
1930
1931 /*
1932 * acf_str_exists
1933 *
1934 * This function will return true if a sub string is found
1935 *
1936 * @type function
1937 * @date 1/05/2014
1938 * @since 5.0.0
1939 *
1940 * @param $needle (string)
1941 * @param $haystack (string)
1942 * @return (boolean)
1943 */
1944
1945 function acf_str_exists( $needle, $haystack ) {
1946
1947 // return true if $haystack contains the $needle
1948 if( is_string($haystack) && strpos($haystack, $needle) !== false ) {
1949
1950 return true;
1951
1952 }
1953
1954
1955 // return
1956 return false;
1957 }
1958
1959
1960 /*
1961 * acf_debug
1962 *
1963 * description
1964 *
1965 * @type function
1966 * @date 2/05/2014
1967 * @since 5.0.0
1968 *
1969 * @param $post_id (int)
1970 * @return $post_id (int)
1971 */
1972
1973 function acf_debug() {
1974
1975 // vars
1976 $args = func_get_args();
1977 $s = array_shift($args);
1978 $o = '';
1979 $nl = "\r\n";
1980
1981
1982 // start script
1983 $o .= '<script type="text/javascript">' . $nl;
1984
1985 $o .= 'console.log("' . $s . '"';
1986
1987 if( !empty($args) ) {
1988
1989 foreach( $args as $arg ) {
1990
1991 if( is_object($arg) || is_array($arg) ) {
1992
1993 $arg = json_encode($arg);
1994
1995 } elseif( is_bool($arg) ) {
1996
1997 $arg = $arg ? 'true' : 'false';
1998
1999 }elseif( is_string($arg) ) {
2000
2001 $arg = '"' . $arg . '"';
2002
2003 }
2004
2005 $o .= ', ' . $arg;
2006
2007 }
2008 }
2009
2010 $o .= ');' . $nl;
2011
2012
2013 // end script
2014 $o .= '</script>' . $nl;
2015
2016
2017 // echo
2018 echo $o;
2019 }
2020
2021 function acf_debug_start() {
2022
2023 acf_update_setting( 'debug_start', memory_get_usage());
2024
2025 }
2026
2027 function acf_debug_end() {
2028
2029 $start = acf_get_setting( 'debug_start' );
2030 $end = memory_get_usage();
2031
2032 return $end - $start;
2033
2034 }
2035
2036
2037 /*
2038 * acf_encode_choices
2039 *
2040 * description
2041 *
2042 * @type function
2043 * @date 4/06/2014
2044 * @since 5.0.0
2045 *
2046 * @param $post_id (int)
2047 * @return $post_id (int)
2048 */
2049
2050 function acf_encode_choices( $array = array(), $show_keys = true ) {
2051
2052 // bail early if not array (maybe a single string)
2053 if( !is_array($array) ) return $array;
2054
2055
2056 // bail early if empty array
2057 if( empty($array) ) return '';
2058
2059
2060 // vars
2061 $string = '';
2062
2063
2064 // if allowed to show keys (good for choices, not for default values)
2065 if( $show_keys ) {
2066
2067 // loop
2068 foreach( $array as $k => $v ) {
2069
2070 // ignore if key and value are the same
2071 if( strval($k) == strval($v) ) continue;
2072
2073
2074 // show key in the value
2075 $array[ $k ] = $k . ' : ' . $v;
2076
2077 }
2078
2079 }
2080
2081
2082 // implode
2083 $string = implode("\n", $array);
2084
2085
2086 // return
2087 return $string;
2088
2089 }
2090
2091 function acf_decode_choices( $string = '', $array_keys = false ) {
2092
2093 // bail early if already array
2094 if( is_array($string) ) {
2095
2096 return $string;
2097
2098 // allow numeric values (same as string)
2099 } elseif( is_numeric($string) ) {
2100
2101 // do nothing
2102
2103 // bail early if not a string
2104 } elseif( !is_string($string) ) {
2105
2106 return array();
2107
2108 // bail early if is empty string
2109 } elseif( $string === '' ) {
2110
2111 return array();
2112
2113 }
2114
2115
2116 // vars
2117 $array = array();
2118
2119
2120 // explode
2121 $lines = explode("\n", $string);
2122
2123
2124 // key => value
2125 foreach( $lines as $line ) {
2126
2127 // vars
2128 $k = trim($line);
2129 $v = trim($line);
2130
2131
2132 // look for ' : '
2133 if( acf_str_exists(' : ', $line) ) {
2134
2135 $line = explode(' : ', $line);
2136
2137 $k = trim($line[0]);
2138 $v = trim($line[1]);
2139
2140 }
2141
2142
2143 // append
2144 $array[ $k ] = $v;
2145
2146 }
2147
2148
2149 // return only array keys? (good for checkbox default_value)
2150 if( $array_keys ) {
2151
2152 return array_keys($array);
2153
2154 }
2155
2156
2157 // return
2158 return $array;
2159
2160 }
2161
2162
2163 /*
2164 * acf_str_replace
2165 *
2166 * This function will replace an array of strings much like str_replace
2167 * The difference is the extra logic to avoid replacing a string that has alread been replaced
2168 * This is very useful for replacing date characters as they overlap with eachother
2169 *
2170 * @type function
2171 * @date 21/06/2016
2172 * @since 5.3.8
2173 *
2174 * @param $post_id (int)
2175 * @return $post_id (int)
2176 */
2177
2178 function acf_str_replace( $string = '', $search_replace = array() ) {
2179
2180 // vars
2181 $ignore = array();
2182
2183
2184 // remove potential empty search to avoid PHP error
2185 unset($search_replace['']);
2186
2187
2188 // loop over conversions
2189 foreach( $search_replace as $search => $replace ) {
2190
2191 // ignore this search, it was a previous replace
2192 if( in_array($search, $ignore) ) continue;
2193
2194
2195 // bail early if subsctring not found
2196 if( strpos($string, $search) === false ) continue;
2197
2198
2199 // replace
2200 $string = str_replace($search, $replace, $string);
2201
2202
2203 // append to ignore
2204 $ignore[] = $replace;
2205
2206 }
2207
2208
2209 // return
2210 return $string;
2211
2212 }
2213
2214
2215 /*
2216 * date & time formats
2217 *
2218 * These settings contain an association of format strings from PHP => JS
2219 *
2220 * @type function
2221 * @date 21/06/2016
2222 * @since 5.3.8
2223 *
2224 * @param n/a
2225 * @return n/a
2226 */
2227
2228 acf_update_setting('php_to_js_date_formats', array(
2229
2230 // Year
2231 'Y' => 'yy', // Numeric, 4 digits 1999, 2003
2232 'y' => 'y', // Numeric, 2 digits 99, 03
2233
2234
2235 // Month
2236 'm' => 'mm', // Numeric, with leading zeros 01–12
2237 'n' => 'm', // Numeric, without leading zeros 1–12
2238 'F' => 'MM', // Textual full January – December
2239 'M' => 'M', // Textual three letters Jan - Dec
2240
2241
2242 // Weekday
2243 'l' => 'DD', // Full name (lowercase 'L') Sunday – Saturday
2244 'D' => 'D', // Three letter name Mon – Sun
2245
2246
2247 // Day of Month
2248 'd' => 'dd', // Numeric, with leading zeros 01–31
2249 'j' => 'd', // Numeric, without leading zeros 1–31
2250 'S' => '', // The English suffix for the day of the month st, nd or th in the 1st, 2nd or 15th.
2251
2252 ));
2253
2254 acf_update_setting('php_to_js_time_formats', array(
2255
2256 'a' => 'tt', // Lowercase Ante meridiem and Post meridiem am or pm
2257 'A' => 'TT', // Uppercase Ante meridiem and Post meridiem AM or PM
2258 'h' => 'hh', // 12-hour format of an hour with leading zeros 01 through 12
2259 'g' => 'h', // 12-hour format of an hour without leading zeros 1 through 12
2260 'H' => 'HH', // 24-hour format of an hour with leading zeros 00 through 23
2261 'G' => 'H', // 24-hour format of an hour without leading zeros 0 through 23
2262 'i' => 'mm', // Minutes with leading zeros 00 to 59
2263 's' => 'ss', // Seconds, with leading zeros 00 through 59
2264
2265 ));
2266
2267
2268 /*
2269 * acf_split_date_time
2270 *
2271 * This function will split a format string into seperate date and time
2272 *
2273 * @type function
2274 * @date 26/05/2016
2275 * @since 5.3.8
2276 *
2277 * @param $date_time (string)
2278 * @return $formats (array)
2279 */
2280
2281 function acf_split_date_time( $date_time = '' ) {
2282
2283 // vars
2284 $php_date = acf_get_setting('php_to_js_date_formats');
2285 $php_time = acf_get_setting('php_to_js_time_formats');
2286 $chars = str_split($date_time);
2287 $type = 'date';
2288
2289
2290 // default
2291 $data = array(
2292 'date' => '',
2293 'time' => ''
2294 );
2295
2296
2297 // loop
2298 foreach( $chars as $i => $c ) {
2299
2300 // find type
2301 // - allow misc characters to append to previous type
2302 if( isset($php_date[ $c ]) ) {
2303
2304 $type = 'date';
2305
2306 } elseif( isset($php_time[ $c ]) ) {
2307
2308 $type = 'time';
2309
2310 }
2311
2312
2313 // append char
2314 $data[ $type ] .= $c;
2315
2316 }
2317
2318
2319 // trim
2320 $data['date'] = trim($data['date']);
2321 $data['time'] = trim($data['time']);
2322
2323
2324 // return
2325 return $data;
2326
2327 }
2328
2329
2330 /*
2331 * acf_convert_date_to_php
2332 *
2333 * This fucntion converts a date format string from JS to PHP
2334 *
2335 * @type function
2336 * @date 20/06/2014
2337 * @since 5.0.0
2338 *
2339 * @param $date (string)
2340 * @return (string)
2341 */
2342
2343 function acf_convert_date_to_php( $date = '' ) {
2344
2345 // vars
2346 $php_to_js = acf_get_setting('php_to_js_date_formats');
2347 $js_to_php = array_flip($php_to_js);
2348
2349
2350 // return
2351 return acf_str_replace( $date, $js_to_php );
2352
2353 }
2354
2355 /*
2356 * acf_convert_date_to_js
2357 *
2358 * This fucntion converts a date format string from PHP to JS
2359 *
2360 * @type function
2361 * @date 20/06/2014
2362 * @since 5.0.0
2363 *
2364 * @param $date (string)
2365 * @return (string)
2366 */
2367
2368 function acf_convert_date_to_js( $date = '' ) {
2369
2370 // vars
2371 $php_to_js = acf_get_setting('php_to_js_date_formats');
2372
2373
2374 // return
2375 return acf_str_replace( $date, $php_to_js );
2376
2377 }
2378
2379
2380 /*
2381 * acf_convert_time_to_php
2382 *
2383 * This fucntion converts a time format string from JS to PHP
2384 *
2385 * @type function
2386 * @date 20/06/2014
2387 * @since 5.0.0
2388 *
2389 * @param $time (string)
2390 * @return (string)
2391 */
2392
2393 function acf_convert_time_to_php( $time = '' ) {
2394
2395 // vars
2396 $php_to_js = acf_get_setting('php_to_js_time_formats');
2397 $js_to_php = array_flip($php_to_js);
2398
2399
2400 // return
2401 return acf_str_replace( $time, $js_to_php );
2402
2403 }
2404
2405
2406 /*
2407 * acf_convert_time_to_js
2408 *
2409 * This fucntion converts a date format string from PHP to JS
2410 *
2411 * @type function
2412 * @date 20/06/2014
2413 * @since 5.0.0
2414 *
2415 * @param $time (string)
2416 * @return (string)
2417 */
2418
2419 function acf_convert_time_to_js( $time = '' ) {
2420
2421 // vars
2422 $php_to_js = acf_get_setting('php_to_js_time_formats');
2423
2424
2425 // return
2426 return acf_str_replace( $time, $php_to_js );
2427
2428 }
2429
2430
2431 /*
2432 * acf_update_user_setting
2433 *
2434 * description
2435 *
2436 * @type function
2437 * @date 15/07/2014
2438 * @since 5.0.0
2439 *
2440 * @param $post_id (int)
2441 * @return $post_id (int)
2442 */
2443
2444 function acf_update_user_setting( $name, $value ) {
2445
2446 // get current user id
2447 $user_id = get_current_user_id();
2448
2449
2450 // get user settings
2451 $settings = get_user_meta( $user_id, 'acf_user_settings', true );
2452
2453
2454 // ensure array
2455 $settings = acf_get_array($settings);
2456
2457
2458 // delete setting (allow 0 to save)
2459 if( acf_is_empty($value) ) {
2460
2461 unset($settings[ $name ]);
2462
2463 // append setting
2464 } else {
2465
2466 $settings[ $name ] = $value;
2467
2468 }
2469
2470
2471 // update user data
2472 return update_metadata('user', $user_id, 'acf_user_settings', $settings);
2473
2474 }
2475
2476
2477 /*
2478 * acf_get_user_setting
2479 *
2480 * description
2481 *
2482 * @type function
2483 * @date 15/07/2014
2484 * @since 5.0.0
2485 *
2486 * @param $post_id (int)
2487 * @return $post_id (int)
2488 */
2489
2490 function acf_get_user_setting( $name = '', $default = false ) {
2491
2492 // get current user id
2493 $user_id = get_current_user_id();
2494
2495
2496 // get user settings
2497 $settings = get_user_meta( $user_id, 'acf_user_settings', true );
2498
2499
2500 // ensure array
2501 $settings = acf_get_array($settings);
2502
2503
2504 // bail arly if no settings
2505 if( !isset($settings[$name]) ) return $default;
2506
2507
2508 // return
2509 return $settings[$name];
2510
2511 }
2512
2513
2514 /*
2515 * acf_in_array
2516 *
2517 * description
2518 *
2519 * @type function
2520 * @date 22/07/2014
2521 * @since 5.0.0
2522 *
2523 * @param $post_id (int)
2524 * @return $post_id (int)
2525 */
2526
2527 function acf_in_array( $value = '', $array = false ) {
2528
2529 // bail early if not array
2530 if( !is_array($array) ) return false;
2531
2532
2533 // find value in array
2534 return in_array($value, $array);
2535
2536 }
2537
2538
2539 /*
2540 * acf_get_valid_post_id
2541 *
2542 * This function will return a valid post_id based on the current screen / parameter
2543 *
2544 * @type function
2545 * @date 8/12/2013
2546 * @since 5.0.0
2547 *
2548 * @param $post_id (mixed)
2549 * @return $post_id (mixed)
2550 */
2551
2552 function acf_get_valid_post_id( $post_id = 0 ) {
2553
2554 // allow filter to short-circuit load_value logic
2555 $preload = apply_filters( "acf/pre_load_post_id", null, $post_id );
2556 if( $preload !== null ) {
2557 return $preload;
2558 }
2559
2560 // vars
2561 $_post_id = $post_id;
2562
2563
2564 // if not $post_id, load queried object
2565 if( !$post_id ) {
2566
2567 // try for global post (needed for setup_postdata)
2568 $post_id = (int) get_the_ID();
2569
2570
2571 // try for current screen
2572 if( !$post_id ) {
2573
2574 $post_id = get_queried_object();
2575
2576 }
2577
2578 }
2579
2580
2581 // $post_id may be an object
2582 if( is_object($post_id) ) {
2583
2584 // post
2585 if( isset($post_id->post_type, $post_id->ID) ) {
2586
2587 $post_id = $post_id->ID;
2588
2589 // user
2590 } elseif( isset($post_id->roles, $post_id->ID) ) {
2591
2592 $post_id = 'user_' . $post_id->ID;
2593
2594 // term
2595 } elseif( isset($post_id->taxonomy, $post_id->term_id) ) {
2596
2597 $post_id = acf_get_term_post_id( $post_id->taxonomy, $post_id->term_id );
2598
2599 // comment
2600 } elseif( isset($post_id->comment_ID) ) {
2601
2602 $post_id = 'comment_' . $post_id->comment_ID;
2603
2604 // default
2605 } else {
2606
2607 $post_id = 0;
2608
2609 }
2610
2611 }
2612
2613
2614 // allow for option == options
2615 if( $post_id === 'option' ) {
2616
2617 $post_id = 'options';
2618
2619 }
2620
2621
2622 // append language code
2623 if( $post_id == 'options' ) {
2624
2625 $dl = acf_get_setting('default_language');
2626 $cl = acf_get_setting('current_language');
2627
2628 if( $cl && $cl !== $dl ) {
2629
2630 $post_id .= '_' . $cl;
2631
2632 }
2633
2634 }
2635
2636
2637
2638 // filter for 3rd party
2639 $post_id = apply_filters('acf/validate_post_id', $post_id, $_post_id);
2640
2641
2642 // return
2643 return $post_id;
2644
2645 }
2646
2647
2648
2649 /*
2650 * acf_get_post_id_info
2651 *
2652 * This function will return the type and id for a given $post_id string
2653 *
2654 * @type function
2655 * @date 2/07/2016
2656 * @since 5.4.0
2657 *
2658 * @param $post_id (mixed)
2659 * @return $info (array)
2660 */
2661
2662 function acf_get_post_id_info( $post_id = 0 ) {
2663
2664 // vars
2665 $info = array(
2666 'type' => 'post',
2667 'id' => 0
2668 );
2669
2670 // bail early if no $post_id
2671 if( !$post_id ) return $info;
2672
2673
2674 // check cache
2675 // - this function will most likely be called multiple times (saving loading fields from post)
2676 //$cache_key = "get_post_id_info/post_id={$post_id}";
2677
2678 //if( acf_isset_cache($cache_key) ) return acf_get_cache($cache_key);
2679
2680
2681 // numeric
2682 if( is_numeric($post_id) ) {
2683
2684 $info['id'] = (int) $post_id;
2685
2686 // string
2687 } elseif( is_string($post_id) ) {
2688
2689 // vars
2690 $glue = '_';
2691 $type = explode($glue, $post_id);
2692 $id = array_pop($type);
2693 $type = implode($glue, $type);
2694 $meta = array('post', 'user', 'comment', 'term');
2695
2696
2697 // check if is taxonomy (ACF < 5.5)
2698 // - avoid scenario where taxonomy exists with name of meta type
2699 if( !in_array($type, $meta) && acf_isset_termmeta($type) ) $type = 'term';
2700
2701
2702 // meta
2703 if( is_numeric($id) && in_array($type, $meta) ) {
2704
2705 $info['type'] = $type;
2706 $info['id'] = (int) $id;
2707
2708 // option
2709 } else {
2710
2711 $info['type'] = 'option';
2712 $info['id'] = $post_id;
2713
2714 }
2715
2716 }
2717
2718
2719 // update cache
2720 //acf_set_cache($cache_key, $info);
2721
2722
2723 // filter
2724 $info = apply_filters("acf/get_post_id_info", $info, $post_id);
2725
2726 // return
2727 return $info;
2728
2729 }
2730
2731
2732 /*
2733
2734 acf_log( acf_get_post_id_info(4) );
2735
2736 acf_log( acf_get_post_id_info('post_4') );
2737
2738 acf_log( acf_get_post_id_info('user_123') );
2739
2740 acf_log( acf_get_post_id_info('term_567') );
2741
2742 acf_log( acf_get_post_id_info('category_204') );
2743
2744 acf_log( acf_get_post_id_info('comment_6') );
2745
2746 acf_log( acf_get_post_id_info('options_lol!') );
2747
2748 acf_log( acf_get_post_id_info('option') );
2749
2750 acf_log( acf_get_post_id_info('options') );
2751
2752 */
2753
2754
2755 /*
2756 * acf_isset_termmeta
2757 *
2758 * This function will return true if the termmeta table exists
2759 * https://developer.wordpress.org/reference/functions/get_term_meta/
2760 *
2761 * @type function
2762 * @date 3/09/2016
2763 * @since 5.4.0
2764 *
2765 * @param $post_id (int)
2766 * @return $post_id (int)
2767 */
2768
2769 function acf_isset_termmeta( $taxonomy = '' ) {
2770
2771 // bail ealry if no table
2772 if( get_option('db_version') < 34370 ) return false;
2773
2774
2775 // check taxonomy
2776 if( $taxonomy && !taxonomy_exists($taxonomy) ) return false;
2777
2778
2779 // return
2780 return true;
2781
2782 }
2783
2784
2785 /*
2786 * acf_get_term_post_id
2787 *
2788 * This function will return a valid post_id string for a given term and taxonomy
2789 *
2790 * @type function
2791 * @date 6/2/17
2792 * @since 5.5.6
2793 *
2794 * @param $taxonomy (string)
2795 * @param $term_id (int)
2796 * @return (string)
2797 */
2798
2799 function acf_get_term_post_id( $taxonomy, $term_id ) {
2800
2801 // WP < 4.4
2802 if( !acf_isset_termmeta() ) {
2803
2804 return $taxonomy . '_' . $term_id;
2805
2806 }
2807
2808
2809 // return
2810 return 'term_' . $term_id;
2811
2812 }
2813
2814
2815 /*
2816 * acf_upload_files
2817 *
2818 * This function will walk througfh the $_FILES data and upload each found
2819 *
2820 * @type function
2821 * @date 25/10/2014
2822 * @since 5.0.9
2823 *
2824 * @param $ancestors (array) an internal parameter, not required
2825 * @return n/a
2826 */
2827
2828 function acf_upload_files( $ancestors = array() ) {
2829
2830 // vars
2831 $file = array(
2832 'name' => '',
2833 'type' => '',
2834 'tmp_name' => '',
2835 'error' => '',
2836 'size' => ''
2837 );
2838
2839
2840 // populate with $_FILES data
2841 foreach( array_keys($file) as $k ) {
2842
2843 $file[ $k ] = $_FILES['acf'][ $k ];
2844
2845 }
2846
2847
2848 // walk through ancestors
2849 if( !empty($ancestors) ) {
2850
2851 foreach( $ancestors as $a ) {
2852
2853 foreach( array_keys($file) as $k ) {
2854
2855 $file[ $k ] = $file[ $k ][ $a ];
2856
2857 }
2858
2859 }
2860
2861 }
2862
2863
2864 // is array?
2865 if( is_array($file['name']) ) {
2866
2867 foreach( array_keys($file['name']) as $k ) {
2868
2869 $_ancestors = array_merge($ancestors, array($k));
2870
2871 acf_upload_files( $_ancestors );
2872
2873 }
2874
2875 return;
2876
2877 }
2878
2879
2880 // bail ealry if file has error (no file uploaded)
2881 if( $file['error'] ) {
2882
2883 return;
2884
2885 }
2886
2887
2888 // assign global _acfuploader for media validation
2889 $_POST['_acfuploader'] = end($ancestors);
2890
2891
2892 // file found!
2893 $attachment_id = acf_upload_file( $file );
2894
2895
2896 // update $_POST
2897 array_unshift($ancestors, 'acf');
2898 acf_update_nested_array( $_POST, $ancestors, $attachment_id );
2899
2900 }
2901
2902
2903 /*
2904 * acf_upload_file
2905 *
2906 * This function will uploade a $_FILE
2907 *
2908 * @type function
2909 * @date 27/10/2014
2910 * @since 5.0.9
2911 *
2912 * @param $uploaded_file (array) array found from $_FILE data
2913 * @return $id (int) new attachment ID
2914 */
2915
2916 function acf_upload_file( $uploaded_file ) {
2917
2918 // required
2919 //require_once( ABSPATH . "/wp-load.php" ); // WP should already be loaded
2920 require_once( ABSPATH . "/wp-admin/includes/media.php" ); // video functions
2921 require_once( ABSPATH . "/wp-admin/includes/file.php" );
2922 require_once( ABSPATH . "/wp-admin/includes/image.php" );
2923
2924
2925 // required for wp_handle_upload() to upload the file
2926 $upload_overrides = array( 'test_form' => false );
2927
2928
2929 // upload
2930 $file = wp_handle_upload( $uploaded_file, $upload_overrides );
2931
2932
2933 // bail ealry if upload failed
2934 if( isset($file['error']) ) {
2935
2936 return $file['error'];
2937
2938 }
2939
2940
2941 // vars
2942 $url = $file['url'];
2943 $type = $file['type'];
2944 $file = $file['file'];
2945 $filename = basename($file);
2946
2947
2948 // Construct the object array
2949 $object = array(
2950 'post_title' => $filename,
2951 'post_mime_type' => $type,
2952 'guid' => $url
2953 );
2954
2955 // Save the data
2956 $id = wp_insert_attachment($object, $file);
2957
2958 // Add the meta-data
2959 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
2960
2961 /** This action is documented in wp-admin/custom-header.php */
2962 do_action( 'wp_create_file_in_uploads', $file, $id ); // For replication
2963
2964 // return new ID
2965 return $id;
2966
2967 }
2968
2969
2970 /*
2971 * acf_update_nested_array
2972 *
2973 * This function will update a nested array value. Useful for modifying the $_POST array
2974 *
2975 * @type function
2976 * @date 27/10/2014
2977 * @since 5.0.9
2978 *
2979 * @param $array (array) target array to be updated
2980 * @param $ancestors (array) array of keys to navigate through to find the child
2981 * @param $value (mixed) The new value
2982 * @return (boolean)
2983 */
2984
2985 function acf_update_nested_array( &$array, $ancestors, $value ) {
2986
2987 // if no more ancestors, update the current var
2988 if( empty($ancestors) ) {
2989
2990 $array = $value;
2991
2992 // return
2993 return true;
2994
2995 }
2996
2997
2998 // shift the next ancestor from the array
2999 $k = array_shift( $ancestors );
3000
3001
3002 // if exists
3003 if( isset($array[ $k ]) ) {
3004
3005 return acf_update_nested_array( $array[ $k ], $ancestors, $value );
3006
3007 }
3008
3009
3010 // return
3011 return false;
3012 }
3013
3014
3015 /*
3016 * acf_is_screen
3017 *
3018 * This function will return true if all args are matched for the current screen
3019 *
3020 * @type function
3021 * @date 9/12/2014
3022 * @since 5.1.5
3023 *
3024 * @param $post_id (int)
3025 * @return $post_id (int)
3026 */
3027
3028 function acf_is_screen( $id = '' ) {
3029
3030 // bail early if not defined
3031 if( !function_exists('get_current_screen') ) {
3032 return false;
3033 }
3034
3035 // vars
3036 $current_screen = get_current_screen();
3037
3038 // no screen
3039 if( !$current_screen ) {
3040 return false;
3041
3042 // array
3043 } elseif( is_array($id) ) {
3044 return in_array($current_screen->id, $id);
3045
3046 // string
3047 } else {
3048 return ($id === $current_screen->id);
3049 }
3050 }
3051
3052
3053 /*
3054 * acf_maybe_get
3055 *
3056 * This function will return a var if it exists in an array
3057 *
3058 * @type function
3059 * @date 9/12/2014
3060 * @since 5.1.5
3061 *
3062 * @param $array (array) the array to look within
3063 * @param $key (key) the array key to look for. Nested values may be found using '/'
3064 * @param $default (mixed) the value returned if not found
3065 * @return $post_id (int)
3066 */
3067
3068 function acf_maybe_get( $array = array(), $key = 0, $default = null ) {
3069
3070 return isset( $array[$key] ) ? $array[$key] : $default;
3071
3072 }
3073
3074 function acf_maybe_get_POST( $key = '', $default = null ) {
3075
3076 return isset( $_POST[$key] ) ? $_POST[$key] : $default;
3077
3078 }
3079
3080 function acf_maybe_get_GET( $key = '', $default = null ) {
3081
3082 return isset( $_GET[$key] ) ? $_GET[$key] : $default;
3083
3084 }
3085
3086
3087 /*
3088 * acf_get_attachment
3089 *
3090 * This function will return an array of attachment data
3091 *
3092 * @type function
3093 * @date 5/01/2015
3094 * @since 5.1.5
3095 *
3096 * @param $post (mixed) either post ID or post object
3097 * @return (array)
3098 */
3099
3100 function acf_get_attachment( $attachment ) {
3101
3102 // get post
3103 if( !$attachment = get_post($attachment) ) {
3104 return false;
3105 }
3106
3107 // validate post_type
3108 if( $attachment->post_type !== 'attachment' ) {
3109 return false;
3110 }
3111
3112 // vars
3113 $sizes_id = 0;
3114 $meta = wp_get_attachment_metadata( $attachment->ID );
3115 $attached_file = get_attached_file( $attachment->ID );
3116 $attachment_url = wp_get_attachment_url( $attachment->ID );
3117
3118 // get mime types
3119 if( strpos( $attachment->post_mime_type, '/' ) !== false ) {
3120 list( $type, $subtype ) = explode( '/', $attachment->post_mime_type );
3121 } else {
3122 list( $type, $subtype ) = array( $attachment->post_mime_type, '' );
3123 }
3124
3125 // vars
3126 $response = array(
3127 'ID' => $attachment->ID,
3128 'id' => $attachment->ID,
3129 'title' => $attachment->post_title,
3130 'filename' => wp_basename( $attached_file ),
3131 'filesize' => 0,
3132 'url' => $attachment_url,
3133 'link' => get_attachment_link( $attachment->ID ),
3134 'alt' => get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true ),
3135 'author' => $attachment->post_author,
3136 'description' => $attachment->post_content,
3137 'caption' => $attachment->post_excerpt,
3138 'name' => $attachment->post_name,
3139 'status' => $attachment->post_status,
3140 'uploaded_to' => $attachment->post_parent,
3141 'date' => $attachment->post_date_gmt,
3142 'modified' => $attachment->post_modified_gmt,
3143 'menu_order' => $attachment->menu_order,
3144 'mime_type' => $attachment->post_mime_type,
3145 'type' => $type,
3146 'subtype' => $subtype,
3147 'icon' => wp_mime_type_icon( $attachment->ID )
3148 );
3149
3150 // filesize
3151 if( isset($meta['filesize']) ) {
3152 $response['filesize'] = $meta['filesize'];
3153 } elseif( file_exists($attached_file) ) {
3154 $response['filesize'] = filesize( $attached_file );
3155 }
3156
3157 // image
3158 if( $type === 'image' ) {
3159
3160 $sizes_id = $attachment->ID;
3161 $src = wp_get_attachment_image_src( $attachment->ID, 'full' );
3162
3163 $response['url'] = $src[0];
3164 $response['width'] = $src[1];
3165 $response['height'] = $src[2];
3166
3167 // video
3168 } elseif( $type === 'video' ) {
3169
3170 // dimensions
3171 $response['width'] = acf_maybe_get($meta, 'width', 0);
3172 $response['height'] = acf_maybe_get($meta, 'height', 0);
3173
3174 // featured image
3175 if( $featured_id = get_post_thumbnail_id($attachment->ID) ) {
3176 $sizes_id = $featured_id;
3177 }
3178
3179 // audio
3180 } elseif( $type === 'audio' ) {
3181
3182 // featured image
3183 if( $featured_id = get_post_thumbnail_id($attachment->ID) ) {
3184 $sizes_id = $featured_id;
3185 }
3186 }
3187
3188
3189 // sizes
3190 if( $sizes_id ) {
3191
3192 // vars
3193 $sizes = get_intermediate_image_sizes();
3194 $data = array();
3195
3196 // loop
3197 foreach( $sizes as $size ) {
3198 $src = wp_get_attachment_image_src( $sizes_id, $size );
3199 $data[ $size ] = $src[0];
3200 $data[ $size . '-width' ] = $src[1];
3201 $data[ $size . '-height' ] = $src[2];
3202 }
3203
3204 // append
3205 $response['sizes'] = $data;
3206 }
3207
3208 // return
3209 return $response;
3210
3211 }
3212
3213
3214 /*
3215 * acf_get_truncated
3216 *
3217 * This function will truncate and return a string
3218 *
3219 * @type function
3220 * @date 8/08/2014
3221 * @since 5.0.0
3222 *
3223 * @param $text (string)
3224 * @param $length (int)
3225 * @return (string)
3226 */
3227
3228 function acf_get_truncated( $text, $length = 64 ) {
3229
3230 // vars
3231 $text = trim($text);
3232 $the_length = strlen( $text );
3233
3234
3235 // cut
3236 $return = substr( $text, 0, ($length - 3) );
3237
3238
3239 // ...
3240 if( $the_length > ($length - 3) ) {
3241
3242 $return .= '...';
3243
3244 }
3245
3246
3247 // return
3248 return $return;
3249
3250 }
3251
3252
3253 /*
3254 * acf_get_current_url
3255 *
3256 * This function will return the current URL.
3257 *
3258 * @date 23/01/2015
3259 * @since 5.1.5
3260 *
3261 * @param void
3262 * @return string
3263 */
3264
3265 function acf_get_current_url() {
3266 return ( is_ssl() ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
3267 }
3268
3269 /*
3270 * acf_current_user_can_admin
3271 *
3272 * This function will return true if the current user can administrate the ACF field groups
3273 *
3274 * @type function
3275 * @date 9/02/2015
3276 * @since 5.1.5
3277 *
3278 * @param $post_id (int)
3279 * @return $post_id (int)
3280 */
3281
3282 function acf_current_user_can_admin() {
3283
3284 if( acf_get_setting('show_admin') && current_user_can(acf_get_setting('capability')) ) {
3285
3286 return true;
3287
3288 }
3289
3290
3291 // return
3292 return false;
3293
3294 }
3295
3296
3297 /*
3298 * acf_get_filesize
3299 *
3300 * This function will return a numeric value of bytes for a given filesize string
3301 *
3302 * @type function
3303 * @date 18/02/2015
3304 * @since 5.1.5
3305 *
3306 * @param $size (mixed)
3307 * @return (int)
3308 */
3309
3310 function acf_get_filesize( $size = 1 ) {
3311
3312 // vars
3313 $unit = 'MB';
3314 $units = array(
3315 'TB' => 4,
3316 'GB' => 3,
3317 'MB' => 2,
3318 'KB' => 1,
3319 );
3320
3321
3322 // look for $unit within the $size parameter (123 KB)
3323 if( is_string($size) ) {
3324
3325 // vars
3326 $custom = strtoupper( substr($size, -2) );
3327
3328 foreach( $units as $k => $v ) {
3329
3330 if( $custom === $k ) {
3331
3332 $unit = $k;
3333 $size = substr($size, 0, -2);
3334
3335 }
3336
3337 }
3338
3339 }
3340
3341
3342 // calc bytes
3343 $bytes = floatval($size) * pow(1024, $units[$unit]);
3344
3345
3346 // return
3347 return $bytes;
3348
3349 }
3350
3351
3352 /*
3353 * acf_format_filesize
3354 *
3355 * This function will return a formatted string containing the filesize and unit
3356 *
3357 * @type function
3358 * @date 18/02/2015
3359 * @since 5.1.5
3360 *
3361 * @param $size (mixed)
3362 * @return (int)
3363 */
3364
3365 function acf_format_filesize( $size = 1 ) {
3366
3367 // convert
3368 $bytes = acf_get_filesize( $size );
3369
3370
3371 // vars
3372 $units = array(
3373 'TB' => 4,
3374 'GB' => 3,
3375 'MB' => 2,
3376 'KB' => 1,
3377 );
3378
3379
3380 // loop through units
3381 foreach( $units as $k => $v ) {
3382
3383 $result = $bytes / pow(1024, $v);
3384
3385 if( $result >= 1 ) {
3386
3387 return $result . ' ' . $k;
3388
3389 }
3390
3391 }
3392
3393
3394 // return
3395 return $bytes . ' B';
3396
3397 }
3398
3399
3400 /*
3401 * acf_get_valid_terms
3402 *
3403 * This function will replace old terms with new split term ids
3404 *
3405 * @type function
3406 * @date 27/02/2015
3407 * @since 5.1.5
3408 *
3409 * @param $terms (int|array)
3410 * @param $taxonomy (string)
3411 * @return $terms
3412 */
3413
3414 function acf_get_valid_terms( $terms = false, $taxonomy = 'category' ) {
3415
3416 // force into array
3417 $terms = acf_get_array($terms);
3418
3419
3420 // force ints
3421 $terms = array_map('intval', $terms);
3422
3423
3424 // bail early if function does not yet exist or
3425 if( !function_exists('wp_get_split_term') || empty($terms) ) {
3426
3427 return $terms;
3428
3429 }
3430
3431
3432 // attempt to find new terms
3433 foreach( $terms as $i => $term_id ) {
3434
3435 $new_term_id = wp_get_split_term($term_id, $taxonomy);
3436
3437 if( $new_term_id ) {
3438
3439 $terms[ $i ] = $new_term_id;
3440
3441 }
3442
3443 }
3444
3445
3446 // return
3447 return $terms;
3448
3449 }
3450
3451
3452 /*
3453 * acf_esc_html_deep
3454 *
3455 * Navigates through an array and escapes html from the values.
3456 *
3457 * @type function
3458 * @date 10/06/2015
3459 * @since 5.2.7
3460 *
3461 * @param $value (mixed)
3462 * @return $value
3463 */
3464
3465 /*
3466 function acf_esc_html_deep( $value ) {
3467
3468 // array
3469 if( is_array($value) ) {
3470
3471 $value = array_map('acf_esc_html_deep', $value);
3472
3473 // object
3474 } elseif( is_object($value) ) {
3475
3476 $vars = get_object_vars( $value );
3477
3478 foreach( $vars as $k => $v ) {
3479
3480 $value->{$k} = acf_esc_html_deep( $v );
3481
3482 }
3483
3484 // string
3485 } elseif( is_string($value) ) {
3486
3487 $value = esc_html($value);
3488
3489 }
3490
3491
3492 // return
3493 return $value;
3494
3495 }
3496 */
3497
3498
3499 /*
3500 * acf_validate_attachment
3501 *
3502 * This function will validate an attachment based on a field's resrictions and return an array of errors
3503 *
3504 * @type function
3505 * @date 3/07/2015
3506 * @since 5.2.3
3507 *
3508 * @param $attachment (array) attachment data. Cahnges based on context
3509 * @param $field (array) field settings containing restrictions
3510 * @param $context (string) $file is different when uploading / preparing
3511 * @return $errors (array)
3512 */
3513
3514 function acf_validate_attachment( $attachment, $field, $context = 'prepare' ) {
3515
3516 // vars
3517 $errors = array();
3518 $file = array(
3519 'type' => '',
3520 'width' => 0,
3521 'height' => 0,
3522 'size' => 0
3523 );
3524
3525
3526 // upload
3527 if( $context == 'upload' ) {
3528
3529 // vars
3530 $file['type'] = pathinfo($attachment['name'], PATHINFO_EXTENSION);
3531 $file['size'] = filesize($attachment['tmp_name']);
3532
3533 if( strpos($attachment['type'], 'image') !== false ) {
3534
3535 $size = getimagesize($attachment['tmp_name']);
3536 $file['width'] = acf_maybe_get($size, 0);
3537 $file['height'] = acf_maybe_get($size, 1);
3538
3539 }
3540
3541 // prepare
3542 } elseif( $context == 'prepare' ) {
3543
3544 $file['type'] = pathinfo($attachment['url'], PATHINFO_EXTENSION);
3545 $file['size'] = acf_maybe_get($attachment, 'filesizeInBytes', 0);
3546 $file['width'] = acf_maybe_get($attachment, 'width', 0);
3547 $file['height'] = acf_maybe_get($attachment, 'height', 0);
3548
3549 // custom
3550 } else {
3551
3552 $file = array_merge($file, $attachment);
3553 $file['type'] = pathinfo($attachment['url'], PATHINFO_EXTENSION);
3554
3555 }
3556
3557
3558 // image
3559 if( $file['width'] || $file['height'] ) {
3560
3561 // width
3562 $min_width = (int) acf_maybe_get($field, 'min_width', 0);
3563 $max_width = (int) acf_maybe_get($field, 'max_width', 0);
3564
3565 if( $file['width'] ) {
3566
3567 if( $min_width && $file['width'] < $min_width ) {
3568
3569 // min width
3570 $errors['min_width'] = sprintf(__('Image width must be at least %dpx.', 'acf'), $min_width );
3571
3572 } elseif( $max_width && $file['width'] > $max_width ) {
3573
3574 // min width
3575 $errors['max_width'] = sprintf(__('Image width must not exceed %dpx.', 'acf'), $max_width );
3576
3577 }
3578
3579 }
3580
3581
3582 // height
3583 $min_height = (int) acf_maybe_get($field, 'min_height', 0);
3584 $max_height = (int) acf_maybe_get($field, 'max_height', 0);
3585
3586 if( $file['height'] ) {
3587
3588 if( $min_height && $file['height'] < $min_height ) {
3589
3590 // min height
3591 $errors['min_height'] = sprintf(__('Image height must be at least %dpx.', 'acf'), $min_height );
3592
3593 } elseif( $max_height && $file['height'] > $max_height ) {
3594
3595 // min height
3596 $errors['max_height'] = sprintf(__('Image height must not exceed %dpx.', 'acf'), $max_height );
3597
3598 }
3599
3600 }
3601
3602 }
3603
3604
3605 // file size
3606 if( $file['size'] ) {
3607
3608 $min_size = acf_maybe_get($field, 'min_size', 0);
3609 $max_size = acf_maybe_get($field, 'max_size', 0);
3610
3611 if( $min_size && $file['size'] < acf_get_filesize($min_size) ) {
3612
3613 // min width
3614 $errors['min_size'] = sprintf(__('File size must be at least %s.', 'acf'), acf_format_filesize($min_size) );
3615
3616 } elseif( $max_size && $file['size'] > acf_get_filesize($max_size) ) {
3617
3618 // min width
3619 $errors['max_size'] = sprintf(__('File size must must not exceed %s.', 'acf'), acf_format_filesize($max_size) );
3620
3621 }
3622
3623 }
3624
3625
3626 // file type
3627 if( $file['type'] ) {
3628
3629 $mime_types = acf_maybe_get($field, 'mime_types', '');
3630
3631 // lower case
3632 $file['type'] = strtolower($file['type']);
3633 $mime_types = strtolower($mime_types);
3634
3635
3636 // explode
3637 $mime_types = str_replace(array(' ', '.'), '', $mime_types);
3638 $mime_types = explode(',', $mime_types); // split pieces
3639 $mime_types = array_filter($mime_types); // remove empty pieces
3640
3641 if( !empty($mime_types) && !in_array($file['type'], $mime_types) ) {
3642
3643 // glue together last 2 types
3644 if( count($mime_types) > 1 ) {
3645
3646 $last1 = array_pop($mime_types);
3647 $last2 = array_pop($mime_types);
3648
3649 $mime_types[] = $last2 . ' ' . __('or', 'acf') . ' ' . $last1;
3650
3651 }
3652
3653 $errors['mime_types'] = sprintf(__('File type must be %s.', 'acf'), implode(', ', $mime_types) );
3654
3655 }
3656
3657 }
3658
3659
3660 /**
3661 * Filters the errors for a file before it is uploaded or displayed in the media modal.
3662 *
3663 * @date 3/07/2015
3664 * @since 5.2.3
3665 *
3666 * @param array $errors An array of errors.
3667 * @param array $file An array of data for a single file.
3668 * @param array $attachment An array of attachment data which differs based on the context.
3669 * @param array $field The field array.
3670 * @param string $context The curent context (uploading, preparing)
3671 */
3672 $errors = apply_filters( "acf/validate_attachment/type={$field['type']}", $errors, $file, $attachment, $field, $context );
3673 $errors = apply_filters( "acf/validate_attachment/name={$field['_name']}", $errors, $file, $attachment, $field, $context );
3674 $errors = apply_filters( "acf/validate_attachment/key={$field['key']}", $errors, $file, $attachment, $field, $context );
3675 $errors = apply_filters( "acf/validate_attachment", $errors, $file, $attachment, $field, $context );
3676
3677
3678 // return
3679 return $errors;
3680
3681 }
3682
3683
3684 /*
3685 * _acf_settings_uploader
3686 *
3687 * Dynamic logic for uploader setting
3688 *
3689 * @type function
3690 * @date 7/05/2015
3691 * @since 5.2.3
3692 *
3693 * @param $uploader (string)
3694 * @return $uploader
3695 */
3696
3697 add_filter('acf/settings/uploader', '_acf_settings_uploader');
3698
3699 function _acf_settings_uploader( $uploader ) {
3700
3701 // if can't upload files
3702 if( !current_user_can('upload_files') ) {
3703
3704 $uploader = 'basic';
3705
3706 }
3707
3708
3709 // return
3710 return $uploader;
3711 }
3712
3713
3714 /*
3715 * acf_translate_keys
3716 *
3717 * description
3718 *
3719 * @type function
3720 * @date 7/12/2015
3721 * @since 5.3.2
3722 *
3723 * @param $post_id (int)
3724 * @return $post_id (int)
3725 */
3726
3727 /*
3728 function acf_translate_keys( $array, $keys ) {
3729
3730 // bail early if no keys
3731 if( empty($keys) ) return $array;
3732
3733
3734 // translate
3735 foreach( $keys as $k ) {
3736
3737 // bail ealry if not exists
3738 if( !isset($array[ $k ]) ) continue;
3739
3740
3741 // translate
3742 $array[ $k ] = acf_translate( $array[ $k ] );
3743
3744 }
3745
3746
3747 // return
3748 return $array;
3749
3750 }
3751 */
3752
3753
3754 /*
3755 * acf_translate
3756 *
3757 * This function will translate a string using the new 'l10n_textdomain' setting
3758 * Also works for arrays which is great for fields - select -> choices
3759 *
3760 * @type function
3761 * @date 4/12/2015
3762 * @since 5.3.2
3763 *
3764 * @param $string (mixed) string or array containins strings to be translated
3765 * @return $string
3766 */
3767
3768 function acf_translate( $string ) {
3769
3770 // vars
3771 $l10n = acf_get_setting('l10n');
3772 $textdomain = acf_get_setting('l10n_textdomain');
3773
3774
3775 // bail early if not enabled
3776 if( !$l10n ) return $string;
3777
3778
3779 // bail early if no textdomain
3780 if( !$textdomain ) return $string;
3781
3782
3783 // is array
3784 if( is_array($string) ) {
3785
3786 return array_map('acf_translate', $string);
3787
3788 }
3789
3790
3791 // bail early if not string
3792 if( !is_string($string) ) return $string;
3793
3794
3795 // bail early if empty
3796 if( $string === '' ) return $string;
3797
3798
3799 // allow for var_export export
3800 if( acf_get_setting('l10n_var_export') ){
3801
3802 // bail early if already translated
3803 if( substr($string, 0, 7) === '!!__(!!' ) return $string;
3804
3805
3806 // return
3807 return "!!__(!!'" . $string . "!!', !!'" . $textdomain . "!!')!!";
3808
3809 }
3810
3811
3812 // vars
3813 return __( $string, $textdomain );
3814
3815 }
3816
3817
3818 /*
3819 * acf_maybe_add_action
3820 *
3821 * This function will determine if the action has already run before adding / calling the function
3822 *
3823 * @type function
3824 * @date 13/01/2016
3825 * @since 5.3.2
3826 *
3827 * @param $post_id (int)
3828 * @return $post_id (int)
3829 */
3830
3831 function acf_maybe_add_action( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
3832
3833 // if action has already run, execute it
3834 // - if currently doing action, allow $tag to be added as per usual to allow $priority ordering needed for 3rd party asset compatibility
3835 if( did_action($tag) && !doing_action($tag) ) {
3836
3837 call_user_func( $function_to_add );
3838
3839 // if action has not yet run, add it
3840 } else {
3841
3842 add_action( $tag, $function_to_add, $priority, $accepted_args );
3843
3844 }
3845
3846 }
3847
3848
3849 /*
3850 * acf_is_row_collapsed
3851 *
3852 * This function will return true if the field's row is collapsed
3853 *
3854 * @type function
3855 * @date 2/03/2016
3856 * @since 5.3.2
3857 *
3858 * @param $post_id (int)
3859 * @return $post_id (int)
3860 */
3861
3862 function acf_is_row_collapsed( $field_key = '', $row_index = 0 ) {
3863
3864 // collapsed
3865 $collapsed = acf_get_user_setting('collapsed_' . $field_key, '');
3866
3867
3868 // cookie fallback ( version < 5.3.2 )
3869 if( $collapsed === '' ) {
3870
3871 $collapsed = acf_extract_var($_COOKIE, "acf_collapsed_{$field_key}", '');
3872 $collapsed = str_replace('|', ',', $collapsed);
3873
3874
3875 // update
3876 acf_update_user_setting( 'collapsed_' . $field_key, $collapsed );
3877
3878 }
3879
3880
3881 // explode
3882 $collapsed = explode(',', $collapsed);
3883 $collapsed = array_filter($collapsed, 'is_numeric');
3884
3885
3886 // collapsed class
3887 return in_array($row_index, $collapsed);
3888
3889 }
3890
3891
3892 /*
3893 * acf_get_attachment_image
3894 *
3895 * description
3896 *
3897 * @type function
3898 * @date 24/10/16
3899 * @since 5.5.0
3900 *
3901 * @param $post_id (int)
3902 * @return $post_id (int)
3903 */
3904
3905 function acf_get_attachment_image( $attachment_id = 0, $size = 'thumbnail' ) {
3906
3907 // vars
3908 $url = wp_get_attachment_image_src($attachment_id, 'thumbnail');
3909 $alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
3910
3911
3912 // bail early if no url
3913 if( !$url ) return '';
3914
3915
3916 // return
3917 $value = '<img src="' . $url . '" alt="' . $alt . '" />';
3918
3919 }
3920
3921
3922 /*
3923 * acf_get_post_thumbnail
3924 *
3925 * This function will return a thumbail image url for a given post
3926 *
3927 * @type function
3928 * @date 3/05/2016
3929 * @since 5.3.8
3930 *
3931 * @param $post (obj)
3932 * @param $size (mixed)
3933 * @return (string)
3934 */
3935
3936 function acf_get_post_thumbnail( $post = null, $size = 'thumbnail' ) {
3937
3938 // vars
3939 $data = array(
3940 'url' => '',
3941 'type' => '',
3942 'html' => ''
3943 );
3944
3945
3946 // post
3947 $post = get_post($post);
3948
3949
3950 // bail early if no post
3951 if( !$post ) return $data;
3952
3953
3954 // vars
3955 $thumb_id = $post->ID;
3956 $mime_type = acf_maybe_get(explode('/', $post->post_mime_type), 0);
3957
3958
3959 // attachment
3960 if( $post->post_type === 'attachment' ) {
3961
3962 // change $thumb_id
3963 if( $mime_type === 'audio' || $mime_type === 'video' ) {
3964
3965 $thumb_id = get_post_thumbnail_id($post->ID);
3966
3967 }
3968
3969 // post
3970 } else {
3971
3972 $thumb_id = get_post_thumbnail_id($post->ID);
3973
3974 }
3975
3976
3977 // try url
3978 $data['url'] = wp_get_attachment_image_src($thumb_id, $size);
3979 $data['url'] = acf_maybe_get($data['url'], 0);
3980
3981
3982 // default icon
3983 if( !$data['url'] && $post->post_type === 'attachment' ) {
3984
3985 $data['url'] = wp_mime_type_icon($post->ID);
3986 $data['type'] = 'icon';
3987
3988 }
3989
3990
3991 // html
3992 $data['html'] = '<img src="' . $data['url'] . '" alt="" />';
3993
3994
3995 // return
3996 return $data;
3997
3998 }
3999
4000 /**
4001 * acf_get_browser
4002 *
4003 * Returns the name of the current browser.
4004 *
4005 * @date 17/01/2014
4006 * @since 5.0.0
4007 *
4008 * @param void
4009 * @return string
4010 */
4011 function acf_get_browser() {
4012
4013 // Check server var.
4014 if( isset($_SERVER['HTTP_USER_AGENT']) ) {
4015 $agent = $_SERVER['HTTP_USER_AGENT'];
4016
4017 // Loop over search terms.
4018 $browsers = array(
4019 'Firefox' => 'firefox',
4020 'Trident' => 'msie',
4021 'MSIE' => 'msie',
4022 'Edge' => 'edge',
4023 'Chrome' => 'chrome',
4024 'Safari' => 'safari',
4025 );
4026 foreach( $browsers as $k => $v ) {
4027 if( strpos($agent, $k) !== false ) {
4028 return $v;
4029 }
4030 }
4031 }
4032
4033 // Return default.
4034 return '';
4035 }
4036
4037
4038 /*
4039 * acf_is_ajax
4040 *
4041 * This function will reutrn true if performing a wp ajax call
4042 *
4043 * @type function
4044 * @date 7/06/2016
4045 * @since 5.3.8
4046 *
4047 * @param n/a
4048 * @return (boolean)
4049 */
4050
4051 function acf_is_ajax( $action = '' ) {
4052
4053 // vars
4054 $is_ajax = false;
4055
4056
4057 // check if is doing ajax
4058 if( defined('DOING_AJAX') && DOING_AJAX ) {
4059
4060 $is_ajax = true;
4061
4062 }
4063
4064
4065 // check $action
4066 if( $action && acf_maybe_get($_POST, 'action') !== $action ) {
4067
4068 $is_ajax = false;
4069
4070 }
4071
4072
4073 // return
4074 return $is_ajax;
4075
4076 }
4077
4078
4079
4080
4081 /*
4082 * acf_format_date
4083 *
4084 * This function will accept a date value and return it in a formatted string
4085 *
4086 * @type function
4087 * @date 16/06/2016
4088 * @since 5.3.8
4089 *
4090 * @param $value (string)
4091 * @return $format (string)
4092 */
4093
4094 function acf_format_date( $value, $format ) {
4095
4096 // bail early if no value
4097 if( !$value ) return $value;
4098
4099
4100 // vars
4101 $unixtimestamp = 0;
4102
4103
4104 // numeric (either unix or YYYYMMDD)
4105 if( is_numeric($value) && strlen($value) !== 8 ) {
4106
4107 $unixtimestamp = $value;
4108
4109 } else {
4110
4111 $unixtimestamp = strtotime($value);
4112
4113 }
4114
4115
4116 // return
4117 return date_i18n($format, $unixtimestamp);
4118
4119 }
4120
4121 /**
4122 * acf_clear_log
4123 *
4124 * Deletes the debug.log file.
4125 *
4126 * @date 21/1/19
4127 * @since 5.7.10
4128 *
4129 * @param type $var Description. Default.
4130 * @return type Description.
4131 */
4132 function acf_clear_log() {
4133 unlink( WP_CONTENT_DIR . '/debug.log' );
4134 }
4135
4136 /*
4137 * acf_log
4138 *
4139 * description
4140 *
4141 * @type function
4142 * @date 24/06/2016
4143 * @since 5.3.8
4144 *
4145 * @param $post_id (int)
4146 * @return $post_id (int)
4147 */
4148
4149 function acf_log() {
4150
4151 // vars
4152 $args = func_get_args();
4153
4154 // loop
4155 foreach( $args as $i => $arg ) {
4156
4157 // array | object
4158 if( is_array($arg) || is_object($arg) ) {
4159 $arg = print_r($arg, true);
4160
4161 // bool
4162 } elseif( is_bool($arg) ) {
4163 $arg = 'bool(' . ( $arg ? 'true' : 'false' ) . ')';
4164 }
4165
4166 // update
4167 $args[ $i ] = $arg;
4168 }
4169
4170 // log
4171 error_log( implode(' ', $args) );
4172 }
4173
4174 /**
4175 * acf_dev_log
4176 *
4177 * Used to log variables only if ACF_DEV is defined
4178 *
4179 * @date 25/8/18
4180 * @since 5.7.4
4181 *
4182 * @param mixed
4183 * @return void
4184 */
4185 function acf_dev_log() {
4186 if( defined('ACF_DEV') && ACF_DEV ) {
4187 call_user_func_array('acf_log', func_get_args());
4188 }
4189 }
4190
4191 /*
4192 * acf_doing
4193 *
4194 * This function will tell ACF what task it is doing
4195 *
4196 * @type function
4197 * @date 28/06/2016
4198 * @since 5.3.8
4199 *
4200 * @param $event (string)
4201 * @param context (string)
4202 * @return n/a
4203 */
4204
4205 function acf_doing( $event = '', $context = '' ) {
4206
4207 acf_update_setting( 'doing', $event );
4208 acf_update_setting( 'doing_context', $context );
4209
4210 }
4211
4212
4213 /*
4214 * acf_is_doing
4215 *
4216 * This function can be used to state what ACF is doing, or to check
4217 *
4218 * @type function
4219 * @date 28/06/2016
4220 * @since 5.3.8
4221 *
4222 * @param $event (string)
4223 * @param context (string)
4224 * @return (boolean)
4225 */
4226
4227 function acf_is_doing( $event = '', $context = '' ) {
4228
4229 // vars
4230 $doing = false;
4231
4232
4233 // task
4234 if( acf_get_setting('doing') === $event ) {
4235
4236 $doing = true;
4237
4238 }
4239
4240
4241 // context
4242 if( $context && acf_get_setting('doing_context') !== $context ) {
4243
4244 $doing = false;
4245
4246 }
4247
4248
4249 // return
4250 return $doing;
4251
4252 }
4253
4254
4255 /*
4256 * acf_is_plugin_active
4257 *
4258 * This function will return true if the ACF plugin is active
4259 * - May be included within a theme or other plugin
4260 *
4261 * @type function
4262 * @date 13/07/2016
4263 * @since 5.4.0
4264 *
4265 * @param $basename (int)
4266 * @return $post_id (int)
4267 */
4268
4269
4270 function acf_is_plugin_active() {
4271
4272 // vars
4273 $basename = acf_get_setting('basename');
4274
4275
4276 // ensure is_plugin_active() exists (not on frontend)
4277 if( !function_exists('is_plugin_active') ) {
4278
4279 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
4280
4281 }
4282
4283
4284 // return
4285 return is_plugin_active($basename);
4286
4287 }
4288
4289 /*
4290 * acf_send_ajax_results
4291 *
4292 * This function will print JSON data for a Select2 AJAX query
4293 *
4294 * @type function
4295 * @date 19/07/2016
4296 * @since 5.4.0
4297 *
4298 * @param $response (array)
4299 * @return n/a
4300 */
4301
4302 function acf_send_ajax_results( $response ) {
4303
4304 // validate
4305 $response = wp_parse_args($response, array(
4306 'results' => array(),
4307 'more' => false,
4308 'limit' => 0
4309 ));
4310
4311
4312 // limit
4313 if( $response['limit'] && $response['results']) {
4314
4315 // vars
4316 $total = 0;
4317
4318 foreach( $response['results'] as $result ) {
4319
4320 // parent
4321 $total++;
4322
4323
4324 // children
4325 if( !empty($result['children']) ) {
4326
4327 $total += count( $result['children'] );
4328
4329 }
4330
4331 }
4332
4333
4334 // calc
4335 if( $total >= $response['limit'] ) {
4336
4337 $response['more'] = true;
4338
4339 }
4340
4341 }
4342
4343
4344 // return
4345 wp_send_json( $response );
4346
4347 }
4348
4349
4350 /*
4351 * acf_is_sequential_array
4352 *
4353 * This function will return true if the array contains only numeric keys
4354 *
4355 * @source http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential
4356 * @type function
4357 * @date 9/09/2016
4358 * @since 5.4.0
4359 *
4360 * @param $array (array)
4361 * @return (boolean)
4362 */
4363
4364 function acf_is_sequential_array( $array ) {
4365
4366 // bail ealry if not array
4367 if( !is_array($array) ) return false;
4368
4369
4370 // loop
4371 foreach( $array as $key => $value ) {
4372
4373 // bail ealry if is string
4374 if( is_string($key) ) return false;
4375
4376 }
4377
4378
4379 // return
4380 return true;
4381
4382 }
4383
4384
4385 /*
4386 * acf_is_associative_array
4387 *
4388 * This function will return true if the array contains one or more string keys
4389 *
4390 * @source http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential
4391 * @type function
4392 * @date 9/09/2016
4393 * @since 5.4.0
4394 *
4395 * @param $array (array)
4396 * @return (boolean)
4397 */
4398
4399 function acf_is_associative_array( $array ) {
4400
4401 // bail ealry if not array
4402 if( !is_array($array) ) return false;
4403
4404
4405 // loop
4406 foreach( $array as $key => $value ) {
4407
4408 // bail ealry if is string
4409 if( is_string($key) ) return true;
4410
4411 }
4412
4413
4414 // return
4415 return false;
4416
4417 }
4418
4419
4420 /*
4421 * acf_add_array_key_prefix
4422 *
4423 * This function will add a prefix to all array keys
4424 * Useful to preserve numeric keys when performing array_multisort
4425 *
4426 * @type function
4427 * @date 15/09/2016
4428 * @since 5.4.0
4429 *
4430 * @param $array (array)
4431 * @param $prefix (string)
4432 * @return (array)
4433 */
4434
4435 function acf_add_array_key_prefix( $array, $prefix ) {
4436
4437 // vars
4438 $array2 = array();
4439
4440
4441 // loop
4442 foreach( $array as $k => $v ) {
4443
4444 $k2 = $prefix . $k;
4445 $array2[ $k2 ] = $v;
4446
4447 }
4448
4449
4450 // return
4451 return $array2;
4452
4453 }
4454
4455
4456 /*
4457 * acf_remove_array_key_prefix
4458 *
4459 * This function will remove a prefix to all array keys
4460 * Useful to preserve numeric keys when performing array_multisort
4461 *
4462 * @type function
4463 * @date 15/09/2016
4464 * @since 5.4.0
4465 *
4466 * @param $array (array)
4467 * @param $prefix (string)
4468 * @return (array)
4469 */
4470
4471 function acf_remove_array_key_prefix( $array, $prefix ) {
4472
4473 // vars
4474 $array2 = array();
4475 $l = strlen($prefix);
4476
4477
4478 // loop
4479 foreach( $array as $k => $v ) {
4480
4481 $k2 = (substr($k, 0, $l) === $prefix) ? substr($k, $l) : $k;
4482 $array2[ $k2 ] = $v;
4483
4484 }
4485
4486
4487 // return
4488 return $array2;
4489
4490 }
4491
4492
4493 /*
4494 * acf_strip_protocol
4495 *
4496 * This function will remove the proticol from a url
4497 * Used to allow licences to remain active if a site is switched to https
4498 *
4499 * @type function
4500 * @date 10/01/2017
4501 * @since 5.5.4
4502 * @author Aaron
4503 *
4504 * @param $url (string)
4505 * @return (string)
4506 */
4507
4508 function acf_strip_protocol( $url ) {
4509
4510 // strip the protical
4511 return str_replace(array('http://','https://'), '', $url);
4512
4513 }
4514
4515
4516 /*
4517 * acf_connect_attachment_to_post
4518 *
4519 * This function will connect an attacment (image etc) to the post
4520 * Used to connect attachements uploaded directly to media that have not been attaced to a post
4521 *
4522 * @type function
4523 * @date 11/01/2017
4524 * @since 5.8.0 Added filter to prevent connection.
4525 * @since 5.5.4
4526 *
4527 * @param int $attachment_id The attachment ID.
4528 * @param int $post_id The post ID.
4529 * @return bool True if attachment was connected.
4530 */
4531 function acf_connect_attachment_to_post( $attachment_id = 0, $post_id = 0 ) {
4532
4533 // Bail ealry if $attachment_id is not valid.
4534 if( !$attachment_id || !is_numeric($attachment_id) ) {
4535 return false;
4536 }
4537
4538 // Bail ealry if $post_id is not valid.
4539 if( !$post_id || !is_numeric($post_id) ) {
4540 return false;
4541 }
4542
4543 /**
4544 * Filters whether or not to connect the attachment.
4545 *
4546 * @date 8/11/18
4547 * @since 5.8.0
4548 *
4549 * @param bool $bool Returning false will prevent the connection. Default true.
4550 * @param int $attachment_id The attachment ID.
4551 * @param int $post_id The post ID.
4552 */
4553 if( !apply_filters('acf/connect_attachment_to_post', true, $attachment_id, $post_id) ) {
4554 return false;
4555 }
4556
4557 // vars
4558 $post = get_post( $attachment_id );
4559
4560 // Check if is valid post.
4561 if( $post && $post->post_type == 'attachment' && $post->post_parent == 0 ) {
4562
4563 // update
4564 wp_update_post( array('ID' => $post->ID, 'post_parent' => $post_id) );
4565
4566 // return
4567 return true;
4568 }
4569
4570 // return
4571 return true;
4572 }
4573
4574
4575 /*
4576 * acf_encrypt
4577 *
4578 * This function will encrypt a string using PHP
4579 * https://bhoover.com/using-php-openssl_encrypt-openssl_decrypt-encrypt-decrypt-data/
4580 *
4581 * @type function
4582 * @date 27/2/17
4583 * @since 5.5.8
4584 *
4585 * @param $data (string)
4586 * @return (string)
4587 */
4588
4589
4590 function acf_encrypt( $data = '' ) {
4591
4592 // bail ealry if no encrypt function
4593 if( !function_exists('openssl_encrypt') ) return base64_encode($data);
4594
4595
4596 // generate a key
4597 $key = wp_hash('acf_encrypt');
4598
4599
4600 // Generate an initialization vector
4601 $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
4602
4603
4604 // Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
4605 $encrypted_data = openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv);
4606
4607
4608 // The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
4609 return base64_encode($encrypted_data . '::' . $iv);
4610
4611 }
4612
4613
4614 /*
4615 * acf_decrypt
4616 *
4617 * This function will decrypt an encrypted string using PHP
4618 * https://bhoover.com/using-php-openssl_encrypt-openssl_decrypt-encrypt-decrypt-data/
4619 *
4620 * @type function
4621 * @date 27/2/17
4622 * @since 5.5.8
4623 *
4624 * @param $data (string)
4625 * @return (string)
4626 */
4627
4628 function acf_decrypt( $data = '' ) {
4629
4630 // bail ealry if no decrypt function
4631 if( !function_exists('openssl_decrypt') ) return base64_decode($data);
4632
4633
4634 // generate a key
4635 $key = wp_hash('acf_encrypt');
4636
4637
4638 // To decrypt, split the encrypted data from our IV - our unique separator used was "::"
4639 list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
4640
4641
4642 // decrypt
4643 return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
4644
4645 }
4646
4647 /**
4648 * acf_parse_markdown
4649 *
4650 * A very basic regex-based Markdown parser function based off [slimdown](https://gist.github.com/jbroadway/2836900).
4651 *
4652 * @date 6/8/18
4653 * @since 5.7.2
4654 *
4655 * @param string $text The string to parse.
4656 * @return string
4657 */
4658
4659 function acf_parse_markdown( $text = '' ) {
4660
4661 // trim
4662 $text = trim($text);
4663
4664 // rules
4665 $rules = array (
4666 '/=== (.+?) ===/' => '<h2>$1</h2>', // headings
4667 '/== (.+?) ==/' => '<h3>$1</h3>', // headings
4668 '/= (.+?) =/' => '<h4>$1</h4>', // headings
4669 '/\[([^\[]+)\]\(([^\)]+)\)/' => '<a href="$2">$1</a>', // links
4670 '/(\*\*)(.*?)\1/' => '<strong>$2</strong>', // bold
4671 '/(\*)(.*?)\1/' => '<em>$2</em>', // intalic
4672 '/`(.*?)`/' => '<code>$1</code>', // inline code
4673 '/\n\*(.*)/' => "\n<ul>\n\t<li>$1</li>\n</ul>", // ul lists
4674 '/\n[0-9]+\.(.*)/' => "\n<ol>\n\t<li>$1</li>\n</ol>", // ol lists
4675 '/<\/ul>\s?<ul>/' => '', // fix extra ul
4676 '/<\/ol>\s?<ol>/' => '', // fix extra ol
4677 );
4678 foreach( $rules as $k => $v ) {
4679 $text = preg_replace($k, $v, $text);
4680 }
4681
4682 // autop
4683 $text = wpautop($text);
4684
4685 // return
4686 return $text;
4687 }
4688
4689 /**
4690 * acf_get_sites
4691 *
4692 * Returns an array of sites for a network.
4693 *
4694 * @date 29/08/2016
4695 * @since 5.4.0
4696 *
4697 * @param void
4698 * @return array
4699 */
4700 function acf_get_sites() {
4701 $results = array();
4702 $sites = get_sites( array( 'number' => 0 ) );
4703 if( $sites ) {
4704 foreach( $sites as $site ) {
4705 $results[] = get_site( $site )->to_array();
4706 }
4707 }
4708 return $results;
4709 }
4710
4711 /**
4712 * acf_convert_rules_to_groups
4713 *
4714 * Converts an array of rules from ACF4 to an array of groups for ACF5
4715 *
4716 * @date 25/8/18
4717 * @since 5.7.4
4718 *
4719 * @param array $rules An array of rules.
4720 * @param string $anyorall The anyorall setting used in ACF4. Defaults to 'any'.
4721 * @return array
4722 */
4723 function acf_convert_rules_to_groups( $rules, $anyorall = 'any' ) {
4724
4725 // vars
4726 $groups = array();
4727 $index = 0;
4728
4729 // loop
4730 foreach( $rules as $rule ) {
4731
4732 // extract vars
4733 $group = acf_extract_var( $rule, 'group_no' );
4734 $order = acf_extract_var( $rule, 'order_no' );
4735
4736 // calculate group if not defined
4737 if( $group === null ) {
4738 $group = $index;
4739
4740 // use $anyorall to determine if a new group is needed
4741 if( $anyorall == 'any' ) {
4742 $index++;
4743 }
4744 }
4745
4746 // calculate order if not defined
4747 if( $order === null ) {
4748 $order = isset($groups[ $group ]) ? count($groups[ $group ]) : 0;
4749 }
4750
4751 // append to group
4752 $groups[ $group ][ $order ] = $rule;
4753
4754 // sort groups
4755 ksort( $groups[ $group ] );
4756 }
4757
4758 // sort groups
4759 ksort( $groups );
4760
4761 // return
4762 return $groups;
4763 }
4764
4765 /**
4766 * acf_register_ajax
4767 *
4768 * Regsiters an ajax callback.
4769 *
4770 * @date 5/10/18
4771 * @since 5.7.7
4772 *
4773 * @param string $name The ajax action name.
4774 * @param array $callback The callback function or array.
4775 * @param bool $public Whether to allow access to non logged in users.
4776 * @return void
4777 */
4778 function acf_register_ajax( $name = '', $callback = false, $public = false ) {
4779
4780 // vars
4781 $action = "acf/ajax/$name";
4782
4783 // add action for logged-in users
4784 add_action( "wp_ajax_$action", $callback );
4785
4786 // add action for non logged-in users
4787 if( $public ) {
4788 add_action( "wp_ajax_nopriv_$action", $callback );
4789 }
4790 }
4791
4792 /**
4793 * acf_str_camel_case
4794 *
4795 * Converts a string into camelCase.
4796 * Thanks to https://stackoverflow.com/questions/31274782/convert-array-keys-from-underscore-case-to-camelcase-recursively
4797 *
4798 * @date 24/10/18
4799 * @since 5.8.0
4800 *
4801 * @param string $string The string ot convert.
4802 * @return string
4803 */
4804 function acf_str_camel_case( $string = '' ) {
4805 return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $string))));
4806 }
4807
4808 /**
4809 * acf_array_camel_case
4810 *
4811 * Converts all aray keys to camelCase.
4812 *
4813 * @date 24/10/18
4814 * @since 5.8.0
4815 *
4816 * @param array $array The array to convert.
4817 * @return array
4818 */
4819 function acf_array_camel_case( $array = array() ) {
4820 $array2 = array();
4821 foreach( $array as $k => $v ) {
4822 $array2[ acf_str_camel_case($k) ] = $v;
4823 }
4824 return $array2;
4825 }
4826
4827 /**
4828 * acf_is_block_editor
4829 *
4830 * Returns true if the current screen uses the block editor.
4831 *
4832 * @date 13/12/18
4833 * @since 5.8.0
4834 *
4835 * @param void
4836 * @return bool
4837 */
4838 function acf_is_block_editor() {
4839 if( function_exists('get_current_screen') ) {
4840 $screen = get_current_screen();
4841 if( method_exists($screen, 'is_block_editor') ) {
4842 return $screen->is_block_editor();
4843 }
4844 }
4845 return false;
4846 }
4847
4848 ?>