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-licensing.php

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

312 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Free vs paid form-builder gating (multiple forms + Pro-only field types).
4 * Entitlement uses PROFILE_BUILDER (active plugin), not serial status — an expired
5 * licence keeps unlocked features, matching other paid surfaces.
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) exit;
9
10 /**
11 * PROFILE_BUILDER values that unlock paid form-builder features (Pro and up).
12 *
13 * @return string[]
14 */
15 function wppb_fb_paid_versions() {
16 return apply_filters( 'wppb_fb_paid_versions', array(
17 'Profile Builder Pro',
18 'Profile Builder Agency',
19 'Profile Builder Unlimited',
20 'Profile Builder Dev',
21 ) );
22 }
23
24 /**
25 * Whether a paid (Pro and up) version of Profile Builder is the active plugin.
26 *
27 * @return bool
28 */
29 function wppb_fb_is_paid_version() {
30 return defined( 'PROFILE_BUILDER' ) && in_array( PROFILE_BUILDER, wppb_fb_paid_versions(), true );
31 }
32
33 /**
34 * Whether this install may create forms beyond the two defaults. Creation-only —
35 * existing forms keep working after a downgrade. Enforced via create_posts =>
36 * do_not_allow; direct wp_insert_post() is unchecked so default seeding still works.
37 *
38 * @return bool
39 */
40 function wppb_fb_multiple_forms_available() {
41 return (bool) apply_filters( 'wppb_fb_multiple_forms_available', wppb_fb_is_paid_version() );
42 }
43
44 /**
45 * CPT capabilities override: create_posts => do_not_allow when multiple forms locked.
46 *
47 * @return array<string,string> Empty when unlocked.
48 */
49 function wppb_fb_form_cpt_capability_overrides() {
50 if ( wppb_fb_multiple_forms_available() ) {
51 return array();
52 }
53 return array( 'create_posts' => 'do_not_allow' );
54 }
55
56 /**
57 * Pro-only field type labels hidden from the inserter on free (mirror manage-fields.php).
58 * Blocks stay registered so existing instances remain editable after a downgrade.
59 *
60 * @return string[] PB field-type labels.
61 */
62 function wppb_fb_free_locked_field_types() {
63 return apply_filters( 'wppb_fb_free_locked_field_types', array(
64 // standard
65 'Number',
66 'Input (Hidden)',
67 'Language',
68 'WYSIWYG',
69 'Select (Multiple)',
70 'HTML',
71 'Upload',
72 'International Telephone Input',
73 // advanced
74 'Phone',
75 'Select (Country)',
76 'Select (Timezone)',
77 'Select (Currency)',
78 'Select (CPT)',
79 'Select (Taxonomy)',
80 'Checkbox (Terms and Conditions)',
81 'Datepicker',
82 'Timepicker',
83 'Colorpicker',
84 'Validation',
85 'Map',
86 'Additional Map',
87 // other
88 'WooCommerce Customer Billing Address',
89 'WooCommerce Customer Shipping Address',
90 'MailChimp Subscribe',
91 'Email',
92 'URL',
93 'Select2 (Multiple)',
94 'Honeypot',
95 ) );
96 }
97
98 /**
99 * Pro-only field block names to hide from the inserter on free. Empty when paid.
100 *
101 * @return string[]
102 */
103 function wppb_fb_free_locked_field_blocks() {
104 if ( wppb_fb_is_paid_version() ) {
105 return array();
106 }
107
108 $locked = wppb_fb_free_locked_field_types();
109 $hidden = array();
110 foreach ( WPPB_FB_Field_Registry::all() as $field_type => $entry ) {
111 if ( in_array( $field_type, $locked, true ) && ! empty( $entry['block'] ) ) {
112 $hidden[] = $entry['block'];
113 }
114 }
115 return $hidden;
116 }
117
118 /**
119 * Whether Conditional Logic UI may show. Hide UI only — attributes stay in schema
120 * so stored rules survive a downgrade save.
121 *
122 * @return bool
123 */
124 function wppb_fb_conditional_logic_available() {
125 $available = function_exists( 'wppb_conditional_fields_exists' ) ? wppb_conditional_fields_exists() : false;
126 return (bool) apply_filters( 'wppb_fb_conditional_logic_available', $available );
127 }
128
129 /**
130 * Upsell URL for a locked form-builder surface.
131 *
132 * @param string $campaign utm_campaign value.
133 * @return string
134 */
135 function wppb_fb_upsell_url( $campaign ) {
136 return add_query_arg(
137 array(
138 'utm_source' => 'pb-forms',
139 'utm_medium' => 'client-site',
140 'utm_campaign' => $campaign,
141 ),
142 'https://www.cozmoslabs.com/wordpress-profile-builder/'
143 ) . '#pricing';
144 }
145
146 /**
147 * Friendlier wp_die on post-new.php when create_posts is locked (cap still gates).
148 */
149 add_action( 'load-post-new.php', 'wppb_fb_block_new_form_screen' );
150 function wppb_fb_block_new_form_screen() {
151 $post_type = isset( $_GET['post_type'] ) ? sanitize_key( wp_unslash( $_GET['post_type'] ) ) : 'post';
152
153 if ( ! wppb_fb_is_form_cpt( $post_type ) || wppb_fb_multiple_forms_available() ) {
154 return;
155 }
156
157 wp_die(
158 wp_kses_post( sprintf(
159 /* translators: %s is the upgrade link URL */
160 __( '<h1>Multiple Forms is a Pro feature</h1><p>The free version of Profile Builder includes the Default Registration and Default Edit Profile forms. Creating additional forms requires <a href="%s" target="_blank" rel="noopener">Profile Builder Pro</a>.</p>', 'profile-builder' ),
161 esc_url( wppb_fb_upsell_url( 'pb-multiple-forms' ) )
162 ) ),
163 esc_html__( 'Multiple Forms is a Pro feature', 'profile-builder' ),
164 array(
165 'response' => 403,
166 'back_link' => true,
167 )
168 );
169 }
170
171 /**
172 * Greyed "Add New" stand-in + wp-pointer when create_posts is locked (core hides
173 * the real button). Use a focusable span (not disabled button); close pointer on
174 * a short timer so the Upgrade link stays reachable across the arrow gap.
175 */
176 add_action( 'admin_enqueue_scripts', 'wppb_fb_enqueue_locked_add_new_button' );
177 function wppb_fb_enqueue_locked_add_new_button() {
178 if ( ! wppb_fb_is_forms_list_screen() || wppb_fb_multiple_forms_available() ) {
179 return;
180 }
181
182 $post_type_object = get_post_type_object( get_current_screen()->post_type );
183
184 if ( empty( $post_type_object ) ) {
185 return;
186 }
187
188 wp_enqueue_style( 'wp-pointer' );
189 wp_enqueue_script( 'wp-pointer' );
190
191 $label = $post_type_object->labels->add_new;
192 $post_type = get_current_screen()->post_type;
193 $title = ( $post_type === 'wppb-epf-cpt' )
194 ? __( 'Need a second edit profile form?', 'profile-builder' )
195 : __( 'Need a second registration form?', 'profile-builder' );
196
197 // Custom markup (not pointer <h3>): core paints a WP logo bar on h3.
198 $message = sprintf(
199 '<div class="wppb-fb-upsell-pointer">'
200 . '<p class="wppb-fb-upsell-pointer__title">%1$s</p>'
201 . '<p class="wppb-fb-upsell-pointer__text">%2$s</p>'
202 . '<p class="wppb-fb-upsell-pointer__cta"><a href="%3$s" target="_blank" rel="noopener">%4$s</a></p>'
203 . '</div>',
204 esc_html( $title ),
205 esc_html__( 'Free gives you the Default Registration and Edit Profile forms. With Pro you can create as many as you like and set different fields on each.', 'profile-builder' ),
206 esc_url( wppb_fb_upsell_url( 'pb-multiple-forms' ) ),
207 esc_html__( 'Upgrade to Pro', 'profile-builder' )
208 );
209
210 $plain = __( 'Creating additional forms requires Profile Builder Pro.', 'profile-builder' );
211
212 // NOWDOC: translated strings arrive as one JSON argument (no PHP interpolation).
213 $body = <<<'JS'
214 ( function ( data ) {
215 document.addEventListener( 'DOMContentLoaded', function () {
216 var heading = document.querySelector( '.wrap .wp-heading-inline' );
217
218 if ( ! heading || document.querySelector( '.wrap .page-title-action' ) ) {
219 return;
220 }
221
222 var button = document.createElement( 'span' );
223 button.className = 'page-title-action wppb-fb-add-new-locked';
224 button.setAttribute( 'role', 'button' );
225 button.setAttribute( 'aria-disabled', 'true' );
226 button.setAttribute( 'tabindex', '0' );
227 button.textContent = data.label;
228
229 heading.insertAdjacentElement( 'afterend', button );
230 heading.insertAdjacentText( 'afterend', ' ' );
231
232 var $ = window.jQuery;
233
234 if ( ! $ || ! $.fn.pointer ) {
235 button.title = data.plain;
236 return;
237 }
238
239 var $button = $( button ), closeTimer = null;
240
241 $button.pointer( {
242 content: data.message,
243 position: { edge: 'top', align: 'left' },
244 pointerClass: 'wp-pointer wppb-fb-add-new-locked-pointer',
245 // No Dismiss: tooltip reopens on hover; returning false skips the button.
246 buttons: function () {
247 return false;
248 }
249 } );
250
251 function cancelClose() {
252 window.clearTimeout( closeTimer );
253 }
254
255 function close() {
256 cancelClose();
257 $button.pointer( 'close' );
258 }
259
260 function closeSoon() {
261 cancelClose();
262 closeTimer = window.setTimeout( close, 300 );
263 }
264
265 function show() {
266 cancelClose();
267 $button.pointer( 'open' );
268
269 // Keep open while cursor is in the pointer; off() first so handlers do not stack.
270 $button.pointer( 'widget' )
271 .off( '.wppbFbLocked' )
272 .on( 'mouseenter.wppbFbLocked', cancelClose )
273 .on( 'mouseleave.wppbFbLocked', closeSoon );
274 }
275
276 $button.on( 'mouseenter focus', show );
277 $button.on( 'mouseleave blur', closeSoon );
278 $button.on( 'click', function ( event ) {
279 event.preventDefault();
280 show();
281 } );
282 $button.on( 'keydown', function ( event ) {
283 if ( event.key === 'Enter' || event.key === ' ' ) {
284 event.preventDefault();
285 show();
286 } else if ( event.key === 'Escape' ) {
287 close();
288 }
289 } );
290
291 // Touch has no mouseleave — close on outside click.
292 $( document ).on( 'click.wppbFbLocked', function ( event ) {
293 var $target = $( event.target );
294
295 if ( ! $target.closest( button ).length
296 && ! $target.closest( $button.pointer( 'widget' ) ).length ) {
297 close();
298 }
299 } );
300 } );
301 } )
302 JS;
303
304 $data = wp_json_encode( array(
305 'label' => $label,
306 'message' => $message,
307 'plain' => $plain,
308 ) );
309
310 wp_add_inline_script( 'wp-pointer', $body . '( ' . $data . ' );', 'after' );
311 }
312