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