PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.1
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.1
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / form-builder / form-builder-registration.php

form-builder-registration.php in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 4.0.1, at form-builder/form-builder-registration.php

736 lines 28.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Form-builder block registration, editor assets, and inserter gating.
4 * Main hooks: `wppb_fb_register_blocks()` (init:30) and
5 * `wppb_fb_enqueue_editor_assets()` (publishes `window.wppbFb`).
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) exit;
9
10 // 1. Allowed block types + pattern suppression
11
12 /**
13 * PB blocks only in form CPT inserters; stripped elsewhere. Blocks stay registered
14 * globally so existing markup still parses.
15 */
16 add_filter( 'allowed_block_types_all', 'wppb_fb_allowed_block_types', 10, 2 );
17 function wppb_fb_allowed_block_types( $allowed_blocks, $editor_context ) {
18 $field_block_names = array_values( wp_list_pluck( WPPB_FB_Field_Registry::all(), 'block' ) );
19 $all_registered = array_keys( WP_Block_Type_Registry::get_instance()->get_all_registered() );
20 $structural_blocks = array_values( array_filter( $all_registered, function ( $name ) use ( $field_block_names ) {
21 return strpos( $name, 'profile-builder/' ) === 0 && ! in_array( $name, $field_block_names, true );
22 } ) );
23 $pb_block_names = array_merge( $field_block_names, $structural_blocks );
24
25 // Repeater off: hide from inserter; existing blocks still render.
26 if ( ! wppb_fb_is_repeater_module_active() ) {
27 $pb_block_names = array_values( array_diff( $pb_block_names, array( 'profile-builder/field-repeater' ) ) );
28 }
29
30 $is_pb_form = ! empty( $editor_context->post ) && wppb_fb_is_form_cpt( $editor_context->post->post_type );
31
32 if ( $is_pb_form ) {
33 // PB form CPT only: hide gated blocks from inserter. Blocks stay registered.
34 // Do not apply on the non-PB branch — that would leak them into post/page editors.
35 $hidden = array_merge(
36 wppb_fb_inactive_addon_field_blocks(),
37 wppb_fb_inactive_addon_structural_blocks(),
38 wppb_fb_context_hidden_field_blocks(),
39 wppb_fb_free_locked_field_blocks()
40 );
41 if ( ! empty( $hidden ) ) {
42 return array_values( array_diff( $pb_block_names, $hidden ) );
43 }
44 return array_values( $pb_block_names );
45 }
46
47 // Never replace `true` with an explicit list — the PHP registry omits JS-only blocks
48 // and would strip them site-wide. PB blocks hide client-side instead. When another
49 // plugin already narrowed the list, subtract ours from it.
50 if ( is_array( $allowed_blocks ) && ! empty( $pb_block_names ) ) {
51 return array_values( array_diff( $allowed_blocks, $pb_block_names ) );
52 }
53
54 return $allowed_blocks;
55 }
56
57 /**
58 * Sets `window.wppbFbIsFormScreen` before the editor bundle loads.
59 * Blocks register before the store knows the post type.
60 */
61 add_action( 'enqueue_block_editor_assets', 'wppb_fb_flag_editor_screen', 9 );
62 function wppb_fb_flag_editor_screen() {
63 $is_form_screen = wppb_fb_is_form_cpt( get_post_type() ) && wppb_fb_is_active_for( get_post_type() );
64 wp_add_inline_script(
65 'wppb-form-editor-bundle',
66 'window.wppbFbIsFormScreen = ' . ( $is_form_screen ? 'true' : 'false' ) . ';',
67 'before'
68 );
69 }
70
71 /**
72 * Field blocks gated by `"wppbAddonFieldType"` when the add-on bridge is inactive.
73 *
74 * @return string[] Block names to hide from the PB-form inserter.
75 */
76 function wppb_fb_inactive_addon_field_blocks() {
77 $enabled = apply_filters( 'wppb_fb_enabled_addon_field_types', array() );
78 $hidden = array();
79 foreach ( WPPB_FB_Field_Registry::all() as $field_type => $entry ) {
80 if ( ! empty( $entry['addon_gated'] ) && ! in_array( $field_type, (array) $enabled, true ) ) {
81 $hidden[] = $entry['block'];
82 }
83 }
84 return $hidden;
85 }
86
87 /**
88 * Structural blocks declaring `"wppbAddonBlock": true` (block.json scan).
89 *
90 * @return string[] Registered structural block names with the flag.
91 */
92 function wppb_fb_addon_gated_structural_blocks() {
93 static $cache = null;
94 if ( $cache !== null ) return $cache;
95
96 $cache = array();
97 $blocks_dir = __DIR__ . '/blocks/src/blocks';
98 if ( ! is_dir( $blocks_dir ) ) return $cache;
99
100 $it = new DirectoryIterator( $blocks_dir );
101 foreach ( $it as $fileinfo ) {
102 if ( ! $fileinfo->isDir() || $fileinfo->isDot() ) continue;
103 $json = WPPB_FB_Field_Registry::read_block_json( $fileinfo->getPathname() . '/block.json' );
104 if ( ! is_array( $json ) || empty( $json['name'] ) ) continue;
105 if ( strpos( $json['name'], 'profile-builder/field-' ) === 0 ) continue;
106 if ( empty( $json['wppbAddonBlock'] ) ) continue;
107 $cache[] = $json['name'];
108 }
109 return $cache;
110 }
111
112 /**
113 * Structural add-on blocks inactive via `wppb_fb_enabled_structural_blocks`.
114 *
115 * @return string[] Block names to hide from the PB-form inserter.
116 */
117 function wppb_fb_inactive_addon_structural_blocks() {
118 $enabled = apply_filters( 'wppb_fb_enabled_structural_blocks', array() );
119 $hidden = array();
120 foreach ( wppb_fb_addon_gated_structural_blocks() as $name ) {
121 if ( ! in_array( $name, (array) $enabled, true ) ) {
122 $hidden[] = $name;
123 }
124 }
125 return $hidden;
126 }
127
128 /**
129 * Core field blocks the classic Manage Fields dropdown offers only under a
130 * particular WP context/setting; the form-builder inserter mirrors those gates.
131 *
132 * @return string[] PB field block names to exclude from the PB-form inserter.
133 */
134 function wppb_fb_context_hidden_field_blocks() {
135 $hidden = array();
136
137 // Blog Details only when multisite blog signup is on (manage-fields.php).
138 if ( ! function_exists( 'wppb_can_users_signup_blog' ) || ! wppb_can_users_signup_blog() ) {
139 $hidden[] = 'profile-builder/field-default-blog-details';
140 }
141
142 // Legacy IM fields only on DB versions before WP 3.6 defaults (manage-fields.php).
143 $show_contact_methods = apply_filters( 'wppb_remove_default_contact_methods', get_site_option( 'initial_db_version' ) < 23588 );
144 if ( ! $show_contact_methods ) {
145 $hidden[] = 'profile-builder/field-default-aim';
146 $hidden[] = 'profile-builder/field-default-yim';
147 $hidden[] = 'profile-builder/field-default-jabber';
148 }
149
150 /**
151 * Extend or override context-gated blocks hidden from the inserter.
152 *
153 * @param string[] $hidden Block names to hide.
154 */
155 return apply_filters( 'wppb_fb_context_hidden_field_blocks', $hidden );
156 }
157
158 /**
159 * Wire an add-on subscribe field: gating filter + `window.wppbFb.<bridge_key>` on
160 * `wppb_fb_enqueue_editor_assets_late`. Lists come from cached settings, not live API.
161 *
162 * @param array $config {
163 * @type string $field_type PB field-type label. Required.
164 * @type string $bridge_key `window.wppbFb` key for SubscribeFieldEdit. Required.
165 * @type callable $lists `[ { value, label } ]` for the editor selector.
166 * @type string $settings_url Add-on settings admin URL ('' if none).
167 * }
168 */
169 function wppb_fb_register_subscribe_bridge( array $config ) {
170 $field_type = isset( $config['field_type'] ) ? (string) $config['field_type'] : '';
171 $bridge_key = isset( $config['bridge_key'] ) ? (string) $config['bridge_key'] : '';
172 $settings_url = isset( $config['settings_url'] ) ? (string) $config['settings_url'] : '';
173 $lists_cb = isset( $config['lists'] ) ? $config['lists'] : null;
174
175 // A malformed config would otherwise emit `window.wppbFb. = …`; bail instead.
176 if ( $field_type === '' || $bridge_key === '' ) return;
177
178 add_filter( 'wppb_fb_enabled_addon_field_types', function ( $types ) use ( $field_type ) {
179 $types[] = $field_type;
180 return $types;
181 } );
182
183 add_action( 'wppb_fb_enqueue_editor_assets_late', function () use ( $bridge_key, $settings_url, $lists_cb ) {
184 $lists = is_callable( $lists_cb ) ? call_user_func( $lists_cb ) : array();
185 if ( ! is_array( $lists ) ) $lists = array();
186
187 $payload = array(
188 // array_values so an associative return still encodes as a JS array.
189 'lists' => array_values( $lists ),
190 'settingsUrl' => $settings_url,
191 );
192 wp_add_inline_script(
193 'wppb-form-editor-bundle',
194 'window.wppbFb = window.wppbFb || {}; window.wppbFb.' . $bridge_key . ' = ' . wp_json_encode( $payload ) . ';',
195 'before'
196 );
197 } );
198 }
199
200 /**
201 * Whether a post type is a form-builder CPT. Editor-mode gate is
202 * `wppb_fb_is_active_for()`; use this for behavior in both editor modes.
203 */
204 function wppb_fb_is_form_cpt( $post_type ) {
205 return in_array( $post_type, array( 'wppb-rf-cpt', 'wppb-epf-cpt' ), true );
206 }
207
208 /** Disables remote block patterns for the form editor. */
209 add_filter( 'should_load_remote_block_patterns', 'wppb_fb_disable_remote_patterns' );
210 function wppb_fb_disable_remote_patterns( $should_load ) {
211 return wppb_fb_is_form_cpt( get_post_type() ) ? false : $should_load;
212 }
213
214 /** Disables all block patterns in the editor settings for the form CPTs. */
215 add_filter( 'block_editor_settings_all', 'wppb_fb_disable_patterns_in_settings', 10, 2 );
216 function wppb_fb_disable_patterns_in_settings( $settings, $context ) {
217 if ( ! wppb_fb_is_form_cpt( $context->post->post_type ) ) {
218 return $settings;
219 }
220
221 $settings['__experimentalBlockPatterns'] = array();
222 $settings['__experimentalBlockPatternCategories'] = array();
223
224 return $settings;
225 }
226
227 // 2. Block category registration
228
229 /**
230 * Registers the PB block categories, mirroring classic Manage Fields' optgroups.
231 */
232 add_filter( 'block_categories_all', 'wppb_fb_register_block_category', 10, 2 );
233 function wppb_fb_register_block_category( $categories, $context ) {
234 if ( empty( $context->post ) || ! wppb_fb_is_form_cpt( $context->post->post_type ) ) {
235 return $categories;
236 }
237 return array_merge(
238 $categories,
239 array(
240 // No category icon — Gutenberg duplicates per-block icons.
241 array(
242 'slug' => 'profile-builder-default',
243 'title' => __( 'Default', 'profile-builder' ),
244 ),
245 array(
246 'slug' => 'profile-builder-standard',
247 'title' => __( 'Standard', 'profile-builder' ),
248 ),
249 array(
250 'slug' => 'profile-builder-advanced',
251 'title' => __( 'Advanced', 'profile-builder' ),
252 ),
253 array(
254 'slug' => 'profile-builder-other',
255 'title' => __( 'Other', 'profile-builder' ),
256 ),
257 array(
258 'slug' => 'profile-builder-structural',
259 'title' => __( 'Structural', 'profile-builder' ),
260 ),
261 )
262 );
263 }
264
265 // 3. Editor gating contracts (filterable getters)
266
267 /**
268 * PB field types that cannot HOST a conditional-logic rule (the CL panel isn't
269 * shown for them in the block editor). Mirrors the legacy admin JS
270 * `disabled_fields` array.
271 */
272 function wppb_fb_cf_disabled_field_types() {
273 return apply_filters( 'wppb_fb_cf_disabled_field_types', array(
274 'Default - Username',
275 'Default - E-mail',
276 'Default - Password',
277 'Default - Repeat Password',
278 'reCAPTCHA',
279 'Turnstile',
280 'Heading',
281 'HTML',
282 'Honeypot',
283 ) );
284 }
285
286 /**
287 * PB field types that may appear at most once per form. Prefers the legacy
288 * `wppb_return_unique_field_list()` when it's loaded, so the contract and its
289 * `wppb_unique_field_list` filter have one source.
290 */
291 function wppb_fb_unique_field_types() {
292 if ( function_exists( 'wppb_return_unique_field_list' ) ) {
293 $list = wppb_return_unique_field_list();
294 } else {
295 /*
296 * Front-end path when manage-fields.php is not loaded. Must match
297 * wppb_return_unique_field_list() exactly or sites diverge by request.
298 */
299 $list = array(
300 'Default - Name (Heading)',
301 'Default - Contact Info (Heading)',
302 'Default - About Yourself (Heading)',
303 'Default - Username',
304 'Default - First Name',
305 'Default - Last Name',
306 'Default - Nickname',
307 'Default - E-mail',
308 'Default - Website',
309 );
310
311 // Default contact methods were removed in WP 3.6.
312 if ( apply_filters( 'wppb_remove_default_contact_methods', get_site_option( 'initial_db_version' ) < 23588 ) ) {
313 $list[] = 'Default - AIM';
314 $list[] = 'Default - Yahoo IM';
315 $list[] = 'Default - Jabber / Google Talk';
316 }
317
318 $list[] = 'Default - Password';
319 $list[] = 'Default - Repeat Password';
320 $list[] = 'Default - Biographical Info';
321 $list[] = 'Default - Display name publicly as';
322 $list[] = 'GDPR Checkbox';
323 $list[] = 'Email Confirmation';
324
325 if ( function_exists( 'wppb_can_users_signup_blog' ) && wppb_can_users_signup_blog() ) {
326 $list[] = 'Default - Blog Details';
327 }
328
329 $list[] = 'Avatar';
330 $list[] = 'reCAPTCHA';
331 $list[] = 'Turnstile';
332 $list[] = 'Select (User Role)';
333 $list[] = 'Map';
334
335 // Apply the legacy filter this branch used to skip.
336 $list = apply_filters( 'wppb_unique_field_list', $list );
337 }
338 return apply_filters( 'wppb_fb_unique_field_types', array_values( $list ) );
339 }
340
341 /** Groups of mutually exclusive field types: at most one per group, per form. */
342 function wppb_fb_exclusive_field_groups() {
343 return apply_filters( 'wppb_fb_exclusive_field_groups', array(
344 array( 'reCAPTCHA', 'Turnstile' ),
345 ) );
346 }
347
348 // 4. Block registration (init priority 30)
349
350 add_action( 'init', 'wppb_fb_register_blocks', 30 );
351 function wppb_fb_register_blocks() {
352 if ( ! function_exists( 'register_block_type' ) ) return;
353
354 $asset_file = __DIR__ . '/blocks/build/index.asset.php';
355 if ( ! file_exists( $asset_file ) ) return;
356
357 $assets = include( $asset_file );
358 $handle = 'wppb-form-editor-bundle';
359
360 wp_register_script(
361 $handle,
362 WPPB_PLUGIN_URL . 'form-builder/blocks/build/index.js',
363 $assets['dependencies'],
364 $assets['version']
365 );
366
367 wp_register_style(
368 $handle,
369 WPPB_PLUGIN_URL . 'form-builder/blocks/build/style-index.css',
370 array(),
371 $assets['version']
372 );
373
374 $blocks_dir = __DIR__ . '/blocks/src/blocks';
375 if ( ! is_dir( $blocks_dir ) ) return;
376
377 $cf_disabled = wppb_fb_cf_disabled_field_types();
378 $no_meta_name_types = wppb_fb_no_meta_name_field_types();
379 $default_meta_map = wppb_fb_default_field_meta_names();
380 $unique_types = wppb_fb_unique_field_types();
381
382 $it = new DirectoryIterator( $blocks_dir );
383 foreach ( $it as $fileinfo ) {
384 if ( $fileinfo->isDir() && ! $fileinfo->isDot() ) {
385 $block_json_path = $fileinfo->getPathname() . '/block.json';
386 // Shared registry parse cache; returned copy so mutations below are safe.
387 $metadata = WPPB_FB_Field_Registry::read_block_json( $block_json_path );
388 if ( ! $metadata || ! isset( $metadata['name'] ) ) continue;
389
390 $metadata['editor_script'] = $handle;
391 $metadata['editor_style'] = $handle;
392
393 if ( ! isset( $metadata['attributes'] ) || ! is_array( $metadata['attributes'] ) ) {
394 $metadata['attributes'] = array();
395 }
396
397 // Legacy renderer ignores anchor/CSS class; hide Advanced panel support.
398 if ( ! isset( $metadata['supports'] ) || ! is_array( $metadata['supports'] ) ) {
399 $metadata['supports'] = array();
400 }
401 $metadata['supports']['customClassName'] = false;
402 $metadata['supports']['anchor'] = false;
403
404 // Structural blocks skip field attribute injection.
405 $is_field_block = strpos( $metadata['name'], 'profile-builder/field-' ) === 0;
406 if ( ! $is_field_block ) {
407 register_block_type( $metadata['name'], $metadata );
408 continue;
409 }
410
411 $field_type = WPPB_FB_Field_Registry::get_field_type_from_block_name( $metadata['name'], $metadata );
412
413 $metadata['attributes'] = wppb_fb_compute_field_attributes(
414 $metadata['attributes'],
415 $field_type,
416 $metadata['name'],
417 array(
418 'cf_disabled' => $cf_disabled,
419 'default_meta_map' => $default_meta_map,
420 'no_meta_name_types' => $no_meta_name_types,
421 'context_label' => 'register',
422 )
423 );
424
425 // supports.multiple=false gates inserter/duplicate; server still re-checks.
426 if ( in_array( $field_type, $unique_types, true ) ) {
427 if ( ! isset( $metadata['supports'] ) || ! is_array( $metadata['supports'] ) ) {
428 $metadata['supports'] = array();
429 }
430 $metadata['supports']['multiple'] = false;
431 }
432
433 register_block_type( $metadata['name'], $metadata );
434 }
435 }
436 }
437
438 // 5. Editor assets + inline-script bridges
439
440 add_action( 'enqueue_block_editor_assets', 'wppb_fb_enqueue_editor_assets' );
441 function wppb_fb_enqueue_editor_assets() {
442 $post_type = get_post_type();
443 if ( ! wppb_fb_is_form_cpt( $post_type ) ) {
444 return;
445 }
446 wp_enqueue_script( 'wppb-form-editor-bundle' );
447 wp_enqueue_style( 'wppb-form-editor-bundle' );
448
449 $registry = WPPB_FB_Field_Registry::all();
450 $block_to_field_type = array();
451 foreach ( $registry as $field_type => $entry ) {
452 $block_to_field_type[ $entry['block'] ] = $field_type;
453 }
454
455 // Field types that cannot be rule sources (legacy wppb_conditional_fields_not_allowed).
456 $not_allowed_as_source = apply_filters( 'wppb_conditional_fields_not_allowed', array(
457 'Default - Name (Heading)',
458 'Default - Contact Info (Heading)',
459 'Default - About Yourself (Heading)',
460 'Default - Password',
461 'Default - Repeat Password',
462 'Heading',
463 'WYSIWYG',
464 'Checkbox (Terms and Conditions)',
465 'Upload',
466 'Avatar',
467 'reCAPTCHA',
468 'Turnstile',
469 'HTML',
470 'Map',
471 ), array() );
472
473 // Fresh each load so new post types / taxonomies appear.
474 $reserved = function_exists( 'wppb_get_reserved_meta_name_list' )
475 ? wppb_get_reserved_meta_name_list( wppb_fb_manage_fields(), array() )
476 : array();
477
478 $default_meta_map = wppb_fb_default_field_meta_names();
479
480 // Exclusive groups need an editor Notice; Gutenberg has no native mutual-exclusion.
481 $unique_types = wppb_fb_unique_field_types();
482 $unique_block_names = array();
483 foreach ( $unique_types as $field_type ) {
484 if ( isset( $registry[ $field_type ]['block'] ) ) {
485 $unique_block_names[] = $registry[ $field_type ]['block'];
486 }
487 }
488
489 $exclusive_groups = wppb_fb_exclusive_field_groups();
490 $exclusive_block_groups = array();
491 foreach ( $exclusive_groups as $group ) {
492 $block_group = array();
493 foreach ( $group as $field_type ) {
494 if ( isset( $registry[ $field_type ]['block'] ) ) {
495 $block_group[] = $registry[ $field_type ]['block'];
496 }
497 }
498 if ( count( $block_group ) > 1 ) {
499 $exclusive_block_groups[] = $block_group;
500 }
501 }
502
503 // Role list for Set Role (RF) and Repeater row limits; legacy WCK order.
504 global $wp_roles;
505 $roles_assoc = ( $wp_roles && isset( $wp_roles->roles ) ) ? $wp_roles->roles : array();
506
507 $ordered = array();
508 foreach ( array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ) as $slug ) {
509 if ( isset( $roles_assoc[ $slug ] ) ) {
510 $ordered[ $slug ] = $roles_assoc[ $slug ];
511 unset( $roles_assoc[ $slug ] );
512 }
513 }
514 foreach ( $roles_assoc as $slug => $role ) {
515 $ordered[ $slug ] = $role;
516 }
517
518 $role_options = array(
519 array( 'label' => __( 'Default Role', 'profile-builder' ), 'value' => 'default role' ),
520 );
521 foreach ( $ordered as $slug => $role ) {
522 $role_options[] = array(
523 'label' => translate_user_role( $role['name'] ),
524 'value' => $slug,
525 );
526 }
527
528 // User Role field list: same order, no sentinel, no administrator.
529 $user_role_field_options = array();
530 foreach ( $ordered as $slug => $role ) {
531 if ( 'administrator' === $slug ) {
532 continue;
533 }
534 $user_role_field_options[] = array(
535 'label' => translate_user_role( $role['name'] ),
536 'value' => $slug,
537 );
538 }
539
540 // loginWith=email strips Username on front end; editor shows a Notice.
541 $general = get_option( 'wppb_general_settings', array() );
542 $login_with = ( is_array( $general ) && isset( $general['loginWith'] ) ) ? (string) $general['loginWith'] : 'usernameemail';
543
544 // Select option values must match classic admin encoding (ISO country/currency,
545 // timezone string as value+label, CPT/taxonomy slugs).
546 $select_field_options = array(
547 'countries' => array(),
548 'currencies' => array(),
549 'timezones' => array(),
550 'postTypes' => array(),
551 'taxonomies' => array(),
552 );
553
554 if ( function_exists( 'wppb_country_select_options' ) ) {
555 foreach ( wppb_country_select_options( 'back_end' ) as $iso => $name ) {
556 $select_field_options['countries'][] = array(
557 'label' => $name,
558 'value' => (string) $iso,
559 );
560 }
561 }
562
563 if ( function_exists( 'wppb_get_currencies' ) ) {
564 $select_field_options['currencies'][] = array(
565 'label' => __( '— Select a Currency —', 'profile-builder' ),
566 'value' => '',
567 );
568 foreach ( wppb_get_currencies( 'back_end' ) as $iso => $name ) {
569 $select_field_options['currencies'][] = array(
570 'label' => $name,
571 'value' => (string) $iso,
572 );
573 }
574 }
575
576 if ( function_exists( 'wppb_timezone_select_options' ) ) {
577 $select_field_options['timezones'][] = array(
578 'label' => __( '— Select a Timezone —', 'profile-builder' ),
579 'value' => '',
580 );
581 foreach ( wppb_timezone_select_options( 'back_end' ) as $tz ) {
582 $select_field_options['timezones'][] = array(
583 'label' => (string) $tz,
584 'value' => (string) $tz,
585 );
586 }
587 }
588
589 // Public post types/taxonomies; defaults match classic (post/category).
590 foreach ( get_post_types( array( 'public' => true ), 'objects' ) as $pt ) {
591 if ( isset( $pt->label ) ) {
592 $select_field_options['postTypes'][] = array(
593 'label' => $pt->label,
594 'value' => $pt->name,
595 );
596 }
597 }
598 foreach ( get_taxonomies( array( 'public' => true ), 'objects' ) as $tax ) {
599 if ( isset( $tax->label ) ) {
600 $select_field_options['taxonomies'][] = array(
601 'label' => $tax->label,
602 'value' => $tax->name,
603 );
604 }
605 }
606
607 // One `window.wppbFb` snapshot per load (install/config only). Add-ons merge via
608 // integrations/; existingFields.fields is the lone live-read exception.
609 $bridge = array(
610 'conditionalFields' => array(
611 // Conditional fields panel is paid-only.
612 'available' => wppb_fb_conditional_logic_available(),
613 'blockToFieldType' => $block_to_field_type,
614 'notAllowedAsSource' => array_values( $not_allowed_as_source ),
615 'disabledFieldTypes' => array_values( wppb_fb_cf_disabled_field_types() ),
616 'numericFieldTypes' => array( 'Input', 'Textarea', 'Number' ),
617 'restRoot' => esc_url_raw( rest_url( 'wppb/v1/cf-source-options' ) ),
618 'restNonce' => wp_create_nonce( 'wp_rest' ),
619 ),
620 'metaNames' => array(
621 'defaultFieldTypes' => array_keys( $default_meta_map ),
622 'defaultMetaNames' => $default_meta_map,
623 'fixedMetaNameFieldTypes' => array_keys( wppb_fb_fixed_meta_name_field_types() ),
624 'noMetaNameFieldTypes' => array_values( wppb_fb_no_meta_name_field_types() ),
625 'noOverwriteFieldTypes' => array_values( wppb_fb_no_overwrite_existing_field_types() ),
626 'reservedNames' => array_values( $reserved ),
627 'reservedSubstrings' => array( 'map' ),
628 'customFieldPrefix' => 'custom_field_',
629 'maxLength' => 255,
630 'uploadFieldType' => 'Upload',
631 ),
632 'allocate' => array(
633 'restRoot' => esc_url_raw( rest_url( 'wppb/v1/allocate-field-id' ) ),
634 'restNonce' => wp_create_nonce( 'wp_rest' ),
635 ),
636 'existingFields' => array(
637 'fields' => wppb_fb_existing_fields_bridge(),
638 ),
639 'uniqueness' => array(
640 'uniqueBlocks' => $unique_block_names,
641 'exclusiveBlockGroups' => $exclusive_block_groups,
642 ),
643 'formSettings' => array(
644 'roles' => $role_options,
645 ),
646 'userRoleField' => array(
647 'roles' => $user_role_field_options,
648 ),
649 'selectFields' => $select_field_options,
650 'generalSettings' => array(
651 'loginWith' => $login_with,
652 ),
653 'repeater' => array(
654 'parentBlockName' => 'profile-builder/field-repeater',
655 'allowedBlocks' => wppb_fb_repeater_allowed_block_names(),
656 ),
657 // Honors filtered wppb_fb_mandatory_registration_field_types.
658 'mandatoryTypes' => array_values( wppb_fb_mandatory_registration_field_types() ),
659 'preview' => array(
660 'home' => esc_url_raw( home_url( '/' ) ),
661 'nonce' => wp_create_nonce( 'wppb_fb_preview' ),
662 ),
663 );
664
665 wp_add_inline_script(
666 'wppb-form-editor-bundle',
667 'window.wppbFb = Object.assign( window.wppbFb || {}, ' . wp_json_encode( $bridge ) . ' );',
668 'before'
669 );
670
671 do_action( 'wppb_fb_enqueue_editor_assets_late' );
672 }
673
674 /**
675 * Deep-merge add-on schemas into `window.wppbFb.extraAttributes`.
676 * Flat assign would drop keys when two bridges share a field type.
677 *
678 * @param array $payload_by_field_type PB field-type label => `{ attrSlug => schema }`.
679 */
680 function wppb_fb_extra_attributes_inline_script( array $payload_by_field_type ): void {
681 if ( empty( $payload_by_field_type ) ) return;
682 if ( ! wp_script_is( 'wppb-form-editor-bundle', 'registered' ) ) return;
683
684 $iife = '(function(extra){'
685 . 'window.wppbFb=window.wppbFb||{};'
686 . 'window.wppbFb.extraAttributes=window.wppbFb.extraAttributes||{};'
687 . 'for(var ft in extra){'
688 . 'var prev=window.wppbFb.extraAttributes[ft];'
689 . 'window.wppbFb.extraAttributes[ft]=Object.assign('
690 . '(typeof prev==="object"&&prev!==null)?prev:{},'
691 . 'extra[ft]'
692 . ');'
693 . '}'
694 . '})(' . wp_json_encode( $payload_by_field_type ) . ');';
695
696 wp_add_inline_script( 'wppb-form-editor-bundle', $iife, 'before' );
697 }
698
699 /**
700 * All PB field-type labels for bridge payloads.
701 *
702 * Safe from `wppb_fb_enqueue_editor_assets_late`. Do not call from
703 * `wppb_fb_block_attributes` — recurses into registry construction.
704 *
705 * @return string[] Field-type labels.
706 */
707 function wppb_fb_all_field_type_labels(): array {
708 if ( ! class_exists( 'WPPB_FB_Field_Registry' ) ) return array();
709 return array_keys( WPPB_FB_Field_Registry::all() );
710 }
711
712 /**
713 * Register a generic Additional Settings control on `window.wppbFb.extraControls`.
714 * Pairs with `wppb_fb_block_attributes` + `wppb_fb_extra_attributes_inline_script()`.
715 * Repeat calls de-dupe by attribute (last wins).
716 *
717 * @param array $control {
718 * @type string $attribute Block attribute slug. Required.
719 * @type string $label Control label.
720 * @type string $help Help text.
721 * @type string $control 'text' (default) | 'number'.
722 * }
723 */
724 function wppb_fb_register_extra_field_control( array $control ): void {
725 if ( empty( $control['attribute'] ) ) return;
726 if ( ! wp_script_is( 'wppb-form-editor-bundle', 'registered' ) ) return;
727
728 $iife = '(function(c){'
729 . 'window.wppbFb=window.wppbFb||{};'
730 . 'window.wppbFb.extraControls=window.wppbFb.extraControls||[];'
731 . 'window.wppbFb.extraControls.push(c);'
732 . '})(' . wp_json_encode( $control ) . ');';
733
734 wp_add_inline_script( 'wppb-form-editor-bundle', $iife, 'before' );
735 }
736