PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.3
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.3
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-defaults.php

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

347 lines 12.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ensures the two default forms exist and stay non-deletable.
4 * Seeded with the full field list on create; then behave like any other form.
5 */
6
7 if ( ! defined( 'ABSPATH' ) ) exit;
8
9 add_action( 'init', 'wppb_fb_ensure_default_forms', 20 );
10 function wppb_fb_ensure_default_forms() {
11 /*
12 * Admin/cron/CLI only: concurrent anonymous init writers race on manage_fields,
13 * and post_status=>any adoption needs edit caps (else a second default is created).
14 */
15 if ( ! is_admin() && ! wp_doing_cron() && ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
16 return;
17 }
18
19 $defaults = get_option( 'wppb_default_form_ids', array() );
20 $original = $defaults;
21
22 // Adopt an existing default-flagged form before creating one.
23 $defaults['register'] = wppb_fb_resolve_default_form_slot( 'register', isset( $defaults['register'] ) ? (int) $defaults['register'] : 0 );
24 $defaults['edit_profile'] = wppb_fb_resolve_default_form_slot( 'edit_profile', isset( $defaults['edit_profile'] ) ? (int) $defaults['edit_profile'] : 0 );
25
26 $repaired = ( $defaults !== $original );
27 if ( $repaired ) {
28 update_option( 'wppb_default_form_ids', $defaults );
29 }
30
31 wppb_fb_dedupe_default_forms( $defaults, $repaired );
32 wppb_fb_migrate_legacy_forms_seed_fields();
33 wppb_fb_prune_incomplete_conditional_logic_rules();
34 }
35
36 /**
37 * One-time prune of CL rules with no source field (placeholder kills whole show/hide).
38 * Flag-gated; skips Repeater sub-fields (CL stripped on write anyway).
39 */
40 function wppb_fb_prune_incomplete_conditional_logic_rules() {
41 if ( get_option( 'wppb_fb_cl_incomplete_rules_pruned' ) === '1' ) return;
42 if ( ! function_exists( 'wppb_fb_cl_rule_has_source' ) ) return;
43
44 $manage = wppb_fb_manage_fields();
45 if ( is_array( $manage ) && ! empty( $manage ) ) {
46 $changed = false;
47 foreach ( $manage as $key => $row ) {
48 if ( ! is_array( $row ) || empty( $row['conditional-logic'] ) ) continue;
49 if ( ! is_string( $row['conditional-logic'] ) ) continue;
50
51 $data = json_decode( $row['conditional-logic'], true );
52 if ( ! is_array( $data ) || ! isset( $data['rules'] ) || ! is_array( $data['rules'] ) ) continue;
53
54 $clean = array();
55 foreach ( $data['rules'] as $rule ) {
56 if ( wppb_fb_cl_rule_has_source( $rule ) ) {
57 $clean[] = $rule;
58 }
59 }
60 if ( count( $clean ) === count( $data['rules'] ) ) continue;
61
62 $data['rules'] = $clean;
63
64 $manage[ $key ]['conditional-logic'] = wp_json_encode( $data );
65 $changed = true;
66 }
67 if ( $changed ) {
68 update_option( 'wppb_manage_fields', $manage );
69 }
70 }
71
72 update_option( 'wppb_fb_cl_incomplete_rules_pruned', '1' );
73 }
74
75 /**
76 * Canonical default-form post ID: tracked if valid, else adopt oldest flagged, else create.
77 *
78 * @param string $type 'register' | 'edit_profile'.
79 * @param int $tracked_id Currently tracked ID for the slot (0 if none).
80 * @return int Post ID (0 only if creation failed).
81 */
82 function wppb_fb_resolve_default_form_slot( $type, $tracked_id ) {
83 $post_type = $type === 'register' ? 'wppb-rf-cpt' : 'wppb-epf-cpt';
84
85 if ( $tracked_id > 0 ) {
86 $post = get_post( $tracked_id );
87 if ( $post
88 && $post->post_type === $post_type
89 && $post->post_status !== 'trash'
90 && get_post_meta( $tracked_id, '_pbform_is_default', true ) === '1' ) {
91 return $tracked_id;
92 }
93 }
94
95 $existing = get_posts( array(
96 'post_type' => $post_type,
97 'post_status' => 'any',
98 'numberposts' => 1,
99 'orderby' => 'ID',
100 'order' => 'ASC',
101 'fields' => 'ids',
102 'meta_key' => '_pbform_is_default',
103 'meta_value' => '1',
104 'no_found_rows' => true,
105 ) );
106 if ( ! empty( $existing ) ) {
107 return (int) $existing[0];
108 }
109
110 $new_id = wppb_fb_create_default_form( $type );
111 return $new_id ? (int) $new_id : 0;
112 }
113
114 /**
115 * Demote other defaults; do not delete their posts.
116 * Runs only when a slot was repaired ($force) or a stray flag armed the pending option.
117 *
118 * @param array $defaults Resolved slot => post ID map.
119 * @param bool $force Run even when nothing armed the pending flag.
120 * @return void
121 */
122 function wppb_fb_dedupe_default_forms( $defaults, $force = false ) {
123 if ( ! $force && get_option( 'wppb_fb_default_forms_dedupe_pending' ) !== '1' ) {
124 return;
125 }
126
127 $slots = array(
128 'wppb-rf-cpt' => isset( $defaults['register'] ) ? (int) $defaults['register'] : 0,
129 'wppb-epf-cpt' => isset( $defaults['edit_profile'] ) ? (int) $defaults['edit_profile'] : 0,
130 );
131
132 foreach ( $slots as $post_type => $keep ) {
133 if ( ! $keep ) continue;
134
135 $flagged = get_posts( array(
136 'post_type' => $post_type,
137 'post_status' => 'any',
138 'numberposts' => -1,
139 'fields' => 'ids',
140 'meta_key' => '_pbform_is_default',
141 'meta_value' => '1',
142 'no_found_rows' => true,
143 ) );
144
145 foreach ( $flagged as $flagged_id ) {
146 if ( (int) $flagged_id !== $keep ) {
147 delete_post_meta( $flagged_id, '_pbform_is_default' );
148 }
149 }
150 }
151
152 delete_option( 'wppb_fb_default_forms_dedupe_pending' );
153 }
154
155 add_action( 'added_post_meta', 'wppb_fb_rearm_default_forms_dedupe', 10, 4 );
156 add_action( 'updated_post_meta', 'wppb_fb_rearm_default_forms_dedupe', 10, 4 );
157 /**
158 * Arm dedupe when `_pbform_is_default` is set on a non-tracked form (import/duplicate).
159 * Arm only — demote needs the resolved slot map and may run after several writes.
160 *
161 * @param int $meta_id Unused.
162 * @param int $post_id Post the meta was written to.
163 * @param string $meta_key Meta key written.
164 * @param mixed $meta_value Value written.
165 * @return void
166 */
167 function wppb_fb_rearm_default_forms_dedupe( $meta_id, $post_id, $meta_key, $meta_value ) {
168 if ( $meta_key !== '_pbform_is_default' ) return;
169 if ( (string) $meta_value !== '1' ) return;
170
171 $post_type = get_post_type( $post_id );
172 if ( $post_type !== 'wppb-rf-cpt' && $post_type !== 'wppb-epf-cpt' ) return;
173
174 $defaults = get_option( 'wppb_default_form_ids', array() );
175 $slot = $post_type === 'wppb-rf-cpt' ? 'register' : 'edit_profile';
176 $tracked = ( is_array( $defaults ) && isset( $defaults[ $slot ] ) ) ? (int) $defaults[ $slot ] : 0;
177 if ( (int) $post_id === $tracked ) return;
178
179 update_option( 'wppb_fb_default_forms_dedupe_pending', '1' );
180 }
181
182 /**
183 * One-time seed for pre-rework forms with an empty ordered list (no read-path fallback).
184 * Flag is per-CPT: classic mode skips without burning, so a later switch still seeds.
185 */
186 function wppb_fb_migrate_legacy_forms_seed_fields() {
187 foreach ( array( 'wppb-rf-cpt', 'wppb-epf-cpt' ) as $post_type ) {
188 $flag = 'wppb_fb_legacy_forms_seeded_' . $post_type;
189 if ( get_option( $flag ) === '1' ) continue;
190
191 if ( ! wppb_fb_is_active_for( $post_type ) ) continue;
192
193 $meta_key = $post_type === 'wppb-rf-cpt' ? 'wppb_rf_fields' : 'wppb_epf_fields';
194
195 $ids = get_posts( array(
196 'post_type' => $post_type,
197 'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future' ),
198 'posts_per_page' => -1,
199 'fields' => 'ids',
200 'no_found_rows' => true,
201 ) );
202
203 foreach ( $ids as $post_id ) {
204 $existing = get_post_meta( $post_id, $meta_key, true );
205 if ( is_array( $existing ) && ! empty( $existing ) ) continue;
206 wppb_fb_seed_form_fields_from_registry( $post_id, $post_type );
207 }
208
209 update_option( $flag, '1' );
210 }
211 }
212
213 /**
214 * One ordered-list entry in the projection's "Title ( Type )" shape.
215 *
216 * @param string $title Field title.
217 * @param string $type PB field-type label.
218 * @param int $id Field id.
219 * @return array{field:string,id:int}
220 */
221 function wppb_fb_format_form_field_entry( $title, $type, $id ) {
222 $field = function_exists( 'wppb_field_format' )
223 ? wppb_field_format( $title, $type )
224 : ( $title . ' ( ' . $type . ' )' );
225 return array(
226 'field' => $field,
227 'id' => (int) $id,
228 );
229 }
230
231 /**
232 * Seed per-form field list from the registry (same exclusions as legacy auto-draft seeder).
233 */
234 function wppb_fb_seed_form_fields_from_registry( $post_id, $post_type ) {
235 $all = wppb_fb_manage_fields();
236 if ( ! is_array( $all ) || empty( $all ) ) return;
237
238 $list = array();
239 foreach ( $all as $row ) {
240 if ( ! isset( $row['field'], $row['id'] ) ) continue;
241
242 $field_type = (string) $row['field'];
243 $field_title = isset( $row['field-title'] ) ? (string) $row['field-title'] : '';
244
245 if ( $post_type === 'wppb-rf-cpt' && strpos( $field_type, 'Display name publicly as' ) !== false ) continue;
246 if ( $post_type === 'wppb-epf-cpt' && ( $field_type === 'reCAPTCHA' || $field_type === 'Validation' ) ) continue;
247
248 $list[] = wppb_fb_format_form_field_entry( $field_title, $field_type, $row['id'] );
249 }
250
251 $meta_key = $post_type === 'wppb-rf-cpt' ? 'wppb_rf_fields' : 'wppb_epf_fields';
252 update_post_meta( $post_id, $meta_key, $list );
253 }
254
255 /**
256 * Create a default form post and seed its field list from the registry.
257 */
258 function wppb_fb_create_default_form( $type ) {
259 $config = array(
260 'register' => array(
261 'title' => __( 'Default Registration', 'profile-builder' ),
262 'post_type' => 'wppb-rf-cpt',
263 ),
264 'edit_profile' => array(
265 'title' => __( 'Default Edit Profile', 'profile-builder' ),
266 'post_type' => 'wppb-epf-cpt',
267 ),
268 );
269
270 if ( ! isset( $config[ $type ] ) ) return false;
271 $c = $config[ $type ];
272
273 $post_id = wp_insert_post( array(
274 'post_title' => $c['title'],
275 'post_type' => $c['post_type'],
276 'post_status' => 'publish',
277 ) );
278
279 if ( ! $post_id || is_wp_error( $post_id ) ) return false;
280
281 update_post_meta( $post_id, '_pbform_is_default', '1' );
282
283 wppb_fb_seed_form_fields_from_registry( $post_id, $c['post_type'] );
284
285 return $post_id;
286 }
287
288 /**
289 * Seed new Registration forms with Username / E-mail / Password at canonical ids.
290 * Priority 25: projection at 20 would write an empty list first and clobber this.
291 */
292 add_action( 'save_post_wppb-rf-cpt', 'wppb_fb_seed_new_form_mandatory_fields', 25, 2 );
293 function wppb_fb_seed_new_form_mandatory_fields( $post_id, $post ) {
294 if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) return;
295 if ( ! wppb_fb_is_active_for( $post->post_type ) ) return;
296 if ( $post->post_status !== 'auto-draft' ) return;
297 if ( get_post_meta( $post_id, '_pbform_is_default', true ) === '1' ) return;
298
299 $existing = get_post_meta( $post_id, 'wppb_rf_fields', true );
300 if ( is_array( $existing ) && ! empty( $existing ) ) return;
301
302 $manage = wppb_fb_manage_fields();
303 if ( ! is_array( $manage ) ) return;
304
305 $mandatory_types = wppb_fb_mandatory_registration_field_types();
306
307 // First occurrence of each mandatory type is canonical.
308 $canonical_by_type = array();
309 foreach ( $manage as $row ) {
310 if ( ! isset( $row['field'], $row['id'] ) ) continue;
311 $type = (string) $row['field'];
312 if ( ! in_array( $type, $mandatory_types, true ) ) continue;
313 if ( isset( $canonical_by_type[ $type ] ) ) continue;
314 $canonical_by_type[ $type ] = $row;
315 }
316
317 $seeded = array();
318 foreach ( $mandatory_types as $type ) {
319 if ( ! isset( $canonical_by_type[ $type ] ) ) continue;
320 $row = $canonical_by_type[ $type ];
321 $title = isset( $row['field-title'] ) ? (string) $row['field-title'] : str_replace( 'Default - ', '', $type );
322 $seeded[] = wppb_fb_format_form_field_entry( $title, $type, $row['id'] );
323 }
324
325 if ( empty( $seeded ) ) return;
326
327 update_post_meta( $post_id, 'wppb_rf_fields', $seeded );
328
329 // Do not write block markup to post_content: isCleanNewPost / autosaves blank it.
330 // Editor useAutoSeedRegistrationForm inserts the three blocks on first paint.
331 }
332
333 add_filter( 'map_meta_cap', 'wppb_fb_protect_default_forms_caps', 10, 4 );
334 function wppb_fb_protect_default_forms_caps( $caps, $cap, $user_id, $args ) {
335 if ( $cap !== 'delete_post' || empty( $args[0] ) ) {
336 return $caps;
337 }
338 $post_id = (int) $args[0];
339 if ( ! in_array( get_post_type( $post_id ), array( 'wppb-rf-cpt', 'wppb-epf-cpt' ), true ) ) {
340 return $caps;
341 }
342 if ( get_post_meta( $post_id, '_pbform_is_default', true ) === '1' ) {
343 $caps[] = 'do_not_allow';
344 }
345 return $caps;
346 }
347