PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.30
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.30
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.30, at classes/controllers/FrmAddonsController.php

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