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

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

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