PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / trunk
Secure Custom Fields vtrunk
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / src / AI / GEO / Outputs / Blocks.php
secure-custom-fields / src / AI / GEO / Outputs Last commit date
Blocks.php 2 months ago Posts.php 2 months ago
Blocks.php
288 lines
1 <?php
2 /**
3 * ACF 6.8.0 feature port.
4 *
5 * @package wordpress/secure-custom-fields
6 */
7
8 // phpcs:disable -- Upstream ACF 6.8.0 feature-port files are kept close to source.
9
10 namespace SCF\AI\GEO\Outputs;
11
12 use SCF\AI\GEO\GEO;
13
14 // Exit if accessed directly.
15 defined( 'ABSPATH' ) || exit;
16
17 /**
18 * SCF GEO Blocks Output
19 *
20 * Extends ACF Blocks to add JSON-LD structured data output for block fields.
21 *
22 * To enable JSON-LD output for a block, add "autoJsonLd": true to the ACF namespace
23 * in your block.json file, or use the acf/ai/block_jsonld_enabled filter.
24 *
25 * See README.md for complete usage examples and documentation.
26 */
27 class Blocks {
28
29 /**
30 * Constructor
31 */
32 public function __construct() {
33 $this->init();
34 }
35
36 /**
37 * Initialize the GEO Blocks extension.
38 *
39 * @since 6.8.0
40 *
41 * @return void
42 */
43 public function init() {
44 // Add support for autoJsonLd property from block.json ACF namespace.
45 add_filter( 'block_type_metadata_settings', array( $this, 'add_block_json_auto_jsonld_support' ), 10, 2 );
46
47 // Add support for autoJsonLd property from programmatic registration.
48 add_filter( 'acf/register_block_type_args', array( $this, 'add_programmatic_auto_jsonld_support' ) );
49
50 // Add front-end JSON-LD output for blocks.
51 add_action( 'acf/blocks/pre_block_template_render', array( $this, 'output_block_jsonld_data' ), 10, 6 );
52 }
53
54 /**
55 * Add support for autoJsonLd property from block.json ACF namespace
56 *
57 * Maps the 'autoJsonLd' property from block.json's acf namespace to 'auto_jsonld' setting.
58 * Also maps 'schemaType' to 'schema_type' for custom Schema.org @type values.
59 * This runs after ACF's own block.json handler.
60 *
61 * @since 6.8.0
62 *
63 * @param array $settings The compiled block settings.
64 * @param array $metadata The raw json metadata.
65 * @return array Modified block settings.
66 */
67 public function add_block_json_auto_jsonld_support( $settings, $metadata ) {
68 // Only process ACF blocks.
69 if ( ! isset( $metadata['acf'] ) || ! is_array( $metadata['acf'] ) ) {
70 return $settings;
71 }
72
73 // Map autoJsonLd from ACF namespace to auto_jsonld.
74 if ( isset( $metadata['acf']['autoJsonLd'] ) ) {
75 $settings['auto_jsonld'] = $metadata['acf']['autoJsonLd'];
76 }
77
78 // Map schemaType from ACF namespace to schema_type.
79 if ( isset( $metadata['acf']['schemaType'] ) ) {
80 $settings['schema_type'] = $metadata['acf']['schemaType'];
81 }
82
83 return $settings;
84 }
85
86 /**
87 * Add support for autoJsonLd property from programmatic registration
88 *
89 * Maps the 'autoJsonLd' property from the acf namespace to 'auto_jsonld' setting
90 * and 'schemaType' to 'schema_type' for blocks registered via acf_register_block_type().
91 *
92 * @since 6.8.0
93 *
94 * @param array $block The block settings array.
95 * @return array Modified block settings.
96 */
97 public function add_programmatic_auto_jsonld_support( $block ) {
98 // Check if this is a programmatic registration with ACF namespace.
99 if ( isset( $block['acf'] ) && is_array( $block['acf'] ) ) {
100 if ( isset( $block['acf']['autoJsonLd'] ) ) {
101 $block['auto_jsonld'] = $block['acf']['autoJsonLd'];
102 }
103
104 if ( isset( $block['acf']['schemaType'] ) ) {
105 $block['schema_type'] = $block['acf']['schemaType'];
106 }
107 }
108
109 return $block;
110 }
111
112 /**
113 * Output JSON-LD structured data for ACF block fields.
114 *
115 * @since 6.8.0
116 *
117 * @param array $block The block props.
118 * @param string $content The block content.
119 * @param boolean $is_preview Whether or not the block is being rendered for editing preview.
120 * @param integer $post_id The current post being edited or viewed.
121 * @param WP_Block $wp_block The block instance.
122 * @param array $context The block context array.
123 */
124 public function output_block_jsonld_data( $block, $content, $is_preview, $post_id, $wp_block, $context ) {
125 /**
126 * Filters whether to output debug comments in HTML
127 *
128 * @since 6.8.0
129 *
130 * @param bool $debug Whether to output debug comments. Default false.
131 */
132 $debug = apply_filters( 'acf/schema/debug', false );
133
134 // Don't output JSON-LD in the block editor preview.
135 if ( $is_preview ) {
136 if ( $debug ) {
137 echo "<!-- SCF AI Block JSON-LD: Skipped (block editor preview) -->\n";
138 }
139 return;
140 }
141
142 // Don't output if we don't have a block name.
143 if ( empty( $block['name'] ) ) {
144 if ( $debug ) {
145 echo "<!-- SCF AI Block JSON-LD: Skipped (no block name) -->\n";
146 }
147 return;
148 }
149
150 if ( $debug ) {
151 echo '<!-- SCF AI Block JSON-LD: Checking block: ' . esc_html( $block['name'] ) . " -->\n";
152 }
153
154 // Get the block type.
155 $block_type = acf_get_block_type( $block['name'] );
156 if ( ! $block_type ) {
157 if ( $debug ) {
158 echo '<!-- SCF AI Block JSON-LD: Block type not found for ' . esc_html( $block['name'] ) . " -->\n";
159 }
160 return;
161 }
162
163 // Check if this block has auto_jsonld enabled.
164 $auto_jsonld = isset( $block_type['auto_jsonld'] ) ? $block_type['auto_jsonld'] : false;
165
166 /**
167 * Filters whether JSON-LD output is enabled for this specific block.
168 *
169 * @since 6.8.0
170 *
171 * @param boolean $auto_jsonld Whether JSON-LD is enabled for this block.
172 * @param array $block The block props.
173 * @param array $block_type The block type settings.
174 */
175 $auto_jsonld = apply_filters( 'acf/schema/block_jsonld_enabled', $auto_jsonld, $block, $block_type );
176
177 // Exit if auto JSON-LD is not enabled for this block.
178 if ( ! $auto_jsonld ) {
179 if ( $debug ) {
180 echo '<!-- SCF AI Block JSON-LD: Block \'' . esc_html( $block['name'] ) . "' does not have JSON-LD enabled -->\n";
181 }
182 return;
183 }
184
185 /**
186 * Filters the field objects before retrieval, allowing blocks to provide custom data.
187 *
188 * This is useful for blocks that link to other post types or need custom field data handling.
189 * Return a non-null value to short-circuit the default get_field_objects() call.
190 *
191 * @since 6.8.0
192 *
193 * @param array|null $field_objects The field objects array, or null to use default behavior.
194 * @param array $block The block props.
195 * @param array $block_type The block type settings.
196 * @param int $post_id The current post ID.
197 */
198 $field_objects = apply_filters( 'acf/schema/block_field_objects', null, $block, $block_type, $post_id );
199
200 /**
201 * Filters the field objects for a specific block name/type.
202 *
203 * The dynamic portion of the hook name, `$block['name']`, refers to the block type name.
204 * For example, 'acf/schema/block_field_objects/block_name=acf/testimonial' for the testimonial block.
205 *
206 * @since 6.8.0
207 *
208 * @param array|null $field_objects The field objects array, or null to use default behavior.
209 * @param array $block The block props.
210 * @param array $block_type The block type settings.
211 * @param int $post_id The current post ID.
212 */
213 $field_objects = apply_filters( 'acf/schema/block_field_objects/block_name=' . $block['name'], $field_objects, $block, $block_type, $post_id );
214
215 // If no custom field objects were provided, get them from the block.
216 if ( null === $field_objects ) {
217 // Get all ACF field objects with values for this block.
218 // Use get_field_objects() to get both field metadata and values in a single call.
219 $field_objects = get_field_objects( $block['id'], false );
220 }
221
222 if ( ! $field_objects || ! is_array( $field_objects ) ) {
223 if ( $debug ) {
224 echo '<!-- SCF AI Block JSON-LD: No ACF fields found for block ' . esc_html( $block['name'] ) . " -->\n";
225 }
226 return;
227 }
228
229 if ( $debug ) {
230 echo '<!-- SCF AI Block JSON-LD: Found ' . count( $field_objects ) . " ACF fields -->\n";
231 }
232
233 // Process ACF fields and extract types from qualified properties.
234 // This handles schema_property mapping.
235 $processed_fields = GEO::process_fields( $field_objects );
236
237 // Get any explicitly set schema type for this block.
238 $provided_type = ! empty( $block_type['schema_type'] ) ? $block_type['schema_type'] : null;
239 $field_types = $processed_fields['field_types'] ?? array();
240
241 // Determine the final @type based on provided type or field types from qualified properties.
242 // Supports both string (single type) and array (multiple types).
243 $schema_type = GEO::determine_schema_type( $provided_type, $field_types, 'PropertyValue' );
244
245 // Remove internal type data from processed fields.
246 unset( $processed_fields['field_types'] );
247
248 // Build base JSON-LD structured data.
249 $jsonld_data = array(
250 '@context' => 'https://schema.org',
251 '@type' => $schema_type, // Can be string or array
252 '@id' => ! empty( $block['id'] ) ? get_permalink( $post_id ) . '#' . $block['id'] : get_permalink( $post_id ),
253 );
254
255 // Add block title if available.
256 if ( ! empty( $block_type['title'] ) ) {
257 $jsonld_data['name'] = $block_type['title'];
258 }
259
260 // Add block description if available.
261 if ( ! empty( $block_type['description'] ) ) {
262 $jsonld_data['description'] = $block_type['description'];
263 }
264
265 // Merge processed fields into JSON-LD data.
266 $jsonld_data = array_merge( $jsonld_data, $processed_fields );
267
268 /**
269 * Filters the JSON-LD data before output for a block.
270 *
271 * @since 6.8.0
272 *
273 * @param array $jsonld_data The JSON-LD data array.
274 * @param array $block The block props.
275 * @param array $block_type The block type settings.
276 */
277 $jsonld_data = apply_filters( 'acf/schema/data', $jsonld_data, $block, $block_type );
278
279 // Only output if we have data after filtering.
280 if ( empty( $jsonld_data ) ) {
281 return;
282 }
283
284 // Output the JSON-LD using the shared helper.
285 GEO::render_jsonld_script( $jsonld_data );
286 }
287 }
288