├── test.js ├── Untitled-1.png ├── default-image-for-image-field.php ├── change-option-page-location-display.php ├── acf-delete-images-when-removed-from-gallery.php ├── public-taxonomy-location-rule.php ├── is_admin-acf-location-rule.php ├── public-post-type-location-rule.php ├── render-image-in-editor.php ├── acf-form-kses.php ├── unique-repeater-sub-field.php ├── acf-extended-admin-columns.php ├── acf-page-ancestor-location-rule.php ├── correct-number-field-mouse-scrollwheel-action.php ├── acf-load-parent-theme-field-groups.php ├── acf-page-granparent-location-rule.php ├── acf-custom-post-type-filters.php ├── acf-post-category-ancestor-location-rule.php ├── page-nth-level-location-rule.php ├── acf-json-save-based-on-group-key.php ├── customized-options-page.php ├── acf-options-page-w-cpt-children.php ├── acf-image-aspect-ratio-validation.php ├── acf-field-label-functions.php ├── acf-reciprocal-relationship.php ├── acf-reciprocal-relationships-multiple.php └── README.md /test.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Untitled-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hube2/acf-filters-and-functions/master/Untitled-1.png -------------------------------------------------------------------------------- /default-image-for-image-field.php: -------------------------------------------------------------------------------- 1 | 'Default Image', 11 | 'instructions' => 'Appears when creating a new post', 12 | 'type' => 'image', 13 | 'name' => 'default_value', 14 | )); 15 | } 16 | 17 | ?> 18 | -------------------------------------------------------------------------------- /change-option-page-location-display.php: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /acf-delete-images-when-removed-from-gallery.php: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /public-taxonomy-location-rule.php: -------------------------------------------------------------------------------- 1 | public; 29 | if ($rule['operator'] == '==') { 30 | $match = $public; 31 | } elseif ($rule['operator'] == '!=') { 32 | $match = !$public; 33 | } 34 | return $match; 35 | } 36 | 37 | ?> 38 | -------------------------------------------------------------------------------- /is_admin-acf-location-rule.php: -------------------------------------------------------------------------------- 1 | 41 | -------------------------------------------------------------------------------- /public-post-type-location-rule.php: -------------------------------------------------------------------------------- 1 | public; 36 | } elseif ($rule['operator'] == '!=') { 37 | $match = !$post_type->public; 38 | } 39 | return $match; 40 | } 41 | 42 | ?> 43 | -------------------------------------------------------------------------------- /render-image-in-editor.php: -------------------------------------------------------------------------------- 1 | '; print_r($field); echo ''; 23 | // get the post id 24 | global $post; 25 | $post_id = $post->ID; 26 | // get the current value of the field 27 | // using get_post_meta to avoid confilcts 28 | $url = get_post_meta($post_id, $field_name, true); 29 | if (!$url) { 30 | // nothing has been entered 31 | ?>

Enter a URL and Update to View.

The URL Entered is Not A Valid Image URL. Enter a Valid Image URL and Update to View.

