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