PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.26
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.26
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / controllers / FrmAddonsController.php

FrmAddonsController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.26, at classes/controllers/FrmAddonsController.php

1,596 lines 40.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmAddonsController {
7
8 /**
9 * @var string
10 */
11 const SCRIPT_HANDLE = 'frm-addons-page';
12
13 /**
14 * @var array
15 */
16 private static $categories = array();
17
18 /**
19 * @var string
20 */
21 private static $request_addon_url;
22
23 /**
24 * @var string
25 */
26 protected static $plugin;
27
28 /**
29 * @since 6.15
30 *
31 * @return void
32 */
33 public static function load_admin_hooks() {
34 add_action( 'admin_menu', self::class . '::menu', 100 );
35 add_filter( 'pre_set_site_transient_update_plugins', self::class . '::check_update' );
36
37 if ( FrmAppHelper::is_admin_page( 'formidable-addons' ) ) {
38 self::$request_addon_url = 'https://connect.formidableforms.com/add-on-request/';
39
40 add_action( 'admin_enqueue_scripts', self::class . '::enqueue_assets', 15 );
41 add_filter( 'frm_show_footer_links', '__return_false' );
42 }
43 }
44
45 /**
46 * Enqueues the Add-Ons page scripts and styles.
47 *
48 * @since 6.15
49 *
50 * @return void
51 */
52 public static function enqueue_assets() {
53 $plugin_url = FrmAppHelper::plugin_url();
54 $version = FrmAppHelper::plugin_version();
55 $js_dependencies = array(
56 'wp-i18n',
57 // This prevents a console error "wp.hooks is undefined" in WP versions older than 5.7.
58 'wp-hooks',
59 'formidable_dom',
60 );
61
62 // Enqueue styles that needed.
63 wp_enqueue_style( 'formidable-admin' );
64 wp_enqueue_style( 'formidable-grids' );
65
66 // Register and enqueue Add-Ons page style.
67 wp_register_style( self::SCRIPT_HANDLE, $plugin_url . '/css/admin/addons-page.css', array(), $version );
68 wp_enqueue_style( self::SCRIPT_HANDLE );
69
70 // Register and enqueue Add-Ons page script.
71 wp_register_script( self::SCRIPT_HANDLE, $plugin_url . '/js/addons-page.js', $js_dependencies, $version, true );
72 wp_localize_script( self::SCRIPT_HANDLE, 'frmAddonsVars', self::get_js_variables() );
73 wp_enqueue_script( self::SCRIPT_HANDLE );
74 wp_set_script_translations( self::SCRIPT_HANDLE, 'formidable' );
75
76 FrmAppHelper::dequeue_extra_global_scripts();
77 }
78
79 /**
80 * Get the Add-Ons page JS variables as an array.
81 *
82 * @since 6.15
83 *
84 * @return array
85 */
86 private static function get_js_variables() {
87 return array(
88 'proIsIncluded' => FrmAppHelper::pro_is_included(),
89 'addonRequestURL' => self::$request_addon_url,
90 );
91 }
92
93 /**
94 * @return void
95 */
96 public static function menu() {
97 if ( ! current_user_can( 'activate_plugins' ) ) {
98 return;
99 }
100
101 $label = __( 'Add-Ons', 'formidable' );
102 $label = '<span style="color:#3FCA89">' . esc_html( $label ) . '</span>';
103
104 add_submenu_page( 'formidable', 'Formidable | ' . __( 'Add-Ons', 'formidable' ), $label, 'frm_view_forms', 'formidable-addons', 'FrmAddonsController::list_addons' );
105
106 // remove default created subpage, make the page with highest priority as default.
107 remove_submenu_page( 'formidable', 'formidable' );
108
109 if ( ! FrmAppHelper::pro_is_installed() ) {
110 $cta_text = FrmSalesApi::get_best_sale_value( 'menu_cta_text' );
111
112 if ( ! $cta_text ) {
113 $cta_text = __( 'Upgrade', 'formidable' );
114 }
115
116 add_submenu_page(
117 'formidable',
118 'Formidable | ' . __( 'Upgrade', 'formidable' ),
119 '<span class="frm-upgrade-submenu">' . esc_html( $cta_text ) . '</span>',
120 'frm_view_forms',
121 'formidable-pro-upgrade',
122 function () {
123 // This function doesn't need to do anything.
124 // The redirect is handled earlier to avoid issues with the headers being sent.
125 }
126 );
127 } elseif ( 'formidable-pro-upgrade' === FrmAppHelper::get_param( 'page' ) ) {
128 wp_safe_redirect( admin_url( 'admin.php?page=formidable' ) );
129 exit;
130 }//end if
131 }
132
133 /**
134 * @return void
135 */
136 public static function list_addons() {
137 FrmAppHelper::include_svg();
138
139 $view_path = FrmAppHelper::plugin_path() . '/classes/views/addons/';
140 $installed_addons = apply_filters( 'frm_installed_addons', array() );
141 $addons = self::get_api_addons();
142 $errors = array();
143 $license_type = '';
144 $request_addon_url = self::$request_addon_url;
145
146 if ( isset( $addons['error'] ) ) {
147 $api = new FrmFormApi();
148 $errors = $api->get_error_from_response( $addons );
149 $license_type = $addons['error']['type'] ?? '';
150 unset( $addons['error'] );
151 }
152
153 $pro = array(
154 'pro' => array(
155 'title' => 'Formidable Forms Pro',
156 'slug' => 'formidable-pro',
157 'released' => '2011-02-05',
158 'docs' => 'knowledgebase/',
159 'categories' => array( 'basic', 'plus', 'business', 'elite' ),
160 'excerpt' => 'Create calculators, surveys, smart forms, and data-driven applications. Build directories, real estate listings, job boards, and much more.',
161 ),
162 );
163 $addons = $pro + $addons;
164 self::prepare_addons( $addons );
165
166 $pricing = FrmAppHelper::admin_upgrade_link( 'addons' );
167
168 self::organize_and_get_categories();
169 $categories = self::$categories;
170
171 include $view_path . 'index.php';
172 }
173
174 /**
175 * Organize and set categories.
176 *
177 * @since 6.15
178 *
179 * @return void
180 */
181 protected static function organize_and_get_categories() {
182 unset( self::$categories['strategy11'] );
183 ksort( self::$categories );
184
185 $bottom_categories = array();
186 $plans = FrmFormsHelper::get_license_types(
187 array(
188 'include_all' => false,
189 'case_lower' => true,
190 )
191 );
192
193 // Extract the elements to move
194 foreach ( $plans as $plan ) {
195 if ( isset( self::$categories[ $plan ] ) ) {
196 $bottom_categories[ $plan ] = self::$categories[ $plan ];
197 unset( self::$categories[ $plan ] );
198 }
199 }
200
201 $special_categories = array();
202
203 if ( 'elite' !== self::license_type() ) {
204 $special_categories['available-addons'] = array(
205 'name' => __( 'Available', 'formidable' ),
206 // To be assigned via JavaScript.
207 'count' => 0,
208 );
209 }
210
211 $special_categories['active-addons'] = array(
212 'name' => __( 'Active', 'formidable' ),
213 // To be assigned via JavaScript.
214 'count' => 0,
215 );
216
217 $special_categories['all-items'] = array(
218 'name' => __( 'All Add-Ons', 'formidable' ),
219 // To be assigned via JavaScript.
220 'count' => 0,
221 );
222
223 self::$categories = array_merge(
224 $special_categories,
225 self::$categories,
226 $bottom_categories
227 );
228 }
229
230 /**
231 * Organize and set categories.
232 *
233 * @since 6.15
234 *
235 * @param array $addon The addon array that will be modified by reference.
236 *
237 * @return void
238 */
239 protected static function set_categories( &$addon ) {
240 if ( ! isset( $addon['categories'] ) ) {
241 return;
242 }
243
244 $addon['category-slugs'] = array();
245
246 foreach ( $addon['categories'] as $category ) {
247 $category = FrmFormsHelper::convert_legacy_package_names( $category );
248 $category_slug = sanitize_title( $category );
249
250 // Add the slug to the new array.
251 $addon['category-slugs'][] = $category_slug;
252
253 if ( ! isset( self::$categories[ $category_slug ] ) ) {
254 self::$categories[ $category_slug ] = array(
255 'name' => $category,
256 'count' => 0,
257 );
258 }
259
260 ++self::$categories[ $category_slug ]['count'];
261 }
262 }
263
264 /**
265 * @return void
266 */
267 public static function license_settings() {
268 $plugins = apply_filters( 'frm_installed_addons', array() );
269
270 if ( empty( $plugins ) ) {
271 esc_html_e( 'There are no plugins on your site that require a license', 'formidable' );
272
273 return;
274 }
275
276 ksort( $plugins );
277
278 include FrmAppHelper::plugin_path() . '/classes/views/addons/settings.php';
279 }
280
281 /**
282 * @return array
283 */
284 protected static function get_api_addons() {
285 $api = new FrmFormApi();
286 $addons = $api->get_api_info();
287
288 if ( empty( $addons ) ) {
289 $addons = self::fallback_plugin_list();
290 } else {
291 foreach ( $addons as $k => $addon ) {
292 if ( empty( $addon['excerpt'] ) && $k !== 'error' ) {
293 unset( $addons[ $k ] );
294 }
295 }
296 }
297
298 return $addons;
299 }
300
301 /**
302 * Retrieves the count of available addons.
303 *
304 * @since 6.9
305 *
306 * @return int Count of addons.
307 */
308 public static function get_addons_count() {
309 $addons = self::get_api_addons();
310
311 return count( $addons );
312 }
313
314 /**
315 * If the API is unable to connect, show something on the addons page
316 *
317 * @since 3.04.03
318 *
319 * @return array
320 */
321 protected static function fallback_plugin_list() {
322 $list = array(
323 'formidable-pro' => array(
324 'title' => 'Formidable Forms Pro',
325 'link' => 'pricing/',
326 'docs' => '',
327 'excerpt' => 'Enhance your basic Formidable forms with a plethora of Pro field types and features. Create advanced forms and data-driven applications in minutes.',
328 ),
329 'mailchimp' => array(
330 'title' => 'Mailchimp Forms',
331 'excerpt' => 'Get on the path to more sales and leads in a matter of minutes. Add leads to a Mailchimp mailing list when they submit forms and update their information along with the entry.',
332 ),
333 'registration' => array(
334 'title' => 'User Registration Forms',
335 'link' => 'downloads/user-registration/',
336 'excerpt' => 'Give new users access to your site as quickly and painlessly as possible. Allow users to register, edit and be able to login to their profiles on your site from the front end in a clean, customized registration form.',
337 ),
338 'paypal' => array(
339 'title' => 'PayPal Standard Forms',
340 'link' => 'downloads/paypal-standard/',
341 'excerpt' => 'Automate your business by collecting instant payments from your clients. Collect information, calculate a total, and send them on to PayPal. Require a payment before publishing content on your site.',
342 ),
343 'stripe' => array(
344 'title' => 'Stripe Forms',
345 'docs' => 'knowledgebase/stripe/',
346 'excerpt' => 'Any Formidable forms on your site can accept credit card payments without users ever leaving your site.',
347 ),
348 'authorize-net' => array(
349 'title' => 'Authorize.net AIM Forms',
350 'link' => 'downloads/authorize-net-aim/',
351 'docs' => 'knowledgebase/authorize-net-aim/',
352 'excerpt' => 'Accept one-time payments directly on your site, using Authorize.net AIM.',
353 ),
354 'woocommerce' => array(
355 'title' => 'WooCommerce Forms',
356 'excerpt' => 'Use a Formidable form on your WooCommerce product pages.',
357 ),
358 'autoresponder' => array(
359 'title' => 'Form Action Automation',
360 'docs' => 'knowledgebase/schedule-autoresponder/',
361 'excerpt' => 'Schedule email notifications, SMS messages, and API actions.',
362 ),
363 'modal' => array(
364 'title' => 'Bootstrap Modal Forms',
365 'link' => 'downloads/bootstrap-modal/',
366 'docs' => 'knowledgebase/bootstrap-modal/',
367 'excerpt' => 'Open a view or form in a Bootstrap popup.',
368 ),
369 'bootstrap' => array(
370 'title' => 'Bootstrap Style Forms',
371 'excerpt' => 'Instantly add Bootstrap styling to all your Formidable forms.',
372 ),
373 'zapier' => array(
374 'title' => 'Zapier Forms',
375 'excerpt' => 'Connect with hundreds of different applications through Zapier. Insert a new row in a Google docs spreadsheet, post on Twitter, or add a new Dropbox file with your form.',
376 ),
377 'signature' => array(
378 'title' => 'Digital Signature Forms',
379 'excerpt' => 'Add a signature field to your form. The user may write their signature with a trackpad/mouse or just type it.',
380 ),
381 'api' => array(
382 'title' => 'Formidable Forms API',
383 'link' => 'downloads/formidable-api/',
384 'excerpt' => 'Send entry results to any other site that has a Rest API. This includes the option of sending entries from one Formidable site to another.',
385 ),
386 'twilio' => array(
387 'title' => 'Twilio SMS Forms',
388 'docs' => 'knowledgebase/twilio-add-on/',
389 'excerpt' => 'Allow users to text their votes for polls created by Formidable Forms, or send SMS notifications when entries are submitted or updated.',
390 ),
391 'views' => array(
392 'title' => 'Formidable Views',
393 'excerpt' => 'Add the power of views to your Formidable Forms to display your form submissions in listings, tables, calendars, and more.',
394 ),
395 'quiz_maker' => array(
396 'title' => 'Quiz Maker',
397 'link' => 'downloads/quiz-maker/',
398 'excerpt' => 'Make quizzes, automatically score them and show user scores.',
399 ),
400 );
401
402 $defaults = array(
403 'released' => '',
404 );
405
406 foreach ( $list as $k => $info ) {
407 $info['slug'] = $k;
408 $list[ $k ] = array_merge( $defaults, $info );
409 }
410 return $list;
411 }
412
413 /**
414 * If Pro is missing but has been authenticated, include a download URL
415 *
416 * @since 3.04.03
417 *
418 * @return string
419 */
420 public static function get_pro_download_url() {
421 $license = self::get_pro_license();
422 $api = new FrmFormApi( $license );
423 $downloads = $api->get_api_info();
424 $pro = self::get_pro_from_addons( $downloads );
425
426 return $pro['url'] ?? '';
427 }
428
429 /**
430 * @since 4.08
431 *
432 * @return string
433 */
434 public static function get_pro_license() {
435 $pro_cred_store = 'frmpro-credentials';
436 $pro_wpmu_store = 'frmpro-wpmu-sitewide';
437
438 if ( is_multisite() && get_site_option( $pro_wpmu_store ) ) {
439 $creds = get_site_option( $pro_cred_store );
440 } else {
441 $creds = get_option( $pro_cred_store );
442 }
443
444 if ( empty( $creds ) || ! is_array( $creds ) || ! isset( $creds['license'] ) ) {
445 return '';
446 }
447
448 $license = $creds['license'];
449
450 if ( empty( $license ) ) {
451 return '';
452 }
453
454 if ( strpos( $license, '-' ) ) {
455 // this is a fix for licenses saved in the past
456 $license = strtoupper( $license );
457 }
458 return $license;
459 }
460
461 /**
462 * @since 4.08
463 *
464 * @param array $addons
465 *
466 * @return array
467 */
468 protected static function get_pro_from_addons( $addons ) {
469 return $addons['93790'] ?? array();
470 }
471
472 /**
473 * @since 4.06
474 *
475 * @return string
476 */
477 public static function license_type() {
478 if ( is_callable( 'FrmProAddonsController::license_type' ) ) {
479 return FrmProAddonsController::license_type();
480 }
481
482 return 'free';
483 }
484
485 /**
486 * @since 4.0.01
487 *
488 * @return bool
489 */
490 public static function is_license_expired() {
491 $version_info = self::get_primary_license_info();
492
493 if ( ! isset( $version_info['error'] ) ) {
494 return false;
495 }
496
497 $expires = $version_info['error']['expires'] ?? 0;
498
499 if ( empty( $expires ) || $expires > time() ) {
500 return false;
501 }
502
503 $rate_limited = ! empty( $version_info['response_code'] ) && 429 === (int) $version_info['response_code'];
504
505 if ( $rate_limited ) {
506 // Do not return false positives for rate limited responses.
507 return false;
508 }
509
510 return $version_info['error'];
511 }
512
513 /**
514 * @since 4.08
515 * @since 6.7 This is public.
516 *
517 * @return array|false
518 */
519 public static function get_primary_license_info() {
520 $installed_addons = apply_filters( 'frm_installed_addons', array() );
521
522 if ( empty( $installed_addons ) || ! isset( $installed_addons['formidable_pro'] ) ) {
523 return false;
524 }
525
526 $installed_addons = array(
527 'formidable_pro' => $installed_addons['formidable_pro'],
528 );
529
530 return self::fill_update_addon_info( $installed_addons );
531 }
532
533 /**
534 * @since 3.04.03
535 *
536 * @param mixed $transient
537 *
538 * @return object
539 */
540 public static function check_update( $transient ) {
541 if ( ! FrmAppHelper::pro_is_installed() ) {
542 // Don't make any changes if only Lite is installed.
543 return $transient;
544 }
545
546 if ( ! is_object( $transient ) ) {
547 $transient = new stdClass();
548 }
549
550 $installed_addons = apply_filters( 'frm_installed_addons', array() );
551
552 if ( empty( $installed_addons ) ) {
553 return $transient;
554 }
555
556 $version_info = self::fill_update_addon_info( $installed_addons );
557 $transient->last_checked = time();
558 $wp_plugins = self::get_plugins();
559
560 foreach ( $version_info as $plugin ) {
561 $plugin = (object) $plugin;
562
563 if ( ! isset( $plugin->new_version ) || ! isset( $plugin->package ) ) {
564 continue;
565 }
566
567 $folder = $plugin->plugin;
568
569 if ( empty( $folder ) ) {
570 continue;
571 }
572
573 if ( ! self::is_installed( $folder ) ) {
574 // don't show an update if the plugin isn't installed
575 continue;
576 }
577
578 $wp_plugin = $wp_plugins[ $folder ] ?? array();
579 $wp_version = $wp_plugin['Version'] ?? '1.0';
580 $plugin->slug = explode( '/', $folder )[0];
581
582 if ( version_compare( $wp_version, $plugin->new_version, '<' ) ) {
583 $transient->response[ $folder ] = $plugin;
584 } else {
585 $transient->no_update[ $folder ] = $plugin;
586 }
587
588 $transient->checked[ $folder ] = $wp_version;
589
590 }//end foreach
591
592 return $transient;
593 }
594
595 /**
596 * Copy of FrmAppHelper::get_plugins.
597 * Because this gets called on "pre_set_site_transient_update_plugins" an old version of FrmAppHelper may be loaded on plugin update.
598 * This means that trying to access FrmAppHelper::get_plugins when upgrading from a Lite version before v5.5 results in a one-off error.
599 *
600 * @since 5.5.2
601 *
602 * @return array
603 */
604 protected static function get_plugins() {
605 if ( ! function_exists( 'get_plugins' ) ) {
606 require_once ABSPATH . 'wp-admin/includes/plugin.php';
607 }
608 return get_plugins();
609 }
610
611 /**
612 * Check if a plugin is installed before showing an update for it
613 *
614 * @since 3.05
615 *
616 * @param string $plugin The folder/filename.php for a plugin.
617 *
618 * @return bool - True if installed
619 */
620 protected static function is_installed( $plugin ) {
621 $all_plugins = self::get_plugins();
622 return isset( $all_plugins[ $plugin ] );
623 }
624
625 /**
626 * @since 3.04.03
627 *
628 * @param array $installed_addons
629 *
630 * @return array
631 */
632 protected static function fill_update_addon_info( $installed_addons ) {
633 $checked_licenses = array();
634 $version_info = array();
635
636 foreach ( $installed_addons as $addon ) {
637 if ( $addon->store_url !== 'https://formidableforms.com' ) {
638 // check if this is a third-party addon
639 continue;
640 }
641
642 $new_license = $addon->license;
643
644 if ( empty( $new_license ) || in_array( $new_license, $checked_licenses ) ) {
645 continue;
646 }
647
648 $checked_licenses[] = $new_license;
649
650 $api = new FrmFormApi( $new_license );
651
652 if ( empty( $version_info ) ) {
653 $version_info = $api->get_api_info();
654 continue;
655 }
656
657 $plugin = $api->get_addon_for_license( $addon, $version_info );
658
659 if ( empty( $plugin ) ) {
660 continue;
661 }
662
663 $download_id = $plugin['id'] ?? 0;
664
665 if ( ! empty( $download_id ) && ! isset( $version_info[ $download_id ]['package'] ) ) {
666 // if this addon is using its own license, get the update url
667 $addon_info = $api->get_api_info();
668
669 $version_info[ $download_id ] = $addon_info[ $download_id ];
670
671 if ( isset( $addon_info['error'] ) ) {
672 $version_info[ $download_id ]['error'] = array(
673 'message' => $addon_info['error']['message'],
674 'code' => $addon_info['error']['code'],
675 );
676 }
677 }
678 }//end foreach
679
680 return $version_info;
681 }
682
683 /**
684 * Get the action link for an addon that isn't active.
685 *
686 * @since 3.06.03
687 *
688 * @param string $plugin The plugin slug.
689 *
690 * @return array
691 */
692 public static function install_link( $plugin ) {
693 $link = array();
694 $addon = self::get_addon( $plugin );
695
696 if ( $addon ) {
697 if ( $addon['status']['type'] === 'installed' && ! empty( $addon['activate_url'] ) ) {
698 $link = array(
699 'url' => $addon['plugin'],
700 'class' => 'frm-activate-addon',
701 );
702 } elseif ( ! empty( $addon['url'] ) ) {
703 $link = array(
704 'url' => $addon['url'],
705 'class' => 'frm-install-addon',
706 );
707 } elseif ( ! empty( $addon['categories'] ) ) {
708 $link = array(
709 'categories' => $addon['categories'],
710 );
711 }
712
713 if ( ! empty( $link ) ) {
714 $link['status'] = $addon['status']['type'];
715 }
716 } elseif ( current_user_can( 'activate_plugins' ) && self::is_installed( 'formidable-' . $plugin . '/formidable-' . $plugin . '.php' ) ) {
717 $link = array(
718 'url' => 'formidable-' . $plugin . '/formidable-' . $plugin . '.php',
719 'class' => 'frm-activate-addon',
720 );
721 }//end if
722
723 return $link;
724 }
725
726 /**
727 * @since 4.09
728 *
729 * @param string $plugin The plugin slug.
730 *
731 * @return array|false
732 */
733 public static function get_addon( $plugin ) {
734 $addons = self::get_api_addons();
735 self::prepare_addons( $addons );
736
737 foreach ( $addons as $addon ) {
738 $slug = explode( '/', $addon['plugin'] );
739
740 if ( $slug[0] === 'formidable-' . $plugin ) {
741 return $addon;
742 }
743 }
744 return false;
745 }
746
747 /**
748 * @since 4.09
749 *
750 * @return string
751 */
752 protected static function get_license_type() {
753 $license_type = '';
754 $addons = self::get_api_addons();
755
756 if ( isset( $addons['error'] ) && isset( $addons['error']['type'] ) ) {
757 $license_type = $addons['error']['type'];
758 }
759 return $license_type;
760 }
761
762 /**
763 * @since 3.04.03
764 *
765 * @param array $addons
766 * @param object $license The FrmAddon object.
767 *
768 * @return array
769 */
770 public static function get_addon_for_license( $addons, $license ) {
771 $download_id = $license->download_id;
772 $plugin = array();
773
774 if ( empty( $download_id ) && ! empty( $addons ) ) {
775 foreach ( $addons as $addon ) {
776 if ( strtolower( $license->plugin_name ) === strtolower( $addon['title'] ) ) {
777 return $addon;
778 }
779 }
780 } elseif ( isset( $addons[ $download_id ] ) ) {
781 $plugin = $addons[ $download_id ];
782 }
783
784 return $plugin;
785 }
786
787 /**
788 * @param array $addons
789 *
790 * @return void
791 */
792 protected static function prepare_addons( &$addons ) {
793 $activate_url = '';
794
795 if ( current_user_can( 'activate_plugins' ) ) {
796 $activate_url = add_query_arg( array( 'action' => 'activate' ), admin_url( 'plugins.php' ) );
797 }
798
799 $loop_addons = $addons;
800
801 foreach ( $loop_addons as $id => $addon ) {
802 if ( is_numeric( $id ) ) {
803 $slug = str_replace( array( '-wordpress-plugin', '-wordpress' ), '', $addon['slug'] );
804 $file_name = $addon['plugin'];
805 } else {
806 $slug = $id;
807
808 if ( isset( $addon['file'] ) ) {
809 $base_file = $addon['file'];
810 } else {
811 $base_file = 'formidable-' . $slug;
812 }
813
814 $file_name = $base_file . '/' . $base_file . '.php';
815
816 if ( ! isset( $addon['plugin'] ) ) {
817 $addon['plugin'] = $file_name;
818 }
819 }
820
821 $addon['installed'] = self::is_installed( $file_name );
822
823 if ( $addon['installed'] && 'formidable-views/formidable-views.php' === $file_name ) {
824 $active_views_version = self::get_active_views_version();
825
826 if ( false !== $active_views_version && $slug !== $active_views_version ) {
827 $addon['installed'] = false;
828 }
829 }
830
831 $addon['activate_url'] = '';
832
833 if ( $addon['installed'] && ! empty( $activate_url ) && ! self::is_plugin_active( $file_name, $slug ) ) {
834 $addon['activate_url'] = add_query_arg(
835 array(
836 '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $file_name ),
837 'plugin' => $file_name,
838 ),
839 $activate_url
840 );
841 }
842
843 if ( ! isset( $addon['docs'] ) ) {
844 $addon['docs'] = 'knowledgebase/formidable-' . $slug . '/';
845 }
846 self::prepare_addon_link( $addon['docs'] );
847
848 if ( ! isset( $addon['link'] ) ) {
849 $addon['link'] = 'downloads/' . $slug . '/';
850 }
851
852 self::prepare_addon_link( $addon['link'] );
853 self::set_addon_status( $addon );
854 self::set_categories( $addon );
855
856 $addons[ $id ] = $addon;
857 }//end foreach
858 }
859
860 /**
861 * @param string $file_name
862 * @param string $slug
863 *
864 * @return bool
865 */
866 private static function is_plugin_active( $file_name, $slug ) {
867 if ( 'formidable-views/formidable-views.php' === $file_name ) {
868 return self::get_active_views_version() === $slug;
869 }
870 return is_plugin_active( $file_name );
871 }
872
873 /**
874 * @return false|string either 'visual-views' or 'views', false if one is not found.
875 */
876 private static function get_active_views_version() {
877 if ( ! is_callable( 'FrmViewsAppHelper::plugin_version' ) ) {
878 return false;
879 }
880
881 $plugin_version = FrmViewsAppHelper::plugin_version();
882 return version_compare( $plugin_version, '5.0', '>=' ) ? 'visual-views' : 'views';
883 }
884
885 /**
886 * @since 3.04.02
887 *
888 * @param string $link
889 *
890 * @return void
891 */
892 protected static function prepare_addon_link( &$link ) {
893 $site_url = 'https://formidableforms.com/';
894
895 if ( strpos( $link, 'http' ) !== 0 ) {
896 $link = $site_url . $link;
897 }
898
899 $link = FrmAppHelper::make_affiliate_url( $link );
900
901 $utm = array(
902 'campaign' => 'addons',
903 );
904 $link = FrmAppHelper::maybe_add_missing_utm( $link, $utm );
905 }
906
907 /**
908 * Add the status to the addon array. Status options are:
909 * installed, active, not installed
910 *
911 * @since 3.04.02
912 *
913 * @param array $addon
914 *
915 * @return void
916 */
917 protected static function set_addon_status( &$addon ) {
918 if ( ! empty( $addon['activate_url'] ) ) {
919 $addon['status'] = array(
920 'type' => 'installed',
921 'label' => __( 'Installed', 'formidable' ),
922 );
923 } elseif ( $addon['installed'] ) {
924 $addon['status'] = array(
925 'type' => 'active',
926 'label' => __( 'Active', 'formidable' ),
927 );
928 } else {
929 $addon['status'] = array(
930 'type' => 'not-installed',
931 'label' => __( 'Not Installed', 'formidable' ),
932 );
933 }
934 }
935
936 /**
937 * @since 5.5.2
938 *
939 * @param string $plugin
940 * @param string $redirect
941 * @param bool $network_wide
942 * @param bool $silent
943 *
944 * @return WP_Error|null Null on success, WP_Error on invalid file.
945 */
946 protected static function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
947 if ( ! function_exists( 'activate_plugin' ) ) {
948 require_once ABSPATH . 'wp-admin/includes/plugin.php';
949 }
950 return activate_plugin( $plugin, $redirect, $network_wide, $silent );
951 }
952
953 /**
954 * Deactivate a specified plugin.
955 *
956 * @since 6.8
957 *
958 * @param string $plugin
959 * @param bool $silent
960 *
961 * @return void
962 */
963 protected static function deactivate_plugin( $plugin, $silent = false ) {
964 if ( ! function_exists( 'deactivate_plugins' ) ) {
965 require_once ABSPATH . 'wp-admin/includes/plugin.php';
966 }
967 deactivate_plugins( $plugin, $silent );
968 }
969
970 /**
971 * Uninstall a specified plugin.
972 *
973 * @since 6.8
974 *
975 * @param string $plugin
976 *
977 * @return true|WP_Error True on success, WP_Error on invalid file.
978 */
979 protected static function uninstall_plugin( $plugin ) {
980 if ( ! current_user_can( 'delete_plugins' ) ) {
981 return new WP_Error( 'uninstall_failed', __( 'Current user cannot delete plugins.', 'formidable' ) );
982 }
983
984 if ( ! function_exists( 'delete_plugins' ) ) {
985 require_once ABSPATH . 'wp-admin/includes/plugin.php';
986 }
987
988 self::deactivate_plugin( $plugin, true );
989 $result = delete_plugins( array( $plugin ) );
990
991 if ( is_wp_error( $result ) ) {
992 return $result;
993 }
994
995 return true;
996 }
997
998 /**
999 * @since 5.0.10
1000 *
1001 * @return string
1002 */
1003 protected static function get_current_plugin() {
1004 if ( empty( self::$plugin ) ) {
1005 self::$plugin = FrmAppHelper::get_param( 'plugin', '', 'post', 'esc_url_raw' );
1006 }
1007 return self::$plugin;
1008 }
1009
1010 /**
1011 * @since 4.08
1012 *
1013 * @return array|void
1014 */
1015 protected static function download_and_activate() {
1016 if ( is_admin() ) {
1017 FrmAppHelper::set_current_screen_and_hook_suffix();
1018 }
1019
1020 self::maybe_show_cred_form();
1021
1022 $installed = self::install_addon();
1023
1024 if ( is_array( $installed ) && isset( $installed['message'] ) ) {
1025 return $installed;
1026 }
1027 self::handle_addon_action( $installed, 'activate' );
1028 }
1029
1030 /**
1031 * @since 3.04.02
1032 *
1033 * @return void
1034 */
1035 protected static function maybe_show_cred_form() {
1036 if ( ! function_exists( 'request_filesystem_credentials' ) ) {
1037 include_once ABSPATH . 'wp-admin/includes/file.php';
1038 }
1039
1040 // Start output buffering to catch the filesystem form if credentials are needed.
1041 ob_start();
1042
1043 $show_form = false;
1044 $method = '';
1045 $url = add_query_arg( array( 'page' => 'formidable-settings' ), admin_url( 'admin.php' ) );
1046 $url = esc_url_raw( $url );
1047 $creds = request_filesystem_credentials( $url, $method, false, false, null );
1048
1049 if ( false === $creds ) {
1050 $show_form = true;
1051 } elseif ( ! WP_Filesystem( $creds ) ) {
1052 request_filesystem_credentials( $url, $method, true, false, null );
1053 $show_form = true;
1054 }
1055
1056 if ( $show_form ) {
1057 $form = ob_get_clean();
1058 $message = __( 'Sorry, your site requires FTP authentication. Please download plugins from FormidableForms.com and install them manually.', 'formidable' );
1059 $data = $form;
1060 $response = array(
1061 'success' => false,
1062 'message' => $message,
1063 'form' => $form,
1064 );
1065 wp_send_json( $response );
1066 }
1067
1068 ob_end_clean();
1069 }
1070
1071 /**
1072 * Checks if an addon download url is allowed.
1073 *
1074 * @since 6.2
1075 *
1076 * @param string $download_url
1077 *
1078 * @return bool
1079 */
1080 public static function url_is_allowed( $download_url ) {
1081 return FrmAppHelper::validate_url_is_in_s3_bucket( $download_url, 'zip' ) || in_array( $download_url, self::allowed_external_urls(), true );
1082 }
1083
1084 /**
1085 * We do not need any extra credentials if we have gotten this far,
1086 * so let's install the plugin.
1087 *
1088 * @since 3.04.02
1089 *
1090 * @return array|string
1091 */
1092 protected static function install_addon() {
1093 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1094
1095 $download_url = self::get_current_plugin();
1096
1097 if ( ! self::url_is_allowed( $download_url ) ) {
1098 return array(
1099 'message' => 'Plugin URL is not valid',
1100 'success' => false,
1101 );
1102 }
1103
1104 // Create the plugin upgrader with our custom skin.
1105 $installer = new Plugin_Upgrader( new FrmInstallerSkin() );
1106 $installer->install( $download_url );
1107
1108 // Flush the cache and return the newly installed plugin basename.
1109 wp_cache_flush();
1110
1111 $plugin = $installer->plugin_info();
1112
1113 if ( ! $plugin ) {
1114 return array(
1115 'message' => 'Plugin was not installed. ' . $installer->result,
1116 'success' => false,
1117 );
1118 }
1119 return $plugin;
1120 }
1121
1122 /**
1123 * Handle the AJAX request to activate an add-on.
1124 *
1125 * @since 6.8
1126 *
1127 * @return void
1128 */
1129 public static function ajax_activate_addon() {
1130 self::process_addon_action(
1131 function ( $plugin ) {
1132 return self::handle_addon_action( $plugin, 'activate' );
1133 },
1134 array( 'FrmAddonsController', 'get_addon_activation_response' )
1135 );
1136 }
1137
1138 /**
1139 * @since 3.04.02
1140 *
1141 * @param string $installed The plugin folder name with file name.
1142 */
1143 protected static function maybe_activate_addon( $installed ) {
1144 self::ajax_activate_addon();
1145 }
1146
1147 /**
1148 * Handle the AJAX request to deactivate an add-on.
1149 *
1150 * @since 6.8
1151 *
1152 * @return void
1153 */
1154 public static function ajax_deactivate_addon() {
1155 self::process_addon_action(
1156 function ( $plugin ) {
1157 return self::handle_addon_action( $plugin, 'deactivate' );
1158 }
1159 );
1160 }
1161
1162 /**
1163 * Handle the AJAX request to uninstall an add-on.
1164 *
1165 * @since 6.8
1166 *
1167 * @return void
1168 */
1169 public static function ajax_uninstall_addon() {
1170 self::process_addon_action(
1171 function ( $plugin ) {
1172 return self::handle_addon_action( $plugin, 'uninstall' );
1173 }
1174 );
1175 }
1176
1177 /**
1178 * Process a specific action (activate, deactivate, uninstall) on an add-on.
1179 *
1180 * @since 6.8
1181 *
1182 * @param callable $action_callback The specific add-on action to be executed.
1183 * @param callable|null $response_callback Optional. The response handling callback. Default null.
1184 *
1185 * @return void
1186 */
1187 private static function process_addon_action( $action_callback, $response_callback = null ) {
1188 self::install_addon_permissions();
1189 FrmAppHelper::set_current_screen_and_hook_suffix();
1190
1191 $plugin = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
1192 call_user_func( $action_callback, $plugin );
1193
1194 if ( is_callable( $response_callback ) ) {
1195 wp_send_json_success( call_user_func( $response_callback ) );
1196 return;
1197 }
1198
1199 wp_send_json_success();
1200 }
1201
1202 /**
1203 * Attempt to perform a specific action (activate, deactivate, uninstall) on an add-on.
1204 *
1205 * @since 6.8
1206 *
1207 * @param string $installed The plugin folder name with file name.
1208 * @param string $action The action type ('activate', 'deactivate', 'uninstall').
1209 *
1210 * @return array|void
1211 */
1212 protected static function handle_addon_action( $installed, $action ) {
1213 if ( ! $installed || ! $action ) {
1214 return;
1215 }
1216
1217 $result = null;
1218 switch ( $action ) {
1219 case 'activate':
1220 $result = self::activate_plugin( $installed );
1221 break;
1222 case 'deactivate':
1223 self::deactivate_plugin( $installed );
1224 break;
1225 case 'uninstall':
1226 $result = self::uninstall_plugin( $installed );
1227 break;
1228 }
1229
1230 if ( is_wp_error( $result ) ) {
1231 // Ignore the invalid header message that shows with nested plugins.
1232 if ( $result->get_error_code() !== 'no_plugin_header' ) {
1233 if ( wp_doing_ajax() ) {
1234 wp_send_json_error( array( 'error' => $result->get_error_message() ) );
1235 }
1236 return array(
1237 'message' => $result->get_error_message(),
1238 'success' => false,
1239 );
1240 }
1241 }
1242
1243 return $result;
1244 }
1245
1246 /**
1247 * @return array
1248 */
1249 private static function get_addon_activation_response() {
1250 $activating_page = self::get_activating_page();
1251
1252 $message = $activating_page ? __( 'Your plugin has been activated. Would you like to save and reload the page now?', 'formidable' ) : __( 'Your plugin has been activated.', 'formidable' );
1253
1254 $response = array(
1255 'message' => $message,
1256 'saveAndReload' => $activating_page,
1257 );
1258
1259 return $response;
1260 }
1261
1262 /**
1263 * Return a string that reflects the page from which the addon is being activated on,
1264 * if it is from settings or form builder, otherwise return empty string.
1265 *
1266 * @return string
1267 */
1268 private static function get_activating_page() {
1269 $referer = FrmAppHelper::get_server_value( 'HTTP_REFERER' );
1270
1271 if ( false !== strpos( $referer, 'frm_action=settings' ) ) {
1272 return 'settings';
1273 }
1274
1275 if ( false !== strpos( $referer, 'frm_action=edit' ) ) {
1276 return 'form_builder';
1277 }
1278
1279 return '';
1280 }
1281
1282 /**
1283 * Run security checks before installing
1284 *
1285 * @since 3.04.02
1286 *
1287 * @return void
1288 */
1289 protected static function install_addon_permissions() {
1290 check_ajax_referer( 'frm_ajax', 'nonce' );
1291
1292 if ( ! current_user_can( 'activate_plugins' ) || ! self::get_current_plugin() ) {
1293 echo json_encode( true );
1294 wp_die();
1295 }
1296 }
1297
1298 /**
1299 * @since 4.08
1300 *
1301 * @return string
1302 */
1303 public static function connect_link() {
1304 $auth = get_option( 'frm_connect_token' );
1305
1306 if ( empty( $auth ) ) {
1307 $auth = hash( 'sha512', wp_rand() );
1308 update_option( 'frm_connect_token', $auth, 'no' );
1309 }
1310
1311 $page = FrmAppHelper::simple_get( 'page', 'sanitize_title', 'formidable-settings' );
1312 $link = 'https://formidableforms.com/api-connect/';
1313 $args = array(
1314 'v' => 2,
1315 'siteurl' => FrmAppHelper::site_url(),
1316 'url' => get_rest_url(),
1317 'inst' => (int) FrmAppHelper::pro_is_included(),
1318 'return' => $page,
1319 'token' => $auth,
1320 'l' => self::get_pro_license(),
1321 );
1322
1323 return add_query_arg( $args, $link );
1324 }
1325
1326 /**
1327 * Check the auth value for install permission.
1328 *
1329 * @since 4.08
1330 *
1331 * @return bool
1332 */
1333 public static function can_install_addon_api() {
1334 // Verify params present (auth & download link).
1335 $post_auth = FrmAppHelper::get_param( 'token', '', 'request', 'sanitize_text_field' );
1336 $post_url = FrmAppHelper::get_param( 'file_url', '', 'request', 'sanitize_text_field' );
1337
1338 // The download link is not required if already installed.
1339 $is_installed = FrmAppHelper::pro_is_included();
1340 $file_missing = ! $is_installed && empty( $post_url );
1341
1342 if ( ! $post_auth || $file_missing ) {
1343 return false;
1344 }
1345
1346 // Verify auth.
1347 $auth = get_option( 'frm_connect_token' );
1348
1349 if ( empty( $auth ) || ! hash_equals( $auth, $post_auth ) ) {
1350 return false;
1351 }
1352
1353 return true;
1354 }
1355
1356 /**
1357 * Install and/or activate the add-on file.
1358 *
1359 * @since 4.08
1360 *
1361 * @return array
1362 */
1363 public static function install_addon_api() {
1364 self::$plugin = FrmAppHelper::get_param( 'file_url', '', 'request', 'esc_url_raw' );
1365
1366 // Delete so cannot replay.
1367 delete_option( 'frm_connect_token' );
1368
1369 // It's already installed and active.
1370 $active = self::activate_plugin( 'formidable-pro/formidable-pro.php', false, false, true );
1371
1372 if ( is_wp_error( $active ) ) {
1373 $response = self::maybe_download_and_activate();
1374
1375 if ( is_array( $response ) ) {
1376 // The download failed.
1377 return $response;
1378 }
1379 }
1380
1381 // If empty license, save it now.
1382 if ( empty( self::get_pro_license() ) && function_exists( 'load_formidable_pro' ) ) {
1383 load_formidable_pro();
1384 $license = stripslashes( FrmAppHelper::get_param( 'key', '', 'request', 'sanitize_text_field' ) );
1385
1386 if ( ! $license ) {
1387 return array(
1388 'success' => false,
1389 'error' => 'That site does not have a valid license key.',
1390 );
1391 }
1392
1393 $response = FrmAddon::activate_license_for_plugin( $license, 'formidable_pro' );
1394
1395 if ( ! $response['success'] ) {
1396 // Could not activate license.
1397 return $response;
1398 }
1399 }
1400
1401 return array(
1402 'success' => true,
1403 );
1404 }
1405
1406 /**
1407 * @since 6.8.3
1408 *
1409 * @return array|true
1410 */
1411 private static function maybe_download_and_activate() {
1412 if ( ! self::$plugin ) {
1413 return array(
1414 'success' => false,
1415 'message' => __( 'The plugin download was not found.', 'formidable' ),
1416 );
1417 }
1418
1419 // Download plugin now.
1420 $response = self::download_and_activate();
1421
1422 if ( is_array( $response ) && isset( $response['success'] ) ) {
1423 // The download failed.
1424 return $response;
1425 }
1426
1427 return true;
1428 }
1429
1430 /**
1431 * Render a conditional action button for a specified plugin
1432 *
1433 * @since 4.09
1434 *
1435 * @param string $plugin
1436 * @param array|string $upgrade_link_args
1437 *
1438 * @return void
1439 */
1440 public static function conditional_action_button( $plugin, $upgrade_link_args ) {
1441 if ( is_callable( 'FrmProAddonsController::conditional_action_button' ) ) {
1442 FrmProAddonsController::conditional_action_button( $plugin, $upgrade_link_args );
1443 return;
1444 }
1445
1446 $addon = self::get_addon( $plugin );
1447 $upgrade_link = FrmAppHelper::admin_upgrade_link( $upgrade_link_args );
1448
1449 if ( ! is_array( $upgrade_link_args ) ) {
1450 // A string $upgrade_link_args is used for the utm-medium value when calling
1451 // FrmAppHelper::admin_upgrade_link above.
1452 // For self::addon_upgrade_link, we'll pass just an empty array with the link key (set below).
1453 $upgrade_link_args = array();
1454 }
1455
1456 $upgrade_link_args['link'] = $upgrade_link;
1457
1458 self::addon_upgrade_link( $addon, $upgrade_link_args );
1459 }
1460
1461 /**
1462 * Render a conditional action button for an add on
1463 *
1464 * @since 4.09.01
1465 *
1466 * @param array $atts {
1467 * Button attributes.
1468 *
1469 * @type array $addon
1470 * @type false|string $license_type
1471 * @type string $plan_required
1472 * @type string $upgrade_link
1473 * }
1474 *
1475 * @return void
1476 */
1477 public static function show_conditional_action_button( $atts ) {
1478 if ( is_callable( 'FrmProAddonsController::show_conditional_action_button' ) ) {
1479 FrmProAddonsController::show_conditional_action_button( $atts );
1480 return;
1481 }
1482
1483 self::addon_upgrade_link( $atts['addon'], $atts['upgrade_link'] );
1484 }
1485
1486 /**
1487 * @since 4.09.01
1488 *
1489 * @param array|false $addon
1490 * @param array|string $upgrade_link
1491 *
1492 * @return void
1493 */
1494 public static function addon_upgrade_link( $addon, $upgrade_link ) {
1495 $atts = is_array( $upgrade_link ) ? $upgrade_link : array();
1496 $upgrade_link = is_array( $upgrade_link ) ? $upgrade_link['link'] : $upgrade_link;
1497 $text = ! empty( $atts['text'] ) ? $atts['text'] : __( 'Upgrade Now', 'formidable' );
1498
1499 if ( $addon ) {
1500 $upgrade_link .= '&utm_content=' . $addon['slug'];
1501 }
1502
1503 if ( $addon && isset( $addon['categories'] ) && in_array( 'Solution', $addon['categories'], true ) ) {
1504 // Solutions will go to a separate page.
1505 $upgrade_link = FrmAppHelper::admin_upgrade_link( 'addons', $addon['link'] );
1506 }
1507
1508 $class = ! empty( $atts['class'] ) ? $atts['class'] : '';
1509
1510 if ( strpos( $class, 'frm-button' ) === false ) {
1511 $class .= ' frm-button-secondary frm-button-sm';
1512 }
1513 ?>
1514 <a class="install-now button <?php echo esc_attr( $class ); ?>" href="<?php echo esc_url( $upgrade_link ); ?>" target="_blank" rel="noopener" aria-label="<?php esc_attr_e( 'Upgrade Now', 'formidable' ); ?>">
1515 <?php echo esc_html( $text ); ?>
1516 </a>
1517 <?php
1518 }
1519
1520 /**
1521 * @since 3.04.02
1522 *
1523 * @return void
1524 */
1525 public static function ajax_install_addon() {
1526 self::install_addon_permissions();
1527
1528 $result = self::download_and_activate();
1529
1530 if ( isset( $result['success'] ) && ! $result['success'] ) {
1531 echo json_encode( $result );
1532 wp_die();
1533 }
1534
1535 echo json_encode( self::get_addon_activation_response() );
1536 wp_die();
1537 }
1538
1539 /**
1540 * Allowed URLs used for internal source of plugins installation.
1541 *
1542 * @since 6.3.1
1543 *
1544 * @return array
1545 */
1546 private static function allowed_external_urls() {
1547 $allowed_url_list = array(
1548 'https://downloads.wordpress.org/plugin/formidable-gravity-forms-importer.zip',
1549 'https://downloads.wordpress.org/plugin/formidable-import-pirate-forms.zip',
1550 'https://downloads.wordpress.org/plugin/wp-mail-smtp.zip',
1551 );
1552
1553 /**
1554 * List of URLs used in plugin formidable internal installation.
1555 *
1556 * @since 6.3.1
1557 *
1558 * @param array $allowed_url_list List of URLs.
1559 */
1560 $allowed_url_list = apply_filters( 'frm_allowed_external_urls', $allowed_url_list );
1561
1562 if ( ! is_array( $allowed_url_list ) ) {
1563 _doing_it_wrong( __METHOD__, 'Only an array of URLs could be used within this filter.', '6.3.1' );
1564
1565 return array();
1566 }
1567
1568 return $allowed_url_list;
1569 }
1570
1571 /**
1572 * Gets required plan for an addon.
1573 *
1574 * @since 6.4.2
1575 *
1576 * @param int|string $addon_id
1577 *
1578 * @return string Empty string if no plan is required for active license.
1579 */
1580 public static function get_addon_required_plan( $addon_id ) {
1581 $api = new FrmFormApi();
1582 $addons = $api->get_api_info();
1583
1584 if ( is_array( $addons ) && array_key_exists( $addon_id, $addons ) ) {
1585 $dates = $addons[ $addon_id ];
1586 $requires = FrmFormsHelper::get_plan_required( $dates );
1587 }
1588
1589 if ( ! isset( $requires ) || ! is_string( $requires ) ) {
1590 $requires = '';
1591 }
1592
1593 return $requires;
1594 }
1595 }
1596