43 | 44 | 48 | -------------------------------------------------------------------------------- /acf-form-kses.php: -------------------------------------------------------------------------------- 1 | $value) { 17 | $return[$index] = acf_wp_kses_post($value); 18 | } 19 | } 20 | return $return; 21 | } 22 | add_filter('acf/update_value', 'acf_wp_kses_post', 10, 3) 23 | /* 24 | there is another way to do it posted by the author of ACF that can be 25 | found here http://www.advancedcustomfields.com/resources/acf_form/#security 26 | I'll keep this one here as an alternate 27 | 28 | After doing some additional testing, I would continue to use this over the 29 | array_map() example. The reason being that array_map is not a recursive function 30 | in other words if you have nested repeaters (or flex fields) then array_map() 31 | will fail on these inputs. This filters also has the benefit of not applying the 32 | values to the repeaters in the first place since all of the sub_fields of that 33 | repeater will call this function anyway and checking for the repeater fields 34 | prevents these fields from being processed twice. 35 | 36 | This technique could also be used to apply any function safely to all ACF field types. 37 | Just replace wp_kses_post with the function you want to use 38 | 39 | */ 40 | ?> 41 | -------------------------------------------------------------------------------- /unique-repeater-sub-field.php: -------------------------------------------------------------------------------- 1 | /* 2 | This filter can be used to ensure the unuqueness of a repeater sub field value 3 | */ 4 | 5 | // example add_filter, can use any valid repeater sub field key 6 | add_filter('acf/validate_value/key=field_59dd1ad221414', 'unique_repeater_subfield', 20, 4); 7 | 8 | function unique_repeater_subfield($valid, $value, $field, $input) { 9 | if (!$valid) { 10 | return $valid; 11 | } 12 | 13 | // get list of array indexes from $input 14 | // [ <= this fixes my IDE, it has problems with unmatched brackets in the regex 15 | preg_match_all('/\[([^\]]+)\]/', $input, $matches); 16 | if (!count($matches[1])) { 17 | // this should actually never happen 18 | return $valid; 19 | } 20 | // only need the index list captured 21 | $matches = $matches[1]; 22 | 23 | // walk the acf input to find the repeater and current row 24 | $array = $_POST['acf']; 25 | 26 | $repeater_key = false; 27 | $repeater_value = false; 28 | $row_key = false; 29 | $row_value = false; 30 | $field_key = false; 31 | $field_value = false; 32 | 33 | for ($i=0; $i $row) { 57 | if ($index != $row_key && strtolower($row[$field_key]) == strtolower($value)) { 58 | $valid = 'this value is not unique'; 59 | break; 60 | } 61 | } 62 | 63 | return $valid; 64 | } // end public function unique_repeater_subfield 65 | -------------------------------------------------------------------------------- /acf-extended-admin-columns.php: -------------------------------------------------------------------------------- 1 | menu_order; 15 | break; 16 | case 'location': 17 | $details = unserialize($post->post_content); 18 | $location = $details['location']; 19 | if (is_array($location) && count($location)) { 20 | echo '
';
21 | 					$or_count = 1;
22 | 					foreach ($location as $or) {
23 | 						$and_count = 1;
24 | 						foreach ($or as $and) {
25 | 							if ($and_count == 1) {
26 | 								echo '    ';
27 | 							}
28 | 							echo $and['param'],' ',$and['operator'],' ',$and['value'];
29 | 							if ($and_count < count($or)) {
30 | 								echo "\r\n",'AND ';
31 | 							}
32 | 							$and_count++;
33 | 						}
34 | 						if ($or_count < count($location)) {
35 | 							echo "\r\n\r\n",'OR',"\r\n";
36 | 						}
37 | 						$or_count++;
38 | 					}
39 | 					echo '
'; 40 | } 41 | break; 42 | } // end switch 43 | } 44 | add_action('manage_acf-field-group_posts_custom_column', 'acf_field_group_columns_content', 10, 2 ); 45 | 46 | // some css to pretty up the new columns 47 | add_action('admin_head', 'admin_acf_columns_css'); 48 | function admin_acf_columns_css() { 49 | ?> 50 | 66 | 70 | -------------------------------------------------------------------------------- /acf-page-ancestor-location-rule.php: -------------------------------------------------------------------------------- 1 | 'page' 18 | )); 19 | if (!empty($groups)) { 20 | foreach(array_keys($groups) as $group_title) { 21 | $posts = acf_extract_var($groups, $group_title); 22 | foreach(array_keys($posts) as $post_id) { 23 | $posts[$post_id] = acf_get_post_title($posts[$post_id]); 24 | }; 25 | $choices = $posts; 26 | } 27 | } 28 | // end of copy from ACF 29 | return $choices; 30 | } 31 | 32 | add_filter('acf/location/rule_match/page_ancestor', 'acf_location_rules_match_page_ancestor', 10, 3); 33 | function acf_location_rules_match_page_ancestor($match, $rule, $options) { 34 | // this code is with inspiration from 35 | // acf_location::rule_match_page_parent() 36 | // check parents recursively to see if any 37 | // matches the location value 38 | if (isset($options['page_parent']) && $options['page_parent']) { 39 | $page_parent = $options['page_parent']; 40 | unset($options['page_parent']); 41 | } elseif (isset($options['post_id']) && $options['post_id']) { 42 | $post = get_post($options['post_id']); 43 | $page_parent = $post->post_parent; 44 | } 45 | $ancestors = array(); 46 | if ($page_parent) { 47 | $ancestors = get_ancestors($page_parent, 'page'); 48 | $ancestors[] = $page_parent; 49 | } 50 | if ($rule['operator'] == "==") { 51 | $match = in_array($rule['value'], $ancestors); 52 | } elseif ($rule['operator'] == "!=") { 53 | $match = !in_array($rule['value'], $ancestors); 54 | } 55 | return $match; 56 | } 57 | 58 | ?> 59 | -------------------------------------------------------------------------------- /correct-number-field-mouse-scrollwheel-action.php: -------------------------------------------------------------------------------- 1 | /* 2 | This function can be added to functions.php in order to correct the mouse scroll wheel action for number fields. 3 | 4 | This is not just an ACF issue, this is a browser issue. 5 | Whenever a number field has focus, scrolling the mousewheel will change the value of a field, 6 | even when this is not the desired effect, for example what you really want to do is scroll 7 | the page but forgot to click off of the number field to remove focus before trying to do so. 8 | This is, in my opinion and that of others, to be incorrect behavior. 9 | 10 | This script will only allow the scrollwheel to alter the field value when 11 | 1) The number field has focus 12 | AND 13 | 2) The mouse is actually over the field 14 | 15 | This is given as a WordPress action, but with a little modification this concept could be used anywhere. 16 | 17 | */ 18 | 19 | add_action('admin_footer', 'correct_number_scrollwheel'); 20 | function correct_number_scrollwheel() { 21 | ?> 22 | 46 | get_acf_field_groups(); 34 | foreach ($files as $file) { 35 | $file_path = $path.'/'.$file; 36 | if (is_dir($file_path) || !preg_match('/\.json$/', $file)) { 37 | continue; 38 | } 39 | $group_key = preg_replace('/\.json$/', '', $file); 40 | if (!isset($groups[$group_key]) && 41 | ($json = file_get_contents($file_path)) !== false && 42 | ($field_group = json_decode($json, true)) !== NULL) { 43 | acf_add_local_field_group($field_group); 44 | } 45 | } 46 | // need to delete the ACF cache 47 | wp_cache_delete('get_field_groups', 'acf'); 48 | } // end public function include_fields 49 | 50 | private function get_acf_field_groups() { 51 | $groups = array(); 52 | $acf_groups = acf_get_field_groups(); 53 | if (!count($acf_groups)) { 54 | return; 55 | } 56 | foreach ($acf_groups as $group) { 57 | $groups[$group['key']] = $group['key']; 58 | } 59 | return $groups; 60 | } // end private function get_acf_field_groups 61 | 62 | } // end class acf_load_theme_groups 63 | 64 | ?> 65 | -------------------------------------------------------------------------------- /acf-page-granparent-location-rule.php: -------------------------------------------------------------------------------- 1 | 'page' 20 | )); 21 | if (!empty($groups)) { 22 | foreach(array_keys($groups) as $group_title) { 23 | $posts = acf_extract_var($groups, $group_title); 24 | foreach(array_keys($posts) as $post_id) { 25 | $posts[$post_id] = acf_get_post_title($posts[$post_id]); 26 | }; 27 | $choices = $posts; 28 | } 29 | } 30 | // end of copy from ACF 31 | return $choices; 32 | } 33 | 34 | add_filter('acf/location/rule_match/page_grandparent', 'acf_location_rules_match_page_grandparent', 10, 3); 35 | function acf_location_rules_match_page_grandparent($match, $rule, $options) { 36 | // this code is with inspiration from 37 | // acf_location::rule_match_page_parent() 38 | // with addition of adding grandparent check 39 | $post_grandparent = 0; 40 | if (isset($options['page_parent']) && $options['page_parent']) { 41 | $parent = get_post($options['page_parent']); 42 | if ($parent->post_parent) { 43 | $post_grandparent = $parent->post_parent; 44 | } 45 | } elseif (isset($options['post_id']) && $options['post_id']) { 46 | $post = get_post($options['post_id']); 47 | if ($post->post_parent) { 48 | $parent = get_post($post->post_parent); 49 | if ($parent->post_parent) { 50 | $post_grandparent = $parent->post_parent; 51 | } 52 | } 53 | } 54 | if (!$post_grandparent) { 55 | return false; 56 | } 57 | if ($rule['operator'] == "==") { 58 | $match = ($post_grandparent == $rule['value']); 59 | } elseif ($rule['operator'] == "!=") { 60 | $match = ($post_grandparent != $rule['value']); 61 | } 62 | return $match; 63 | } 64 | 65 | ?> 66 | -------------------------------------------------------------------------------- /acf-custom-post-type-filters.php: -------------------------------------------------------------------------------- 1 | $value) { 18 | $new_choices[$key] = $value; 19 | if ($key == 'Page') { 20 | $new_choices['My Custom Post Type'] = array(); 21 | } 22 | } // end foreach choices 23 | $choices = $new_choices; 24 | } // end if not in choices 25 | if (!isset($choices['My Custom Post Type']['post'])) { 26 | $choices['My Custom Post Type']['my_custom_post_type_post'] = 'My Custom Post Type Post'; 27 | } 28 | return $choices; 29 | } 30 | 31 | // add choices 32 | add_filter('acf/location/rule_values/my_custom_post_type_post', 'acf_location_rules_values_my_custom_post_type'); 33 | function acf_location_rules_values_my_custom_post_type($choices) { 34 | // adjust the for loop to the number of levels you need 35 | $args = array( 36 | 'post_type' => 'my_custom_post_type', 37 | 'post_status' => 'publish', 38 | 'posts_per_page' => -1, 39 | 'orderby' => array('title' => 'ASC', 'date' => 'DESC'), 40 | ); 41 | $query = new WP_Query($args); 42 | //echo '
'; print_r($query->posts); echo '
'; 43 | if (count($query->posts)) { 44 | foreach ($query->posts as $post) { 45 | $choices[$post->ID] = $post->post_title; 46 | } 47 | } 48 | return $choices; 49 | } 50 | 51 | // use the standard post matching 52 | // this is copied directly form ACF 53 | add_filter('acf/location/rule_match/my_custom_post_type_post', 'acf_location_rule_match_my_custom_post_type_post', 10, 3); 54 | function acf_location_rule_match_my_custom_post_type_post($match, $rule, $options) { 55 | $post_id = $options['post_id']; 56 | if( !$post_id ) { 57 | return false; 58 | } 59 | if ($rule['operator'] == "==") { 60 | $match = ($options['post_id'] == $rule['value']); 61 | } elseif ($rule['operator'] == "!=") { 62 | $match = ($options['post_id'] != $rule['value']); 63 | } 64 | return $match; 65 | } 66 | 67 | ?> 68 | -------------------------------------------------------------------------------- /acf-post-category-ancestor-location-rule.php: -------------------------------------------------------------------------------- 1 | $term_id) { 37 | $terms[$index] = get_term_by('id', intval($term_id), $term->taxonomy); 38 | } 39 | } 40 | if (!is_array($terms) && $options['post_id']) { 41 | $terms = wp_get_post_terms(intval($options['post_id']), $term->taxonomy); 42 | } 43 | if (!is_array($terms)) { 44 | $terms = array($terms); 45 | } 46 | $terms = array_filter($terms); 47 | $match = false; 48 | // collect a list of ancestors 49 | $ancestors = array(); 50 | if (count($terms)) { 51 | foreach ($terms as $term_to_check) { 52 | $ancestors = array_merge(get_ancestors($term_to_check->term_id, $term->taxonomy)); 53 | } // end foreach terms 54 | } // end if 55 | // see if the rule matches any term ancetor 56 | if ($term && in_array($term->term_id, $ancestors)) { 57 | $match = true; 58 | } 59 | 60 | if ($rule['operator'] == '!=') { 61 | // reverse the result 62 | $match = !$match; 63 | } 64 | return $match; 65 | } 66 | ?> 67 | -------------------------------------------------------------------------------- /page-nth-level-location-rule.php: -------------------------------------------------------------------------------- 1 | name = 'page_level'; 16 | $this->label = 'Page (Post) Level'; 17 | $this->category = 'page'; 18 | } // end function initialize 19 | 20 | static function get_operators($rule) { 21 | $operators = array( 22 | '!=' => 'is not equal to', 23 | '<' => 'is less than', 24 | '<=' => 'is less than or equal to', 25 | '==' => 'is equal to', 26 | '>=' => 'is greater and or equal to', 27 | '>' => 'is greater than' 28 | ); 29 | return $operators; 30 | } // end static function get_operators 31 | 32 | public function get_values($rule) { 33 | // value indicates number of ancestors 34 | $value_add = array( 35 | ' (Parent)', 36 | ' (Child)', 37 | ' (Grandchild)' 38 | ); 39 | $values = array(); 40 | for ($i=0; $i<10; $i++) { 41 | $values[$i] = strval($i+1); 42 | if (isset($value_add[$i])) { 43 | $values[$i] .= $value_add[$i]; 44 | } 45 | } // end for 46 | return $values; 47 | } // end public function get_values 48 | 49 | public function match($rule, $screen, $field_group) { 50 | $match = false; 51 | if (!isset($screen['post_id'])) { 52 | return $match; 53 | } 54 | if (!isset($screen['page_parent'])) { 55 | $ancestors = count(get_ancestors($screen['post_id'], $screen['post_type'], 'post_type')); 56 | } else { 57 | $ancestors = count(get_ancestors($screen['page_parent'], $screen['post_type'], 'post_type'))+1; 58 | } 59 | switch ($rule['operator']) { 60 | case '!=': 61 | $match = ($rule['value'] != $ancestors); 62 | break; 63 | case '<': 64 | $match = ($rule['value'] < $ancestors); 65 | break; 66 | case '<=': 67 | $match = ($rule['value'] <= $ancestors); 68 | break; 69 | case '==': 70 | $match = ($rule['value'] == $ancestors); 71 | break; 72 | case '>=': 73 | $match = ($rule['value'] >= $ancestors); 74 | break; 75 | case '>': 76 | $match = ($rule['value'] > $ancestors); 77 | break; 78 | default: 79 | // do nothing 80 | break; 81 | } // end switch 82 | return $match; 83 | } // end public function match 84 | 85 | } // end class location_page_level_jh 86 | 87 | acf_register_location_type('location_page_level_jh'); 88 | 89 | } // end if class exists 90 | -------------------------------------------------------------------------------- /acf-json-save-based-on-group-key.php: -------------------------------------------------------------------------------- 1 | path pairs 19 | // these will be set later 20 | private $groups = array(); 21 | 22 | // this variable will store the current group key 23 | // that is being saved so that we can retrieve it later 24 | private $current_group_being_saved; 25 | 26 | public function __construct() { 27 | 28 | // this init action will set up the save paths 29 | add_action('admin_init', array($this, 'admin_init')); 30 | 31 | // this action is called by ACF before saving a field group 32 | // the priority is set to 1 so that it runs before the internal ACF action 33 | add_action('acf/update_field_group', array($this, 'update_field_group'), 1, 1); 34 | 35 | } // end public function __construct 36 | 37 | public function admin_init() { 38 | 39 | // in this function we set up the paths where we want to store JSON files 40 | // in this example we're creating two folders in the theme header and footer 41 | // change the field groups and keys based on your groups 42 | $footer = get_stylesheet_directory().'/modules/footer'; 43 | $header = get_stylesheet_directory().'/modules/header'; 44 | $this->groups = array( 45 | 'group_584d5b7986f02' => $header, 46 | 'group_584d5b7986f03' => $footer 47 | ); 48 | 49 | } // end public function admin_init 50 | 51 | public function update_field_group($group) { 52 | // the purpose of this function is to see if we want to 53 | // change the location where this group is saved 54 | // and if we to to add a filter to alter the save path 55 | 56 | // first check to see if this is one of our groups 57 | if (!isset($this->groups[$group['key']])) { 58 | // not one or our groups 59 | return $group; 60 | } 61 | 62 | // store the group key and add action 63 | $this->current_group_being_saved = $group['key']; 64 | add_action('acf/settings/save_json', array($this, 'override_json_location'), 9999); 65 | 66 | // don't forget to return the groups 67 | return $group; 68 | 69 | } // end public function update_field_group 70 | 71 | public function override_json_location($path) { 72 | 73 | // alter the path based on group being saved and 74 | // our save locations 75 | $path = $this->groups[$this->current_group_being_saved]; 76 | 77 | return $path; 78 | 79 | } // end public function override_json_location 80 | 81 | } // end class acf_save_json_based_on_group_key 82 | 83 | 84 | ?> 85 | -------------------------------------------------------------------------------- /customized-options-page.php: -------------------------------------------------------------------------------- 1 |
',current_filter(),'

