Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generic ACF block to display any field #7

Closed
bobbingwide opened this issue May 13, 2023 · 22 comments
Closed

Add generic ACF block to display any field #7

bobbingwide opened this issue May 13, 2023 · 22 comments
Assignees

Comments

@bobbingwide
Copy link
Owner

The Author name block developed for #6 is a rather trivial block.
Suppose I wanted to add an _author_image field to display next to the author name.
I'd have to develop a new block similar to the first one.
It wouldn't take much effort to copy and cobble the first block, but it would become laborious if I had to do this for lots of fields.

Wouldn't it be better to create a generic acf-field block that could display the value / values of any field registered to ACF?

Requirement

  • A generic acf-field block
  • To display the value or values of any field registered to ACF

Proposed solution

  • Develop a generic ACF Field block oik-testimonials/acf-field
  • The Field group for the block contains a field-name field where the user can enter the field name to be displayed.
  • The block will retrieve the chosen field and display it.

Note: I've not use the_field() for anything other than a text field so this is an opportunity for me to try each of the field types.
Starting with an image.

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 13, 2023

Oh nuts, the string "field_" may not be used at the start of a field name.
That's OK though - I can use the hyphen as originally planned: field-name

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 13, 2023

Looks like there's already a Meta Field Block plugin!
https://wordpress.org/plugins/display-a-meta-field-as-block

  • For the fields to be seen in the editor this plugin requires the Field Group settings to Show in REST API.
  • The Meta field block works within the Query block.
  • It suggests field names to be displayed - but doesn't offer these in a select box.

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 13, 2023

I was having difficulty getting ACF to display a new field added to the Testimonials group.

I believe this was because the plugin is calling acf_add_local_field_group() which must be overriding the definition I'd updated using the ACF Field groups. I'll have to look at the documentation for this API.

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 15, 2023

I'll have to look at the documentation for this API.

I've looked at the documentation and raised an request on ACF Slack #help channel. Had a reply but I'm none the wiser really. I can't yet see why my definition is allowed to override the user defined fields.

I can see a use for local fields when it comes to ACF blocks. Once the fields for a block have been defined they can be registered in response to acf/include_fields and the Field Group can be manually removed from the admin.

BTW. In s.b/wordpress I've now created the acf-json folder in the current theme ( Fizzie ).
See https://www.advancedcustomfields.com/resources/local-json/

@bobbingwide
Copy link
Owner Author

In s.b/wordpress and s.b/cwiccer, having added the field group for Testimonials to the acf-json folder in the Fizzie theme, in both environments there are two almost identical meta boxes for Testimonials.

  • The first is for <div id="acf-group_6461f5c4cde96" class="postbox acf-postbox">, which is the field group registered from the Local JSON.
  • The second is for <div id="acf-group_645a613de20b1" class="postbox acf-postbox">, which is the field group registered by the plugin.
  • We'd expect this since the field group we're checking for has the key of the second field group.
if ( acf_is_local_field_group('group_645a613de20b1')) {
		return;
	}

One note of interest is that the placeholder for an empty field is the same in both meta boxes.

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 15, 2023

Had a reply but I'm none the wiser really.

I've made some more progress. Rather than doing manual database queries, I've used acf_get_raw_field_groups() which will use the local JSON solution if configured, or access the acf-field-group posts to return an array of field groups.
This can be done in response to either acf/include_fields or acf/init.

When the local JSON files aren't in use then the query performed is

        [6] => Array

            [0] => (string) "
			SELECT   wp_posts.*
			FROM wp_posts 
			WHERE 1=1  AND wp_posts.post_type = 'acf-field-group' AND ((wp_posts.post_status = 'publish' OR wp_posts.post_status = 'acf-disabled'))
			
			ORDER BY wp_posts.menu_order ASC, wp_posts.post_title ASC
			
		"
            [1] => (double) 0.00053000450134277
            [2] => (string) "require_once('wp-admin/admin.php'), require_once('wp-load.php'), require_once('wp-config.php'), require_once('wp-settings.php'), do_action('init'), WP_Hook->do_action, WP_Hook->apply_filters, ACF->init, do_action('acf/include_fields'), WP_Hook->do_action, WP_Hook->apply_filters, oik_testimonials_acf_include_fields, oik_maybe_register_testimonials, acf_get_raw_field_groups, acf_get_raw_internal_post_type_posts, ACF_Internal_Post_Type->get_raw_posts, get_posts, WP_Query->query, WP_Query->get_posts"
            [3] => (double) 1684169380.2613
            [4] => Array 

