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