'; 34 | This will output the correct hook to use for your actions for the options page 35 | don't forget to remove the test code 36 | 37 | */ 38 | 39 | /* 40 | create an action for your options page that will run before the ACF callback function 41 | see above for information on the hook you need to use 42 | */ 43 | add_action('toplevel_page_YOUR-PAGE-SLUG', 'before_acf_options_page', 1); 44 | function before_acf_options_page() { 45 | /* 46 | Before ACF outputs the options page content 47 | start an object buffer so that we can capture the output 48 | */ 49 | ob_start(); 50 | } 51 | 52 | /* 53 | create an action for your options page that will run after the ACF callback function 54 | see above for information on the hook you need to use 55 | */ 56 | add_action('toplevel_page_YOUR-PAGE-SLUG', 'after_acf_options_page', 20); 57 | function after_acf_options_page() { 58 | /* 59 | After ACF finishes get the output and modify it 60 | */ 61 | $content = ob_get_clean(); 62 | 63 | $count = 1; // the number of times we should replace any string 64 | 65 | // insert something before the

66 | $my_content = '

This will be inserted before the <h1>

'; 67 | $content = str_replace(' 70 | $my_content = '

This will be inserted after the <h1>

'; 71 | $content = str_replace('

', ''.$my_content, $content, $count); 72 | 73 | // insert something after the form 74 | $my_content = '

This will be inserted after the form

'; 75 | $content = str_replace('', ''.$my_content, $content, $count); 76 | 77 | // output the new content 78 | echo $content; 79 | } 80 | 81 | ?> 82 | -------------------------------------------------------------------------------- /acf-options-page-w-cpt-children.php: -------------------------------------------------------------------------------- 1 | 9 10 | See this note on https://developer.wordpress.org/reference/functions/register_post_type/#show_in_menu 11 | for the "show_in_menu" argument: 12 | 13 | Note: When using 'some string' to show as a submenu of a menu page created by a 14 | plugin, this item will become the first submenu item, and replace the location of 15 | the top-level link. If this isn't desired, the plugin that creates the menu page 16 | needs to set the add_action priority for admin_menu to 9 or lower. 17 | 18 | The Solution: 19 | Add the options page twice, once with ACF and once with the standard WP function add_menu_page() 20 | 21 | But this causes another problem, the menu will be duplicated. 22 | The solution to this is to remove the duplicate 23 | 24 | You should rename these funtions 25 | 26 | */ 27 | 28 | // add an ACF Options Page 29 | add_action('init', 'add_a_test_acf_options_page'); 30 | function add_a_test_acf_options_page() { 31 | if (!function_exists('acf_add_options_page')) { 32 | return; 33 | } 34 | // all of these arguments are identical to the arguments 35 | // used to create in the function add_menu_page() 36 | $args = array( 37 | 'page_title' => 'Test Options Page', 38 | 'menu_title' => 'Test Options Page', 39 | // set the page slug, do not let it be generated 40 | // or you may not be able to find it to remove 41 | 'menu_slug' => 'test-options-page', 42 | 'capability' => 'edit_posts', 43 | // choose a menu postion that you know will not be changed 44 | 'position' => '75.374981', 45 | 'parent_slug' => '', 46 | 'icon_url' => 'dashicons-warning', 47 | 'redirect' => false, 48 | 'post_id' => 'options', 49 | 'autoload' => true 50 | ); 51 | acf_add_options_page($args); 52 | } 53 | 54 | // add a menu page in WP on admin_init with priority < 10 55 | add_action('admin_menu', 'add_a_test_menu_page', 9); 56 | function add_a_test_menu_page() { 57 | // all of these arguments are identical to the arguments 58 | // used to create in the function acf_add_options_page() 59 | $page_title = 'Test Options Page'; 60 | $menu_title = 'Test Options Page'; 61 | $capability = 'edit_posts'; 62 | // choose a menu postions that you know will not be changed 63 | $position = '75.374981'; 64 | // set the page slug, do not let it be generated 65 | // or you may not be able to find it to remove 66 | $menu_slug = 'test-options-page'; 67 | $callback = ''; 68 | $icon = 'dashicons-warning'; 69 | add_menu_page($page_title, $menu_title, $capability, $menu_slug, $callback, $icon, $position); 70 | } 71 | 72 | // remove the duplicate menu item 73 | // ACF uses a priority of 99 for the admin_menu hook 74 | // so we just need to call this with a higher priority 75 | add_action('admin_menu', 'remove_duplicate_admin_menu', 100); 76 | function remove_duplicate_admin_menu() { 77 | global $menu; 78 | // loop trrough the menu and remove one of the duplicates 79 | // this loop is looking for the page slug 80 | foreach ($menu as $key => $values) { 81 | if ($values[2] == 'test-options-page') { 82 | // found our slug, unset the menu item and exit 83 | unset($menu[$key]); 84 | break; 85 | } 86 | } 87 | } 88 | 89 | // add a CPT that is a sub menu item of the options page 90 | // so that we can see that it works 91 | add_action('init', 'add_test_cpt'); 92 | function add_test_cpt() { 93 | $args = array( 94 | 'labels' => array('name' => 'Test CPT', 'singular_name' => 'Test CPT'), 95 | 'show_ui' => true, 96 | 'show_in_menu' => 'test-options-page', 97 | 'map_meta_cap' => true 98 | ); 99 | register_post_type('test-cpt', $args); 100 | } 101 | 102 | 103 | ?> 104 | -------------------------------------------------------------------------------- /acf-image-aspect-ratio-validation.php: -------------------------------------------------------------------------------- 1 | 'ratio_width', 39 | 'type' => 'number', 40 | 'label' => __('Aspect Ratio'), 41 | 'instructions' => __('Restrict which images can be uploaded'), 42 | 'default_value' => 0, 43 | 'min' => 0, 44 | 'step' => 1, 45 | 'prepend' => __('Width'), 46 | ); 47 | acf_render_field_setting($field, $args); 48 | 49 | $args = array( 50 | 'name' => 'ratio_height', 51 | 'type' => 'number', 52 | // notice that there's no label when appending a setting 53 | 'label' => '', 54 | 'default_value' => 0, 55 | 'min' => 0, 56 | 'step' => 1, 57 | 'prepend' => __('Height'), 58 | // this how we append a setting to the previous one 59 | 'wrapper' => array( 60 | 'data-append' => 'ratio_width', 61 | 'width' => '', 62 | 'class' => '', 63 | 'id' => '' 64 | ) 65 | ); 66 | acf_render_field_setting($field, $args); 67 | 68 | $args = array( 69 | 'name' => 'ratio_margin', 70 | 'type' => 'number', 71 | 'label' => '', 72 | 'default_value' => 0, 73 | 'min' => 0, 74 | 'step' => .5, 75 | 'prepend' => __('±'), 76 | 'append' => __('%'), 77 | 'wrapper' => array( 78 | 'data-append' => 'ratio_width', 79 | 'width' => '', 80 | 'class' => '', 81 | 'id' => '' 82 | ) 83 | ); 84 | acf_render_field_setting($field, $args); 85 | } // end function acf_image_aspect_ratio_settings 86 | 87 | // add filter to validate images to ratio 88 | add_filter('acf/validate_attachment/type=image', 'acf_image_aspect_ratio_validate', 20, 4); 89 | function acf_image_aspect_ratio_validate($errors, $file, $attachment, $field) { 90 | // check to make sure everything has a value 91 | if (empty($field['ratio_width']) || empty($field['ratio_height']) || 92 | empty($file['width']) || empty($file['height'])) { 93 | // values we need are not set or otherwise empty 94 | // bail early 95 | return $errors; 96 | } 97 | // make sure all values are numbers, you never know 98 | $ratio_width = intval($field['ratio_width']); 99 | $ratio_height = intval($field['ratio_height']); 100 | // make sure we don't try to divide by 0 101 | if (!$ratio_width || !$ratio_height) { 102 | // cannot do calculations if something is 0 103 | // bail early 104 | return $errors; 105 | } 106 | $width = intval($file['width']); 107 | $height = intval($file['height']); 108 | // do simple ratio math to see how tall 109 | // the image is allowed to be based on width 110 | $allowed_height = $width/$ratio_width*$ratio_height; 111 | // get margin and calc min/max 112 | $margin = 0; 113 | if (!empty($field['ratio_margin'])) { 114 | $margin = floatval($field['ratio_margin']); 115 | } 116 | $margin = $margin/100; // convert % to decimal 117 | $min = round($allowed_height - ($allowed_height*$margin)); 118 | $max = round($allowed_height + ($allowed_height*$margin)); 119 | if ($height < $min || $height > $max) { 120 | // does not meet the requirement, generate an error 121 | $errors['aspect_ratio'] = __('Image does not meet Aspect Ratio Requirements of '). 122 | $ratio_width.__(':').$ratio_height.__('±').$ratio_margin.__('%'); 123 | } 124 | // return the errors 125 | return $errors; 126 | } // end function acf_image_aspect_ratio_validate 127 | 128 | ?> 129 | -------------------------------------------------------------------------------- /acf-field-label-functions.php: -------------------------------------------------------------------------------- 1 | '; 34 | 35 | - get_sub_field_choice_label($selector, $value) 36 | - the_sub_field_choice_label($selector, $value) 37 | 38 | Example: 39 | 40 | if (have_rows('repeater')) { 41 | while(have_rows('repeater')) { 42 | the_row(); 43 | $values = get_sub_field('checkbox'); 44 | if ($values) { 45 | foreach ($values as $value) { 46 | the_sub_field_choice_label('checkbox', $value); 47 | echo '
'; 48 | } 49 | } 50 | } 51 | } 52 | */ 53 | 54 | // this is done on plugins_loaded to make sure that ACF is loaded 55 | add_action('plugins_loaded', 'acf_label_functions'); 56 | function acf_label_functions() { 57 | // make sure acf is active 58 | if (!class_exists('acf')) { 59 | return; 60 | } 61 | // check if functions exist just in case they get added to acf 62 | if (!function_exists('get_field_label')) { 63 | function get_field_label($selector, $post_id=false) { 64 | $post_id = acf_get_valid_post_id($post_id); 65 | $field = acf_maybe_get_field($selector, $post_id); 66 | if (!$field) { 67 | return NULL; 68 | } 69 | return $field['label']; 70 | } // end function get_field_label 71 | } // end !function 72 | if (!function_exists('the_field_label')) { 73 | function the_field_label($selector, $post_id=false) { 74 | echo get_field_label($selector, $post_id); 75 | } // end function the_field_label 76 | } // end !function 77 | if (!function_exists('get_sub_field_label')) { 78 | function get_sub_field_label($selector) { 79 | $row = acf_get_loop('active'); 80 | if (!$row) { 81 | return NULL; 82 | } 83 | $sub_field = get_row_sub_field($selector); 84 | if (!$sub_field) { 85 | return NULL; 86 | } 87 | return $sub_field['label']; 88 | } // end function get_sub_field_label 89 | } // end !function 90 | if (!function_exists('the_sub_field_label')) { 91 | function the_sub_field_label($selector) { 92 | echo get_sub_field_label($selector); 93 | } // end function the_sub_field_label 94 | } // end !function 95 | if (!function_exists('get_field_choice_label')) { 96 | function get_field_choice_label($selector, $value, $post_id=false) { 97 | $post_id = acf_get_valid_post_id($post_id); 98 | $field = acf_maybe_get_field($selector, $post_id); 99 | if (!$field || !isset($field['choices']) || !isset($field['choices'][$value])) { 100 | return NULL; 101 | } 102 | return $field['choices'][$value]; 103 | } // end function get_field_choice_label 104 | } // end !function 105 | if (!function_exists('the_field_choice_label')) { 106 | function the_field_choice_label($selector, $value, $post_id=false) { 107 | echo get_field_choice_label($selector, $value, $post_id); 108 | } // end function the_field_choice_label 109 | } // end if !function 110 | if (!function_exists('get_sub_field_choice_label')) { 111 | function get_sub_field_choice_label($selector, $value) { 112 | $row = acf_get_loop('active'); 113 | if (!$row) { 114 | return NULL; 115 | } 116 | $sub_field = get_row_sub_field($selector); 117 | if (!$sub_field || !isset($sub_field['choices']) || !isset($sub_field['choices'][$value])) { 118 | return NULL; 119 | } 120 | return $sub_field['choices'][$value]; 121 | } // end function get_sub_field_choice_label 122 | } // end !function 123 | if (!function_exists('the_sub_field_choice_label')) { 124 | function the_sub_field_choice_label($selector, $value) { 125 | echo get_sub_field_choice_label($selector, $value); 126 | } // end function the_sub_field_choice_label 127 | } // end !function 128 | } // end function acf_label_functions 129 | 130 | ?> -------------------------------------------------------------------------------- /acf-reciprocal-relationship.php: -------------------------------------------------------------------------------- 1 | 163 | -------------------------------------------------------------------------------- /acf-reciprocal-relationships-multiple.php: -------------------------------------------------------------------------------- 1 | 9 for the `admin_menu` hook. For more information see the 72 | comment at the top of the file. 73 | 74 | ##### [acf-page-ancestor-location-rule.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/acf-page-ancestor-location-rule.php) 75 | 76 | This is another custom location rule example. This custom location rule lets you choose to set a field group 77 | to be located on any page that is a descendant of the page selected. 78 | 79 | ##### [acf-page-granparent-location-rule.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/acf-page-granparent-location-rule.php) 80 | 81 | This is another custom location rule example and it is similar to the ancestor location rule except that the 82 | field group will only be located on pages that have a particular grand parent, or the second ancestor. 83 | 84 | ##### [acf-post-category-ancestor-location-rule.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/acf-post-category-ancestor-location-rule.php) 85 | 86 | This is another custom location rule example. This one sets a location based on category ancestor. It will 87 | actually work with any hierarchical taxonomy. 88 | 89 | ##### [acf-reciprocal-relationship.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/acf-reciprocal-relationship.php) 90 | 91 | This file contains and example of how to create a reciprical or two way relationship field using either 1 or 2 92 | relationship or post object fields. This file must be edited to match the field or fields that you wish to 93 | convert into a bidirectional relationship. See the comments in the file for more information. 94 | 95 | ##### [acf-reciprocal-relationships-multiple.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/acf-reciprocal-relationships-multiple.php) 96 | 97 | Contributed By: [PUncle](https://github.com/PUncle) 98 | 99 | Like the acf-reciprocal-relationship.php example above, this function allows two different reltionship fields 100 | to reciprocate selections. The difference being that this function usues PHP closures to pass in field keys, 101 | avoiding the need to hard-code them inside the function; making this example reusable across multiple 102 | reciprocal relationships. This function requires PHP 5.3+. 103 | 104 | ##### [customized-options-page.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/customized-options-page.php) 105 | 106 | This is an example of how you can make modification to an ACF Options Page to add additional content into 107 | the page that is generated by ACF, for example between the title and the ACF field groups. 108 | 109 | ##### [change-option-page-location-display.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/change-option-page-location-display.php) 110 | 111 | ACF shows the menu_title as the choice in location rules. This can be confusing for those of use that create 112 | multiple options pages with the same menu title and different page titles. This filter alters the location 113 | display to show the page title instead. 114 | 115 | ##### [correct-number-field-mouse-scrollwheel-action.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/correct-number-field-mouse-scrollwheel-action.php) 116 | 117 | Correct number field scrollwheel behavior 118 | 119 | ##### [default-image-for-image-field.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/default-image-for-image-field.php) 120 | 121 | This is a simple example of how to add a default image setting to image fields. Note that this is only a bisic example and may require you to save the field group before you can select a default image. [I have posted more 122 | information here on how to correct this situation](https://acfextras.com/default-image-for-image-field/) 123 | 124 | ##### [is_admin-acf-location-rule.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/is_admin-acf-location-rule.php) 125 | 126 | This is another custom location rule example. This rule lets you choose a field group to be used only in the 127 | in the admin or on a front end form. 128 | 129 | ##### [page-nth-level-location-rule.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/page-nth-level-location-rule.php) 130 | 131 | This is another custom location rule example. This rule let's you choose to display a field group only on a 132 | specific level of a hierarchical post type. This rule should work with any hierarchical post type. 133 | 134 | ##### [public-post-type-location-rule.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/public-post-type-location-rule.php) 135 | 136 | This is another custom location rule example. This one will let you choose a location based on whether or not 137 | the post type is a public post type. 138 | 139 | ##### [public-taxonomy-location-rule.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/public-taxonomy-location-rule.php) 140 | 141 | This is like the public post type rule, but for taxonomies 142 | 143 | ##### [render-image-in-editor.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/render-image-in-editor.php) 144 | 145 | This is an example or how to render additional information in a field. This particular example shows how to 146 | display an image when the URL for the image is from another site rather than an image in the media library. 147 | 148 | #### [unique-repeater-sub-field.php](https://github.com/Hube2/acf-filters-and-functions/blob/master/unique-repeater-sub-field.php) 149 | 150 | A filter that can be used to check for unique repeater sub field values in any repeater. 151 | --------------------------------------------------------------------------------