If the $raw_field_groups array returned includes a group called Testimonials then we don't try to register it using acf_add_local_field_group().

[0] => Array

        [location] => Array

            [0] => Array

                [0] => Array

                    [param] => (string) "post_type"
                    [operator] => (string) "=="
                    [value] => (string) "oik_testimonials"



        [position] => (string) "normal"
        [style] => (string) "default"
        [label_placement] => (string) "top"
        [instruction_placement] => (string) "label"
        [hide_on_screen] => (string) ""
        [description] => (string) ""
        [show_in_rest] => (integer) 1
        [ID] => (integer) 3972
        [title] => (string) "Testimonials"
        [key] => (string) "group_6461f5c4cde96"
        [menu_order] => (integer) 0
        [active] => (boolean) 1

This solution enables the user to define the fields to be associated to the oik_testimonials CPT and display whichever fields are needed using the generic ACF field block #7

@bobbingwide
Copy link
Owner Author

I can't yet see why my definition is allowed to override the user defined fields.

I haven't dug any deeper into this problem. Basically, when the field was empty then the default value being displayed was the same even if the user defined default was different from the hardcoded version.

bobbingwide added a commit that referenced this issue May 15, 2023
@bobbingwide
Copy link
Owner Author

The prototype logic in dddc26e should be reworked:

  1. Remove the unnecessary code.
  2. Change oik_maybe_register_testimonials() to oik_maybe_add_local_field_group()
  3. Pass the array for the local field group to the renamed function and call acf_add_local_field_group() within the function when the field group's not already registered.
  4. Consider improving the logic to check the location as well as the title.

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 16, 2023

Duplicating the ACF Field block and changing the Field name attribute leaves unwanted data values

Original block with field-name set to author-image

<!-- wp:oik-testimonials/acf-field {"name":"oik-testimonials/acf-field",
"data":{"field-name":"author_image","_field-name":"field_645f589a88304"},
"mode":"preview"} /-->

After duplicating and changing the field name to author-image-id the _field-name attribute was unchanged.
In the block editor the original image was still being displayed.

<!-- wp:oik-testimonials/acf-field {"name":"oik-testimonials/acf-field",
"data":{"field-name":"author_image_id","_field-name":"field_645f589a88304"},
"mode":"preview"} /-->

After changing the field name to _oik_testimonial_name it became

<!-- wp:oik-testimonials/acf-field {"name":"oik-testimonials/acf-field",
"data":{"field_645f589a88304":"_oik_testimonial_name"},
"mode":"preview"} /-->

Then, later on, the value disappeared

<!-- wp:oik-testimonials/acf-field {"name":"oik-testimonials/acf-field",
"data":{"field_645f589a88304":""},
"mode":"preview"} /-->

@bobbingwide
Copy link
Owner Author

Front end rendering of fields of type image needs to take into account the Return format of the image:

  • Image array
  • Image URL
  • Image ID

For some reason the image is already displayed in the block. Not sure why.

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 17, 2023

Requirement

  • To be able to choose the field to display from a select list
  • The list shouldn't include fields which are only required in Blocks.

Solution

I've refactored again.

  • acf_get_possible_field_names() lists the possible field names when a field can't be found
  • It uses acf_get_field_groups() to list all the field groups, then acf_get_fields() to get the fields for the group.
  • It doesn't work recursively yet. Does it need to?
  • The fields are returned as an array keyed by name.
  • In response to acf/include_fields the acf-field-name field is dynamically built to be a select field with choices set to the array of possible field names.
  • This logic is now implemented in includes\acf-field-names.php

In edit mode the block currently looks like this.

image

Now I have to eliminate fields which are only registered for blocks. In this example they're the first 6 in the drop down.

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 17, 2023

I changed the select list to only display the internal field name & key when SCRIPT_DEBUG is true
and I improved the select list to eliminate fields which are only registered for blocks.

The logic includes fields for which the param value is post_type.

image

But the Field group name after the field name can be misleading...
the Block Count field is associated with post type Plugin.

It would be better to list one or more post types for which the field is applicable.
eg.

  • Block Count - Plugins
  • Author Name - Testimonials

@bobbingwide
Copy link
Owner Author

This is the ACF Field block in edit mode with SCRIPT_DEBUG true
image

  • I haven't converted the post_type to the post type name.
  • Nor have a catered for Not equals to

@bobbingwide
Copy link
Owner Author

I haven't converted the post_type to the post type name.
Nor have a catered for Not equals to

I have now.
image

In this example I added a Location Rule of Post Type is not equal to Posts

