Support

Account

Forum Replies Created

  • @hube2 Brilliant! That was exactly the reason, thanks for the explanation.

    This is very hard to figure out by yourself, though, I will open a ticket suggesting a doing_it_wrong if calling update_field while in the context of a acf/update_value filter

  • @hube2 Thanks for taking some brain time to parse and process my response in an intelligent and mindful way 🙂

    That makes sense. It would be really nice if ACF could re-initialize when switching to blog, so that it can fetch the fields from whatever site is being used on a multi-site at runtime. I understand this is not possible now.

    I’ll try the field definition approach, but I’d like to ask for the above as a feature request, who knows, perhaps it might be easy to re-init after switching to blog? I wouldn’t know.

    But thank you once again for your attention!

  • @hube2 That makes sense, thank you.

    I understand that the value formatting (eg: return Both) comes from the ACF Store “Values”, therefore the function acf_switch_stores being called on switch_blog should update ACF Stores to point to the multi-site instance after the switch.

    So, this is what I think is happening:

    1. Request starts at main site
    2. ACF initializes naturally, which triggers acf_register_store('values')
    3. I run switch_to_blog(2) on the context of filtering a field value, which fires the hook acf_switch_stores
    4. ACF switches to that multisite instance store, but the store isn’t initialized in this site instance, so the values store is empty

    It works at plugins_loaded because:

    1. Request starts at main site
    2. On plugins_loaded, I call switch_to_blog and then get_field, which internally calls ACF->initialize() on the context of the switched multi-site instance, therefore initializing the stores on the context of that site instance

    Would it make sense for ACF to register the stores again on the acf_switch_stores function, if they are not registered yet?

  • Thanks for the response @hube2, I’m trying to understand why does it work when hooked at plugins_loaded, though?

    There’s a changelog entry for a fix on 5.7.12 that seems to intend to fix this issue:

    * Fix - Fixed bug causing incorrect value retrieval after switch_to_blog().

    I believe it’s related to this code:

    
    /**
     * acf_switch_stores
     *
     * Triggered when switching between sites on a multisite installation.
     *
     * @date    13/2/19
     * @since   5.7.11
     *
     * @param   int                           $site_id New blog ID.
     * @param   int prev_blog_id Prev blog ID.
     * @return  void
     */
    function acf_switch_stores( $site_id, $prev_site_id ) {
    
    	// Loop over stores and call switch_site().
    	global $acf_stores;
    	foreach ( $acf_stores as $store ) {
    		$store->switch_site( $site_id, $prev_site_id );
    	}
    }
    add_action( 'switch_blog', 'acf_switch_stores', 10, 2 );
    

    So the ACF Stores would format the values when retrieving it with get_field, which is exactly what I need. But I don’t understand why that works in plugins_loaded, but not in filters like acf/load_field/name= and acf/pre_submit_form.

    Does that make sense?

  • Thanks for the response @hube2, but why does it work at plugins_loaded?

    There’s a changelog entry for a fix on 5.7.12 that seems to intend to fix this issue: * Fix - Fixed bug causing incorrect value retrieval after switch_to_blog().

    I believe this is related to this code:

    
    /**
     * acf_switch_stores
     *
     * Triggered when switching between sites on a multisite installation.
     *
     * @date    13/2/19
     * @since   5.7.11
     *
     * @param   int                           $site_id New blog ID.
     * @param   int prev_blog_id Prev blog ID.
     * @return  void
     */
    function acf_switch_stores( $site_id, $prev_site_id ) {
    
    	// Loop over stores and call switch_site().
    	global $acf_stores;
    	foreach ( $acf_stores as $store ) {
    		$store->switch_site( $site_id, $prev_site_id );
    	}
    }
    add_action( 'switch_blog', 'acf_switch_stores', 10, 2 );
    

    So the ACF Stores would format the values when retrieving it with get_field, which is exactly what I need. But I don’t understand why that works in plugins_loaded, but not in filters like acf/load_field/name= and acf/pre_submit_form.

    Does that make sense?

  • Yes, I can confirm that ACF is having a hard time loading formatted values in a multi-site field when running during the context of a acf/load_field/name=.

    1. Request starts at main site
    2. I have a filter such as: add_filter('acf/load_field/name=schools', function($field) {});
    3. Inside the closure of this filter, I run switch_to_blog(3) and then get_field('state', 'options');

    Where state is a field of type “Select” such as:

    
    ny : New York
    fl : Florida
    

    And is configured to return Both (Array). But due to a probable issue with ACF, likely at the values data store, it returns the unformatted value ny only.

    What I had to do was a dirty workaround:

    
    // Pre-load the school state value in the "plugins_loaded" context
    add_action('plugins_loaded', function() {
      if (!is_not_editing_schools()) {
        return;
      }
      switch_to_blog(3);
      global $school_state;
      $school_state = get_field('state', 'options'); // ['ny' => 'New York'], as expected
    });
    
    add_filter('acf/load_field/name=schools', function($field) {
    global $school_state;
    // Now I have ['ny' => 'New York'] here, while in this context I was only able to get 'ny' out of get_field after switch_to_blog.
    });
    
  • This might also be related with a possible issue with acf_get_store and switch_to_blog.

    After using switch_to_blog, the acf_get_store('values') returns the same data store as it did from the main site, therefore not finding my formatting rules for the field registered in the multi-site instance.

  • In my case there was one more thing.

    I was updating another field from the same Option page. I created another Option page just for this field, outside the same option page. After this, everything I’ve been trying to do in the past 6h just started to work.

  • CHRIIIIIST!

    I was using options, the correct post_id is option.

    It’s the same issue as the guy in the main thread.

    I ended up using: while(delete_row('my_repeater_field', 1, 'option'));

    Notice it’s option! Not options!!

  • I second the opinion that deleting rows is not working for a Repeater field in a OPTIONS page.

  • Seems I can’t edit my answer after some time. These are the filters I ended up using:

    
    $field_key = 'foo';
    
    add_filter( "acf/load_value/key=$field_key", function ( $original_value ) use ( $field_key ) {
        $network_value = get_site_option( $field_key, null );
    
        if ( ! is_null( $network_value ) ) {
            // Return the value from the "sitemeta" table, if available
            return $network_value;
        } else {
            // This will only happen if the field was never updated.
            return $original_value;
        }
    
    } );
    
    add_filter( "acf/update_value/key=$field_key", function ( $value ) use ( $field_key ) {
        $old_network_value = get_site_option( $field_key, null );
    
        // Replicate the field value to the "sitemeta" table
        if ( is_null( $old_network_value ) ) {
            add_site_option( $field_key, $value );
        } else {
            update_site_option( $field_key, $value );
        }
    
        $new_network_value = get_site_option( $field_key );
    
        return $new_network_value;
    } );
    
  • I have managed to accomplish network-level settings with some filters:

    
    // When loading the value for the "Foo" field, get it from the sitemeta table:
    add_filter( "acf/load_value/key=foo", function () use ( $field_key ) {
    	$value = get_site_option( $field_key );
    
    	if ( $value !== false ) {
    		return $value;
    	}
    
    	return '';
    } );
    
    // When setting the value for the "Foo" field, set it to sitemeta table:
    add_filter( "acf/update_value/key=foo", function ( $value ) use ( $field_key ) {
    	return add_site_option( $field_key, $value );
    } );
    

    Result: Updating the field “Foo” in any of the network sites applies it to all of the network sites.

    It’s up to the developer, though, to make sure that these fields are assigned to an Options page which has the super-admin permission callback.

  • I work on a company that has its own framework for working with WordPress.

    One of the concepts it has is a facade pattern for handling meta data such as those provided by ACF. This means we can theoretically change from ACF to CMB2 in any project at any given time, and vice-versa.

    What I’m doing is writing integration tests for this facade layer, thus I need to create, insert and get data from, then delete multiple groups and fields during runtime.

    Hope I didn’t suck at explaining,
    Lucas

  • Actually, to remove fields too:

    
    $group_key = 'group_foo';
    
    $fields = acf_get_local_fields( $group_key );
    
    foreach ( $fields as $field ) {
    	acf_remove_local_field( $field['key'] );
    	acf_get_local_store( 'fields' )->remove( $field['key'] );
    }
    
    acf_remove_local_field_group( $group_key );
    acf_get_store( 'field-groups' )->remove( $group_key );
    
  • I was able to achieve that like this:

    
    acf_remove_local_field_group( 'group_foo' );
    acf_get_store( 'field-groups' )->remove( 'group_foo' );
    
  • I’m sorry, my mistake.

    I had wrong filters for rule_match. Here’s the right code:

    
    # Should we display this in a Page?
    function acf_location_rules_match_wpml_page( $match, $rule, $options ) {
        var_dump([$match, $rule, $options]);
    }
    add_filter('acf/location/rule_match/wpml_page', 'acf_location_rules_match_wpml_page', 1, 3);
    

    It works now.
    Thanks!

  • Perhaps not the most elegant way, but hey, it works!

        
    /**
    *    Prevents a field from being updated from the panel in ACF.
    *    Useful for business-critical fields that must only by updated internally, but still need to be visible on the admin panel.
    */
    function prevents_field_from_being_updated_from_the_panel($value, $post_id, $field) {
            $backtraces = debug_backtrace();
            foreach ($backtraces as $backtrace) {
                if (!empty($backtrace['function']) && $backtrace['function'] == 'update_field') {
                    return $value;
                }
            }
            return get_field('field_5b5fcd08f9ecf', $post_id, false); # Change field_{key} to your field key/name
    }
    # If you have the field Key:
    add_filter('acf/update_value/key=YOUR_FIELD_KEY', 'prevents_field_from_being_updated_from_the_panel', 10, 3);
        
    # If you prefer to use field name:
    # add_filter('acf/update_value/name=YOUR_FIELD_NAME', 'prevents_field_from_being_updated_from_the_panel', 10, 3);
Viewing 18 posts - 1 through 18 (of 18 total)