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 / integrations / form-fields-in-columns.php

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

342 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Form Fields in Columns ↔ Form Builder bridge.
4 *
5 * Harvests ffc-columns InnerBlocks into `wppb_ffc_break_points` on save, wraps
6 * ranges on REST read, and mirrors `ffc-enable` via `wppb_fb_ffc_enable`.
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) exit;
10
11 // Loaded by wppb_fb_load_addon_integrations(); do not include directly.
12
13 // Keep the Columns structural block in the inserter while FFC is active.
14 add_filter( 'wppb_fb_enabled_structural_blocks', function ( $blocks ) {
15 $blocks[] = 'profile-builder/ffc-columns';
16 return $blocks;
17 } );
18
19 /**
20 * Per-save state by post_id: start/end pairs + current open-wrapper field ids.
21 */
22 function &wppb_ffc_fb_state() {
23 static $state = array();
24 return $state;
25 }
26
27 /** Open a top-level ffc-columns accumulator; nested wrappers are ignored. */
28 add_action( 'wppb_fb_pb_block_walked', 'wppb_ffc_fb_open_wrapper', 10, 2 );
29 function wppb_ffc_fb_open_wrapper( $block, $context ) {
30 if ( empty( $block['blockName'] ) || $block['blockName'] !== 'profile-builder/ffc-columns' ) return;
31 if ( ! empty( $context['is_subfield'] ) ) return;
32 if ( empty( $context['post_id'] ) ) return;
33 if ( ! empty( $context['parent_block_name'] ) ) return;
34
35 $state = &wppb_ffc_fb_state();
36 $post_id = (int) $context['post_id'];
37 if ( ! isset( $state[ $post_id ] ) ) {
38 $state[ $post_id ] = array( 'pairs' => array(), 'current' => null );
39 }
40 $state[ $post_id ]['current'] = array();
41 }
42
43 add_action( 'wppb_fb_field_flattened', 'wppb_ffc_fb_track_inner_field', 10, 4 );
44 function wppb_ffc_fb_track_inner_field( $field_id, $attrs, $field_type, $context ) {
45 if ( ! empty( $context['is_subfield'] ) ) return;
46 if ( empty( $context['post_id'] ) ) return;
47 if ( ( isset( $context['parent_block_name'] ) ? $context['parent_block_name'] : '' ) !== 'profile-builder/ffc-columns' ) return;
48
49 $state = &wppb_ffc_fb_state();
50 $post_id = (int) $context['post_id'];
51 if ( ! isset( $state[ $post_id ] ) || ! is_array( $state[ $post_id ]['current'] ) ) return;
52 $state[ $post_id ]['current'][] = (int) $field_id;
53 }
54
55 /** Close wrapper; drop pairs with fewer than 2 children (legacy needs start+end). */
56 add_action( 'wppb_fb_pb_block_walked_done', 'wppb_ffc_fb_close_wrapper', 10, 2 );
57 function wppb_ffc_fb_close_wrapper( $block, $context ) {
58 if ( empty( $block['blockName'] ) || $block['blockName'] !== 'profile-builder/ffc-columns' ) return;
59 if ( ! empty( $context['is_subfield'] ) ) return;
60 if ( empty( $context['post_id'] ) ) return;
61
62 $state = &wppb_ffc_fb_state();
63 $post_id = (int) $context['post_id'];
64 if ( ! isset( $state[ $post_id ] ) || ! is_array( $state[ $post_id ]['current'] ) ) return;
65
66 $inner_ids = $state[ $post_id ]['current'];
67 $state[ $post_id ]['current'] = null;
68
69 if ( count( $inner_ids ) < 2 ) return;
70
71 $state[ $post_id ]['pairs'][] = array(
72 'start' => $inner_ids[0],
73 'end' => $inner_ids[ count( $inner_ids ) - 1 ],
74 );
75 }
76
77 /**
78 * Flush start/end pairs to wppb_ffc_break_points. Insertion order matters for legacy array_keys().
79 */
80 add_action( 'wppb_fb_post_project', 'wppb_ffc_fb_flush_break_points', 10, 3 );
81 function wppb_ffc_fb_flush_break_points( $post_id, $post, $context ) {
82 $state = &wppb_ffc_fb_state();
83 $entry = isset( $state[ $post_id ] ) ? $state[ $post_id ] : array( 'pairs' => array() );
84 $pairs = $entry['pairs'];
85
86 if ( empty( $pairs ) ) {
87 delete_post_meta( $post_id, 'wppb_ffc_break_points' );
88 } else {
89 $bp = array();
90 foreach ( $pairs as $p ) {
91 $bp[ $p['start'] ] = 'start';
92 $bp[ $p['end'] ] = 'end';
93 }
94 update_post_meta( $post_id, 'wppb_ffc_break_points', $bp );
95 }
96
97 unset( $state[ $post_id ] );
98 }
99
100 /** Wrap start/end field ranges from wppb_ffc_break_points in ffc-columns blocks. */
101 add_filter( 'wppb_fb_form_blocks', 'wppb_ffc_fb_inject_wrappers', 10, 3 );
102 function wppb_ffc_fb_inject_wrappers( $blocks, $post_id, $post_type ) {
103 if ( ! in_array( $post_type, array( 'wppb-rf-cpt', 'wppb-epf-cpt' ), true ) ) return $blocks;
104 $bp = get_post_meta( $post_id, 'wppb_ffc_break_points', true );
105 if ( ! is_array( $bp ) || empty( $bp ) ) return $blocks;
106
107 $out = array();
108 $i = 0;
109 $n = count( $blocks );
110
111 while ( $i < $n ) {
112 $block = $blocks[ $i ];
113 $id = isset( $block['attrs']['id'] ) ? (int) $block['attrs']['id'] : 0;
114
115 if ( $id > 0 && isset( $bp[ $id ] ) && $bp[ $id ] === 'start' ) {
116 $wrap = array( $block );
117 $j = $i + 1;
118 $found_end = false;
119 while ( $j < $n ) {
120 $b2 = $blocks[ $j ];
121 $id2 = isset( $b2['attrs']['id'] ) ? (int) $b2['attrs']['id'] : 0;
122 $wrap[] = $b2;
123 if ( $id2 > 0 && isset( $bp[ $id2 ] ) && $bp[ $id2 ] === 'end' ) {
124 $found_end = true;
125 $j++;
126 break;
127 }
128 $j++;
129 }
130
131 if ( $found_end ) {
132 $out[] = array(
133 'blockName' => 'profile-builder/ffc-columns',
134 'attrs' => array(),
135 'innerBlocks' => $wrap,
136 'innerHTML' => '',
137 // serialize_blocks needs one null per child or children are dropped.
138 'innerContent' => array_fill( 0, count( $wrap ), null ),
139 );
140 $i = $j;
141 } else {
142 foreach ( $wrap as $b ) $out[] = $b;
143 $i = $j;
144 }
145 } else {
146 $out[] = $block;
147 $i++;
148 }
149 }
150
151 return $out;
152 }
153
154 add_action( 'init', 'wppb_ffc_fb_register_virtual_meta' );
155 function wppb_ffc_fb_register_virtual_meta() {
156 foreach ( array( 'wppb-rf-cpt', 'wppb-epf-cpt' ) as $post_type ) {
157 // manage_options write gate; mirror writes into legacy storage.
158 register_post_meta( $post_type, 'wppb_fb_ffc_enable', array(
159 'show_in_rest' => true, 'single' => true, 'type' => 'string',
160 'sanitize_callback' => 'wppb_ffc_fb_sanitize_enable',
161 'auth_callback' => 'wppb_fb_virtual_meta_auth',
162 ) );
163 }
164 }
165
166 /** Normalize to 'yes'/'no' — legacy compares against the literal 'yes'. */
167
168 function wppb_ffc_fb_sanitize_enable( $value ) {
169 return $value === 'yes' ? 'yes' : 'no';
170 }
171
172 add_action( 'added_post_meta', 'wppb_ffc_fb_mirror_settings', 10, 4 );
173 add_action( 'updated_post_meta', 'wppb_ffc_fb_mirror_settings', 10, 4 );
174 function wppb_ffc_fb_mirror_settings( $meta_id, $post_id, $meta_key, $meta_value ) {
175 if ( $meta_key !== 'wppb_fb_ffc_enable' ) return;
176 if ( ! in_array( get_post_type( $post_id ), array( 'wppb-rf-cpt', 'wppb-epf-cpt' ), true ) ) return;
177
178 wppb_ffc_fb_write_enable( $post_id, is_string( $meta_value ) ? $meta_value : '' );
179
180 delete_post_meta( $post_id, 'wppb_fb_ffc_enable' );
181 }
182
183 /** Write the legacy per-form `ffc-enable` flag. */
184
185 function wppb_ffc_fb_write_enable( $post_id, $value ) {
186 $existing = get_post_meta( $post_id, 'wppb_ffc_post_options', true );
187 if ( ! is_array( $existing ) ) $existing = array();
188 $existing['ffc-enable'] = $value;
189 update_post_meta( $post_id, 'wppb_ffc_post_options', $existing );
190 }
191
192 /** Project ffc-enable onto REST (priority 12, after form-builder and MSF). */
193 add_filter( 'rest_prepare_wppb-rf-cpt', 'wppb_ffc_fb_inject_settings_into_response', 12, 3 );
194 add_filter( 'rest_prepare_wppb-epf-cpt', 'wppb_ffc_fb_inject_settings_into_response', 12, 3 );
195 function wppb_ffc_fb_inject_settings_into_response( $response, $post, $request ) {
196 if ( ! $response instanceof WP_REST_Response ) return $response;
197 $data = $response->get_data();
198 if ( ! isset( $data['meta'] ) || ! is_array( $data['meta'] ) ) {
199 $data['meta'] = array();
200 }
201
202 $opts = get_post_meta( (int) $post->ID, 'wppb_ffc_post_options', true );
203 $opts = is_array( $opts ) ? $opts : array();
204 $data['meta']['wppb_fb_ffc_enable'] = isset( $opts['ffc-enable'] ) ? (string) $opts['ffc-enable'] : '';
205
206 $response->set_data( $data );
207 return $response;
208 }
209
210 /** Field types the column wrapper rejects as children. */
211
212 function wppb_ffc_fb_disallowed_field_types() {
213 return apply_filters( 'wppb_ffc_fb_disallowed_field_types', array(
214 'Heading',
215 'HTML',
216 'Repeater',
217 'reCAPTCHA',
218 'Turnstile',
219 'Honeypot',
220 'Language',
221 'Default - Name (Heading)',
222 'Default - Contact Info (Heading)',
223 'Default - About Yourself (Heading)',
224 'Default - Blog Details',
225 ) );
226 }
227
228 add_action( 'enqueue_block_editor_assets', 'wppb_ffc_fb_enqueue_inline_bridge', 11 );
229 function wppb_ffc_fb_enqueue_inline_bridge() {
230 $post_type = get_post_type();
231 if ( ! in_array( $post_type, array( 'wppb-rf-cpt', 'wppb-epf-cpt' ), true ) ) return;
232 if ( ! wp_script_is( 'wppb-form-editor-bundle', 'registered' ) ) return;
233
234 $disallowed = wppb_ffc_fb_disallowed_field_types();
235 $registry = WPPB_FB_Field_Registry::all();
236 $allowed = array();
237 foreach ( $registry as $field_type => $entry ) {
238 if ( in_array( $field_type, $disallowed, true ) ) continue;
239 if ( isset( $entry['block'] ) ) $allowed[] = $entry['block'];
240 }
241
242 wp_add_inline_script(
243 'wppb-form-editor-bundle',
244 'window.wppbFb = window.wppbFb || {}; window.wppbFb.ffc = ' . wp_json_encode( array(
245 'allowedBlocks' => array_values( $allowed ),
246 'disallowedFieldTypes' => array_values( $disallowed ),
247 ) ) . ';',
248 'before'
249 );
250 }
251
252 /** Mirror classic option-level FFC state into default-form posts when the gate is 'yes'. */
253 function wppb_ffc_fb_mirror_options_to_default_forms() {
254 $defaults = get_option( 'wppb_default_form_ids', array() );
255 if ( ! is_array( $defaults ) || empty( $defaults ) ) return;
256
257 $opts = get_option( 'wppb_ffc_options', array() );
258 $bp = get_option( 'wppb_ffc_break_points', array() );
259
260 $opts = is_array( $opts ) ? $opts : array();
261 $bp = is_array( $bp ) ? $bp : array();
262
263 $gate_map = array(
264 'register' => 'pb-ffc-default-register',
265 'edit_profile' => 'pb-ffc-default-edit-profile',
266 );
267
268 foreach ( $gate_map as $key => $gate ) {
269 if ( empty( $defaults[ $key ] ) ) continue;
270 if ( empty( $opts ) || ( isset( $opts[ $gate ] ) ? $opts[ $gate ] : 'no' ) !== 'yes' ) continue;
271
272 $post_id = (int) $defaults[ $key ];
273
274 if ( ! empty( $bp ) ) {
275 update_post_meta( $post_id, 'wppb_ffc_break_points', $bp );
276 } else {
277 delete_post_meta( $post_id, 'wppb_ffc_break_points' );
278 }
279
280 wppb_ffc_fb_write_enable( $post_id, 'yes' );
281 }
282 }
283
284 /**
285 * Clear mirrored columns state on observed yes→off gate transition only
286 * (do not tear down forms configured purely in the new editor).
287 */
288 function wppb_ffc_fb_mirror_gate_disables( $old_value, $new_value ) {
289 $defaults = get_option( 'wppb_default_form_ids', array() );
290 if ( ! is_array( $defaults ) || empty( $defaults ) ) return;
291
292 $old = is_array( $old_value ) ? $old_value : array();
293 $new = is_array( $new_value ) ? $new_value : array();
294
295 $gate_map = array(
296 'register' => 'pb-ffc-default-register',
297 'edit_profile' => 'pb-ffc-default-edit-profile',
298 );
299
300 foreach ( $gate_map as $key => $gate ) {
301 if ( empty( $defaults[ $key ] ) ) continue;
302
303 $was_on = ( isset( $old[ $gate ] ) ? $old[ $gate ] : 'no' ) === 'yes';
304 $now_on = ( isset( $new[ $gate ] ) ? $new[ $gate ] : 'no' ) === 'yes';
305 if ( ! $was_on || $now_on ) continue;
306
307 $post_id = (int) $defaults[ $key ];
308 delete_post_meta( $post_id, 'wppb_ffc_break_points' );
309 wppb_ffc_fb_write_enable( $post_id, 'no' );
310 }
311 }
312
313 /**
314 * One-time migration after default-form slots exist (`wppb_default_form_ids`, init:25).
315 */
316 add_action( 'init', 'wppb_ffc_fb_migrate_to_post_meta', 25 );
317 function wppb_ffc_fb_migrate_to_post_meta() {
318 if ( get_option( 'wppb_ffc_fb_migrated' ) === '1' ) return;
319
320 $default_ids = get_option( 'wppb_default_form_ids', array() );
321 if ( ! is_array( $default_ids ) || empty( $default_ids['register'] ) || empty( $default_ids['edit_profile'] ) ) return;
322
323 wppb_ffc_fb_mirror_options_to_default_forms();
324 update_option( 'wppb_ffc_fb_migrated', '1' );
325 }
326
327 add_action( 'updated_option', 'wppb_ffc_fb_option_changed', 10, 3 );
328 add_action( 'added_option', 'wppb_ffc_fb_option_added', 10, 2 );
329
330 function wppb_ffc_fb_option_changed( $option, $old_value, $new_value ) {
331 if ( ! in_array( $option, array( 'wppb_ffc_options', 'wppb_ffc_break_points' ), true ) ) return;
332
333 if ( $option === 'wppb_ffc_options' ) {
334 wppb_ffc_fb_mirror_gate_disables( $old_value, $new_value );
335 }
336 wppb_ffc_fb_mirror_options_to_default_forms();
337 }
338 function wppb_ffc_fb_option_added( $option, $value ) {
339 if ( ! in_array( $option, array( 'wppb_ffc_options', 'wppb_ffc_break_points' ), true ) ) return;
340 wppb_ffc_fb_mirror_options_to_default_forms();
341 }
342