Implementation

  • I hooked into acf/prepare_field/name=acf-field-name
  • to set the choices field to the array returned from acf_get_possible_field_names()
  • and changed acf_get_possible_field_names() to check the rule['operator'], prepending the post type name with '!' when the operator is '!='
  • there's a possibility of some redundant logic associated with get_post_type_object()
  • when the acf_process_field_group() function is called in response to acf/filter_includes some of the CPTs may not be registered, so get_post_type_object() can return null.
  • This should no longer be the case when invoked in the prepare field filter function.

@bobbingwide
Copy link
Owner Author

bobbingwide commented May 22, 2023

The logic to display a WYSIWYG field is to display the content ASIS. No esc_html().
The logic to display an oEmbed field is similar.

But there's are a couple of problems:

  1. Unwanted paragraph tags are being created.... probably wpautop().
  2. There's a style attribute for the iframe which hides the embedded content.
    [value] => (string) "<blockquote class="wp-embedded-content" data-secret="KnIp5reDKa">
<a href="https://herbmiller.me/about/">About</a></blockquote>
<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" 
style="position: absolute; clip: rect(1px, 1px, 1px, 1px);" 
title="&#8220;About&#8221; &#8212; herb miller" 
src="https://herbmiller.me/about/embed/#?secret=fUKEQ6mMbA#?secret=KnIp5reDKa" data-secret="KnIp5reDKa" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no">
</iframe>"

The style attribute is added in wp_filter_oembed_result, in wp-includes/embed.php by the following code.
This was part of work to oEmbed: Add extra hardening around allowed HTML for improved sandboxing.

if ( ! empty( $content[1] ) ) {
		// We have a blockquote to fall back on. Hide the iframe by default.
		$html = str_replace( '<iframe', '<iframe style="position: absolute; clip: rect(1px, 1px, 1px, 1px);"', $html );
		$html = str_replace( '<blockquote', '<blockquote class="wp-embedded-content"', $html );
	}

Note: The oembed works in the editor - I can see the iframe.
I suspect it's a JavaScript problem with wp-embed.js not being enqueued.

bobbingwide added a commit that referenced this issue May 24, 2023
bobbingwide added a commit that referenced this issue May 24, 2023
@bobbingwide
Copy link
Owner Author

bobbingwide commented May 30, 2023

Now adding support for gallery but not sure exactly what HTML to generate.
Q: Should it be like the WordPress core/gallery block?

@bobbingwide
Copy link
Owner Author

When I added support for the link type field I needed to implement similar logic to that of the Meta Field Block plugin. This required the post_id parameter to be passed to the implementing function.

For improved extensibility I should do the following:

  • Implement a filter to allow overrides to the default processing for a particular field type and field name.
  • Pass the post_id parameter to each acf_display_field_type() function

@bobbingwide
Copy link
Owner Author

For ACF extensions see https://www.awesomeacf.com/

@bobbingwide
Copy link
Owner Author

bobbingwide commented Jun 6, 2023

The acf-field block's enclosing div should include the usuals suspects for a WordPress block in the class attribute.

  • wp-block-oik-testimonials-acf-field
  • acf-field-{field_type}
  • acf-field-{field_name}

Q: Does get_block_wrapper_attributes() automatically add the anchor ID?
A: No, apparently not, though the documentation does suggest this is done automatically by this function.
See https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/

@bobbingwide
Copy link
Owner Author

The block.json file should be updated to include several of the more useful supports attributes.

@bobbingwide
Copy link
Owner Author

bobbingwide commented Jul 11, 2023

Now that the acf-field-block plugin has been developed it's time to disable the generic field block logic in oik-testimonials.

I need to do this soon since I've twice encountered problems where the logic in oik-testimonials overrides the logic in acf-field-block.

  1. The first was when attempting to prevent tab field types from being listed in the select list of field names.
  2. Today I had problems when trying to internationalize / localize the block.

Both of these were due to oik-testimonials trumping the output of acf-field-blocks.
Although part of the reason for the second problem was how I was hooking into the actions and filters.
I'd naively assumed that since that since the acf/include_fields hook is called within init then I could call load_text_domain() within my own action hook for init.
But this didn't take into account that ACF hooks into init with a lower priority ( 5 ) than the default ( 10 ).
Therefore, ACF invokes acf/include_fields before other plugins have had a chance to respond to init.
One solution is to hook into init with a lower priority, another is to call load_plugin_text_domain() in the action hook response to acf/include_fields. I chose the second solution.

@bobbingwide
Copy link
Owner Author

Closing in favour of #8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant