Support

Account

Forum Replies Created

  • I’ve been using this for a while without issue. I sometimes modify a field group imported from the parent within the child theme. It simply saves a new acf-json file into the child theme.
    This used to work fine but I my posts are suddenly only rendering the group as defined in the parent. So I had to flip the load order though I have no idea why.

    
    add_filter('acf/settings/load_json', function($paths) {
      $paths = array();
    
      if(is_child_theme())
      {
        $paths[] = get_stylesheet_directory() . '/acf-json';
      }
      $paths[] = get_template_directory() . '/acf-json';
    
      return $paths;
    });
    
  • Just a quick update. For an upgrade-proof workaround, just save the entire contents of the file I posted earlier in this thread to your theme and use require_once within your functions.php file.
    require_once 'class-acf-location-post-template.php
    It will override the built in one and provide the fix.

  • I created a acf ticket but don’t have a link to it yet.
    For my situation a post template makes sense. I created an online training system where each lesson is comprised of a series of Topics which are custom post types. Some topics include a piece of audio which requires its own acf group. Being able to leverage all of the core topic logic and yet create an alternative version via a template is a perfect solution.

  • I was able to modify /includes/locations/class-acf-location-post-template.php with a workaround. I’ll post the whole file here for clarity. I added a class method acf_location_post_template::acf_get_post_templates() then changed both calls to the native get_post_templates()

    
    <?php 
    
    if( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
    
    if( ! class_exists('acf_location_post_template') ) :
    
    class acf_location_post_template extends acf_location {
    	
    	
    	/*
    	*  __construct
    	*
    	*  This function will setup the class functionality
    	*
    	*  @type	function
    	*  @date	5/03/2014
    	*  @since	5.0.0
    	*
    	*  @param	n/a
    	*  @return	n/a
    	*/
    	
    	function initialize() {
    		// vars
    		$this->name = 'post_template';
    		$this->label = __("Post Template",'acf');
    		$this->category = 'post';
    		$this->public = acf_version_compare('wp', '>=', '4.7');
        	
    	}
    	
    	
    	/*
    	*  get_post_type
    	*
    	*  This function will return the current post_type
    	*
    	*  @type	function
    	*  @date	25/11/16
    	*  @since	5.5.0
    	*
    	*  @param	$options (int)
    	*  @return	(mixed)
    	*/
    	
    	function get_post_type( $screen ) {
    		
    		// vars
    		$post_id = acf_maybe_get( $screen, 'post_id' );
    		$post_type = acf_maybe_get( $screen, 'post_type' );
    		
    		
    		// post_type
    		if( $post_type ) return $post_type;
    		
    		
    		// $post_id
    		if( $post_id ) return get_post_type( $post_id );
    		
    		
    		// return
    		return false;
    		
    	}
    	
    	
    	/*
    	*  rule_match
    	*
    	*  This function is used to match this location $rule to the current $screen
    	*
    	*  @type	function
    	*  @date	3/01/13
    	*  @since	3.5.7
    	*
    	*  @param	$match (boolean) 
    	*  @param	$rule (array)
    	*  @return	$options (array)
    	*/
    	
    	function rule_match( $result, $rule, $screen ) {
    		
    		// vars
    		$templates = array();
    		$post_id = acf_maybe_get( $screen, 'post_id' );
    		$page_template = acf_maybe_get( $screen, 'page_template' );
    		$post_type = $this->get_post_type( $screen  );
    		
    		// bail early if no post_type found (not a post)
    		if( !$post_type ) return false;
    		
    		
    		// get templates (WP 4.7)
    		if( acf_version_compare('wp', '>=', '4.7') ) {
    			
    			// $templates = wp_get_theme()->get_post_templates();
    			$templates = $this->acf_get_post_templates();
    			
    		}
    		
    		
    		// 'page' is always a valid pt even if no templates exist in the theme
    		// allows scenario where page_template = 'default' and no templates exist
    		if( !isset($templates['page']) ) {
    			
    			$templates['page'] = array();
    			
    		}
    		
    		
    		// bail early if this post type does not allow for templates
    		if( !isset($templates[ $post_type ]) ) return false;
    		
    		
    		// get page template
    		if( !$page_template ) {
    		
    			$page_template = get_post_meta( $post_id, '_wp_page_template', true );
    			
    		}
    		
    		
    		// new post - no page template
    		if( !$page_template ) $page_template = "default";
    		
    		
    		// match
    		return $this->compare( $page_template, $rule );
    		
    	}
    	
    	
    	/*
    	*  rule_operators
    	*
    	*  This function returns the available values for this rule type
    	*
    	*  @type	function
    	*  @date	30/5/17
    	*  @since	5.6.0
    	*
    	*  @param	n/a
    	*  @return	(array)
    	*/
    	
    	function rule_values( $choices, $rule ) {
    		
    		// vars
    		$choices = array(
    			'default' => apply_filters( 'default_page_template_title',  __('Default Template', 'acf') )
    		);
    		
    		
    		// get templates (WP 4.7)
    		if( acf_version_compare('wp', '>=', '4.7') ) {
    			// $templates = wp_get_theme()->get_post_templates();
    			$templates = $this->acf_get_post_templates();
    			$choices = array_merge($choices, $templates);
    		}
    		
    		
    		// return choices
    		return $choices;
    		
    	}
    
    	function acf_get_post_templates() {
    		$post_types = acf_get_post_types(array(
    			'exclude'	=> array('attachment')
    		));
    		$post_templates = array();
    		foreach ($post_types as $post_type) {
    			if ( $files = wp_get_theme()->get_page_templates(null, $post_type) ) {
    				if ( ! isset( $post_templates[ $post_type ] ) ) {
    					$post_templates[ $post_type ] = array();
    				}
    				$post_templates[ $post_type ] = $files;
    			}
    		}
    		return $post_templates;
    	}
    	
    }
    
    // initialize
    acf_register_location_rule( 'acf_location_post_template' );
    
    endif; // class_exists check
    
    ?>
    
  • Ok thanks @hube2. I had assumed a ticket already existed for this.

    I did some digging and turns out it might be a bug in WP. ACF uses wp_get_theme()->get_post_templates(); to get available templates as it probably should but WP_Theme::get_post_templates() fails to request files from the parent theme folder and there are no params or hooks to force it to.
    I created a ticket on trac. https://core.trac.wordpress.org/ticket/41717

    Going to take a shot at a workaround and I’ll submit here and in a new ticket if it works.

  • Just got hit by this. Hoping to hear about a resolution soon.

  • @hube2 Your load_value version was spot on. Good work!

  • It actually required a bit of a hack. I can’t post the entire plugin but I can try to hit the highlights. These are just snippets from my larger code base so try and just grasp the concepts.

    First enqueue ACF

    
    add_action('admin_enqueue_scripts', array( $this, 'on_admin_enqueue_scripts' ) );
    	function on_admin_enqueue_scripts() {
    		// global
    		global $pagenow;
    		if ( 'nav-menus.php' != $pagenow ) {
    			return;
    		}
    
    	  	wp_enqueue_script('acf-input');
    	  	wp_enqueue_style('acf-mpa');
    	}
    

    Now you need to dequeue the native walker that normally renders the menu admin and replace it with one you copied and edited to include the acf field.

    
    include_once( 'edit_custom_walker.php' );
    add_filter( 'wp_edit_nav_menu_walker', array( $this, 'acf_menu_edit_walker'), 1, 2 );
    function acf_menu_edit_walker($walker, $menu_id) {
    		$options = get_option( 'dcdc_menu_partials' );
    		$keys = array_keys($options['menu_ids']);
    		if ( in_array($menu_id, $keys) ) {
    			remove_all_filters('wp_edit_nav_menu_walker' );
    	    	return 'Walker_Nav_Menu_Edit_Custom';
    		} 
    		return $walker;	    
    	}
    

    Part of your custom walker needs an acf_form()

    
    <!-- end normal -->
    				    <p>
    				        <?php 
    				        $GLOBALS['current_menu_item_id'] = $item_id;
    					    $acf_options = array(
    					    	'post_id' => $item_id,
    					    	'form' => false,
    					    	/* (array) An array of field group IDs/keys to override the fields displayed in this form */
    					    	'field_groups' => array('group_573e71ff44db1'),
    					    	'return' => '',
    					    );
    					    acf_form( $acf_options );
    				        ?>
    				    </p>
    				    <!-- end acf -->
    

    Then you have to catch the acf form value when the menu is being saved

    
    add_action( 'wp_update_nav_menu_item', array( $this, 'update_custom_nav_fields'), 10, 3 );
    function update_custom_nav_fields ($menu_id, $menu_item_db_id, $args) {
    		if ( isset( $_REQUEST['acf']['field_573e727f24ef4'] ) && is_array( $_REQUEST['acf']['field_573e727f24ef4'] ) ) {
    			$field_value = $_REQUEST['acf']['field_573e727f24ef4'][$menu_item_db_id];
    		    $field = get_field_object('field_573e727f24ef4', $menu_item_db_id, false, false);
    		    acf_update_value( $field_value, $menu_item_db_id, $field );
    		}
    	}
    

    This also a requires a custom nav walker for the front end in order to pick up on the field data. Hope that helps

  • UPDATE: apparently the actual selector is a sibling of the <select> tag.
    $expirationSelect2.siblings('input').select2("open"); appears to work.

  • Update: The selected terms actually do save correctly, they’re just reflected correctly in the edit. ie. If I select two checkboxes, that will save but all boxes will be checked when I edit the term.

  • I haven’t had a chance to try this with an up to date ACF but at least I can better explain what I’m seeing.

    I have a custom post type, Announcement which has two dedicated Taxonomies: Announcement-Type and Announcement-Audience. The idea is that each Announcement-Type term needs to select which Audiences it’s targeting. That way, in the Announcement post editor, choosing an announcement-type (from an ACF taxonomy field shown as a radio group) will change the available options in an audience-type taxonomy field shown as checkboxes. I’m handling the view updates in javascript.

    So in my Announcement-Type term editor I’ve inserted the Annoucement-type taxonomy field. This field has “Load Terms” set to Yes.

    The result in the term editor is that all boxes are ticked all the time.

    And even if I change the selection for a term,

    The changes are not saved and all terms have all options ticked

    If I set Load Terms to ‘No’, everything works fine.

  • Sure, I’ll show some screens tomorrow to describe the issue better.

  • So I modified your solution with the intent making the value optional. I’m not very good with SQL so I doubt my use of the AND statement is the cleanest way to do that.

    
    function acf_get_terms( $args = array() ) {
      global $wpdb;
      $defaults = array(
        'taxonomy_slug'     => '',
        'acf_field_name'    => null,
        'meta_value'        => '',
      );
      $args = wp_parse_args( $args, $defaults );
    
      if ( empty($args['taxonomy_slug']) || ! taxonomy_exists( $args['taxonomy_slug'] ) || empty( $args['acf_field_name'] ) ) {
        return new WP_Error( 'invalid_option_name', __('ACF term meta names are formatted as {$term->taxonomy}_{$term->term_id}_{$field[\'name\']}.') );
      }
    
      $rows = $wpdb->get_results($wpdb->prepare(
          "
          SELECT option_name
          FROM {$wpdb->prefix}options
          WHERE option_name LIKE %s
          AND option_value LIKE %s
          ",
          $args['taxonomy_slug'] . '_%_' . $args['acf_field_name'],
          empty($args['meta_value']) ? '%' : $args['meta_value']
      ));
    
      $unit_ids = array();
    
      foreach( $rows as $row ){
          preg_match('/^' . $args['taxonomy_slug'] . '_(\d*)_' . $args['acf_field_name'] . '$/', $row->option_name, $matches);
          $unit_ids[] = $matches[1];
      }
    
      $terms = get_terms(array(
          'taxonomy' => $args['taxonomy_slug'],
          'hide_empty' => false,
          'include' => $unit_ids,
      ));
    
      return $terms;
    }
    
  • Thanks @James,
    It wasn’t until my drive home that I realized the lack of a wp_options api meant I was probably going to have to resort to wpdb. I’ll check it out tomorrow but this looks like exactly what I was after. Cheers.
    Sadly, this would be easier if termsmeta was adopted.

  • Thanks for the suggestion @jonathan
    I actually got it working by manually enqueueing acf-input rather than go the do_action() route. Race conditions I guess.

    
    add_action('admin_enqueue_scripts', 'on_admin_enqueue_scripts' );
    function on_admin_enqueue_scripts() {
      // global
      global $pagenow;
      if ( 'nav-menus.php' != $pagenow ) {
        return;
      }
      wp_enqueue_script('acf-input');
    }
    
  • @James,
    Thanks for the suggestion. I actually came up with a slightly more elegant solution.
    First I pluralize the name attribute by hooking into 'acf/prepare_field'

    
    // My field group contains only a single Post Object field
    add_filter('acf/prepare_field/type=post_object', 'my_acf_prepare_field' );
    function my_acf_prepare_field ( $field ) {
        $field['_input'] = $field['_input'] . '[]';
        return $field;
    }
    

    That will create an array of entries in $_POST

    
    [acf] => Array
            (
                [field_573e727f24ef4] => Array
                    (
                        [0] => 170
                        [1] => 152
                        [2] => 155
                        [3] => 170
                    )
    
                [_validate_email] =>
            )
    

    This creates 2 issues however.
    1. The array keys are kinda meaningless to me.
    2. acf_form_head() cannot process this. IMO it should be able to in a future version.

    To fix the first issue I had to store a post id in $GLOBALS at the time I was calling acf_form()

    
    // leaving out some context here but this demonstrates the idea
    $GLOBALS['current_menu_item_id'] = $item_id;
    $acf_options = array(
      'post_id' => $item_id,
      'form' => false,
      'field_groups' => array('group_573e71ff44db1'),
      'return' => '',
    );
    acf_form( $acf_options );
    

    Now we can modify our earlier prepare_field handler to pluraize with meaning

    
    function my_acf_prepare_field ( $field ) {
      $field['_input'] = $field['_input'] . '[' . $GLOBALS['current_menu_item_id'] . ']';
      return $field;
    }
    

    Which creates something in $_POST we can use during our fix for the second issue.

    
    [acf] => Array
      (
        [field_573e727f24ef4] => Array
          (
            [172] => 170
            [173] => 152
            [174] => 155
            [175] => 170
          )
    
        [_validate_email] =>
    )
    

    To fix the second issue I removed acf_form_head() and called acf’s meta update manually. In my case I was saving changes to a Menu so I just hooked into the relevant filter but this could be done wherever you’re handling your form submit. This is inspired by Remi Corson

    
    // save menu custom fields
    add_action( 'wp_update_nav_menu_item', 'my_update_custom_nav_fields', 10, 3 );
    function my_update_custom_nav_fields ($menu_id, $menu_item_db_id, $args) {
      if ( is_array( $_REQUEST['acf']['field_573e727f24ef4'] ) ) {
        $field_value = $_REQUEST['acf']['field_573e727f24ef4'][$menu_item_db_id];
        $field = get_field_object('field_573e727f24ef4', $menu_item_db_id, false, false);
        acf_update_value( $field_value, $menu_item_db_id, $field );
      }
    }
    
  • Still no luck, but I did notice that acf.php and acf-pro.php each use wp_register_script() with different dependency params where the latter has none. Why is that?

  • Ooooh! I get it now. I’ll just create a non-hierarchical taxonomy and offer that as my open tag field. Thanks!

  • From my understanding, Tag’s and Taxonomy+Terms are very similar other than a Taxonomies ability to be hierarchical. I’m building a custom post type with a front end form for creating new posts. In the form I intend to allow choosing category(s) and adding tags. On the archive for this post type I want to list all of the categories for easy filtering. I don’t want that category list to be cluttered with every tag ever used. I feel they should be separate types of meta-data. Is there a way I can pull that off using just the Taxonomy field type?

  • My code’s not bullet proof for sure, but given a certain environment+process, it should work. So I’ll attempt to describe my setup to see if we differ.
    First. Do not use the wp-post-meta-revisions plugin. Even if you don’t use its required filter, I fear it creates a race condition with ACF. There may be a more elegant way to tie the two plugins together but I couldn’t find one. This could be an issue if it ever becomes part of core.

    Ok, so as far as front end EDITS, acf_form_head() must come before get_header() as explained here. In fact, that basic code example should be all you need. I haven’t tested this where a specific post_id is passed to acf_form’s post_id argument although that should work too.
    My code is only intended to be ran while processing an acf_form() so it assumes $_POST['acf'] will exist.

    As far as your 3 revisions, wordpress is kinda funny. I tested on a clean wordpress install with no plugins and the twentyfifteen theme and when you create a new post and publish, you won’t see the revisions meta box. Then if you change the content, title or except and click update, the revisions meta box will show (if you have it in screen options) and two revisions will be there. They are your first ‘publish’ and first ‘update’. This doesn’t explain why you see 3 after your first front end edit. Perhaps you could share more about your setup.

  • Ok so I finally have something that so far appears to be pretty solid. It’s basically hooking into acf filters to trigger our own revision and then copying values over. This hasn’t been tested for creating NEW posts with acf_form, just editing them.

    
    function my_pre_save_post( $post_id ) {
        // bail early if editing in admin
        // ACF already handles admin revisions correctly
        if( is_admin() ) {
          return $post_id;
        }
        // force a post update, this will generate a revision and
        // trigger the '_wp_put_post_revision' action
        wp_update_post( get_post($post_id) );
        // allow acf to update the parent post meta normally
        return $post_id;
    }
    add_filter('acf/pre_save_post' , 'my_pre_save_post' );
    
    // revision just got created
    // detect all of the acf fields on the original and
    // apply them to the revision
    function _on_wp_put_post_revision( $revision_id ) {
      // bail early if editing in admin
      if( is_admin() ) {
        return;
      }
      // get the revision post object
      $revision = get_post( $revision_id );
      // get the id of the original post
      $post_id  = $revision->post_parent;
      // A lot of this is copied from ACF's revision class
      // get all the post meta from the original post
      $custom_fields = get_post_custom( $post_id );
      if( !empty($custom_fields) ) {
        foreach( $custom_fields as $k => $v ) {
          // value is always an array
          $v = $v[0];
          // bail early if $value is not is a field_key
          if( !acf_is_field_key($v) ) {
            continue;
          }
          // remove prefix '_' field from reference
          $field_name = substr($k, 1);
          // update the field value using the POST value supplied in the form
          update_field($field_name, $_POST['acf'][$v], $revision_id);
          // add the reference key
          update_metadata('post', $revision_id, $k, $v);
        }
      }
    }
    add_action( '_wp_put_post_revision', '_on_wp_put_post_revision' );
    
  • UPDATE: disregard this hack. See next post for a more complete solution.

    Ok so I’ve made a little progress on the concept of revisioning with acf_form() but is admittedly a hack. It will revision a post but for some reason, not until the 2nd change. I’m still working on it but I’m posting it here in hopes that someone else can expand on or replace with a more elegant solution.

    Basically I’m hijacking ‘pre_save_post’ and instead of letting it alter post meta for the existing post, I’m forcing the creation of a revision and letting acf act on that new post ID instead. Of course we still want the active post to get updated so I’m manually calling an un-documented acf function which obviously is a very bad approach.

    function my_pre_save_post( $post_id ) {
        // bail early if editing in admin
        if( is_admin() ) {
          return;
        }
        // bail if this is a brand new post
        if ( ! is_numeric($post_id) ) {
          return $post_id;
        }
    
        acf_save_post( $post_id );
        add_filter( 'wp_save_post_revision_check_for_changes', 'on_forced_revision', 10, 3 );
        $new_id = wp_save_post_revision( $post_id );
        remove_filter( 'wp_save_post_revision_check_for_changes', 'on_forced_revision', 10 );
        // return the new ID
        return $new_id;
    
    }
    add_filter('acf/pre_save_post' , 'my_pre_save_post', 1, 2 );
    
    function on_forced_revision($check_for_changes, $last_revision, $post) {
      if( isset($_POST['_acfchanged']) && $_POST['_acfchanged'] == '1' )
      {
        return false;
      }
      return $check_for_changes;
    }
  • @idealien I don’t see how this is correct for changes made on the front end.
    ACF handles it’s own revisions very similar to how wp-post-meta-revisions does. They both react to filters applied as a result of wp_insert_post which is only called when updates are made in the admin. acf_form_head() however only updates metadata on a single post ID. It does not trigger any functions that would engage ACF’s revisioning or wp-post-meta-revisions revisioning. Altering WP_Query won’t reveal any revisions resulting from use of acf_form() because it doesn’t create any.

  • I didn’t realize ACF has already solved the admin side revisions. So the Post Meta Revisions Plugin is not needed not that it integrates well anyway. Because of ACF’s multiple meta-keys per field setup, a plugin native solution is most desirable. This is the last piece of a big problem so I would really love to hear from @elliot how this is coming along.

Viewing 25 posts - 1 through 25 (of 35 total)