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

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