PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
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
← All changes | classes/controllers/FrmAddonsController.php +343 -108 6.25.1 → trunk View file →
@@ -26,19 +26,23 @@
26 26 protected static $plugin;
27 27
28 28 /**
29 29 * @since 6.15
30 + *
31 + * @return void
30 32 */
31 33 public static function load_admin_hooks() {
32 34 add_action( 'admin_menu', self::class . '::menu', 100 );
33 35 add_filter( 'pre_set_site_transient_update_plugins', self::class . '::check_update' );
34 36
35 - if ( FrmAppHelper::is_admin_page( 'formidable-addons' ) ) {
36 - self::$request_addon_url = 'https://connect.formidableforms.com/add-on-request/';
37 + if ( ! FrmAppHelper::is_admin_page( 'formidable-addons' ) ) {
38 + return;
39 + }
37 40
38 - add_action( 'admin_enqueue_scripts', self::class . '::enqueue_assets', 15 );
39 - add_filter( 'frm_show_footer_links', '__return_false' );
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' );
41 45 }
42 46
43 47 /**
44 48 * Enqueues the Add-Ons page scripts and styles.
@@ -100,13 +104,14 @@
100 104 $label = '<span style="color:#3FCA89">' . esc_html( $label ) . '</span>';
101 105
102 106 add_submenu_page( 'formidable', 'Formidable | ' . __( 'Add-Ons', 'formidable' ), $label, 'frm_view_forms', 'formidable-addons', 'FrmAddonsController::list_addons' );
103 107
104 - // remove default created subpage, make the page with highest priority as default.
108 + // Remove default created subpage, make the page with highest priority as default.
105 109 remove_submenu_page( 'formidable', 'formidable' );
106 110
107 111 if ( ! FrmAppHelper::pro_is_installed() ) {
108 112 $cta_text = FrmSalesApi::get_best_sale_value( 'menu_cta_text' );
113 +
109 114 if ( ! $cta_text ) {
110 115 $cta_text = __( 'Upgrade', 'formidable' );
111 116 }
112 117
@@ -151,9 +156,10 @@
151 156 'pro' => array(
152 157 'title' => 'Formidable Forms Pro',
153 158 'slug' => 'formidable-pro',
154 159 'released' => '2011-02-05',
155 - 'docs' => 'knowledgebase/',
160 + 'docs' => 'knowledgebase/what-is-the-difference-between-the-lite-free-and-pro-version/',
161 + 'docs_label' => __( 'Why Upgrade', 'formidable' ),
156 162 'categories' => array( 'basic', 'plus', 'business', 'elite' ),
157 163 'excerpt' => 'Create calculators, surveys, smart forms, and data-driven applications. Build directories, real estate listings, job boards, and much more.',
158 164 ),
159 165 );
@@ -188,15 +194,18 @@
188 194 );
189 195
190 196 // Extract the elements to move
191 197 foreach ( $plans as $plan ) {
192 - if ( isset( self::$categories[ $plan ] ) ) {
193 - $bottom_categories[ $plan ] = self::$categories[ $plan ];
194 - unset( self::$categories[ $plan ] );
198 + if ( ! isset( self::$categories[ $plan ] ) ) {
199 + continue;
195 200 }
201 +
202 + $bottom_categories[ $plan ] = self::$categories[ $plan ];
203 + unset( self::$categories[ $plan ] );
196 204 }
197 205
198 206 $special_categories = array();
207 +
199 208 if ( 'elite' !== self::license_type() ) {
200 209 $special_categories['available-addons'] = array(
201 210 'name' => __( 'Available', 'formidable' ),
202 211 // To be assigned via JavaScript.
@@ -228,8 +237,9 @@
228 237 *
229 238 * @since 6.15
230 239 *
231 240 * @param array $addon The addon array that will be modified by reference.
241 + *
232 242 * @return void
233 243 */
234 244 protected static function set_categories( &$addon ) {
235 245 if ( ! isset( $addon['categories'] ) ) {
@@ -260,11 +270,11 @@
260 270 * @return void
261 271 */
262 272 public static function license_settings() {
263 273 $plugins = apply_filters( 'frm_installed_addons', array() );
264 - if ( empty( $plugins ) ) {
274 +
275 + if ( ! $plugins ) {
265 276 esc_html_e( 'There are no plugins on your site that require a license', 'formidable' );
266 -
267 277 return;
268 278 }
269 279
270 280 ksort( $plugins );
@@ -278,15 +288,15 @@
278 288 protected static function get_api_addons() {
279 289 $api = new FrmFormApi();
280 290 $addons = $api->get_api_info();
281 291
282 - if ( empty( $addons ) ) {
283 - $addons = self::fallback_plugin_list();
284 - } else {
285 - foreach ( $addons as $k => $addon ) {
286 - if ( empty( $addon['excerpt'] ) && $k !== 'error' ) {
287 - unset( $addons[ $k ] );
288 - }
292 + if ( ! $addons ) {
293 + return self::fallback_plugin_list();
294 + }
295 +
296 + foreach ( $addons as $k => $addon ) {
297 + if ( empty( $addon['excerpt'] ) && $k !== 'error' ) {
298 + unset( $addons[ $k ] );
289 299 }
290 300 }
291 301
292 302 return $addons;
@@ -299,11 +309,9 @@
299 309 *
300 310 * @return int Count of addons.
301 311 */
302 312 public static function get_addons_count() {
303 - $addons = self::get_api_addons();
304 -
305 - return count( $addons );
313 + return count( self::get_api_addons() );
306 314 }
307 315
308 316 /**
309 317 * If the API is unable to connect, show something on the addons page
@@ -308,8 +316,9 @@
308 316 /**
309 317 * If the API is unable to connect, show something on the addons page
310 318 *
311 319 * @since 3.04.03
320 + *
312 321 * @return array
313 322 */
314 323 protected static function fallback_plugin_list() {
315 324 $list = array(
@@ -320,19 +329,19 @@
320 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.',
321 330 ),
322 331 'mailchimp' => array(
323 332 'title' => 'Mailchimp Forms',
324 - '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.',
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
325 334 ),
326 335 'registration' => array(
327 336 'title' => 'User Registration Forms',
328 337 'link' => 'downloads/user-registration/',
329 - '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.',
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
330 339 ),
331 340 'paypal' => array(
332 341 'title' => 'PayPal Standard Forms',
333 342 'link' => 'downloads/paypal-standard/',
334 - '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.',
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
335 344 ),
336 345 'stripe' => array(
337 346 'title' => 'Stripe Forms',
338 347 'docs' => 'knowledgebase/stripe/',
@@ -364,9 +373,9 @@
364 373 'excerpt' => 'Instantly add Bootstrap styling to all your Formidable forms.',
365 374 ),
366 375 'zapier' => array(
367 376 'title' => 'Zapier Forms',
368 - '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.',
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
369 378 ),
370 379 'signature' => array(
371 380 'title' => 'Digital Signature Forms',
372 381 'excerpt' => 'Add a signature field to your form. The user may write their signature with a trackpad/mouse or just type it.',
@@ -399,8 +408,9 @@
399 408 foreach ( $list as $k => $info ) {
400 409 $info['slug'] = $k;
401 410 $list[ $k ] = array_merge( $defaults, $info );
402 411 }
412 +
403 413 return $list;
404 414 }
405 415
406 416 /**
@@ -406,13 +416,13 @@
406 416 /**
407 417 * If Pro is missing but has been authenticated, include a download URL
408 418 *
409 419 * @since 3.04.03
420 + *
410 421 * @return string
411 422 */
412 423 public static function get_pro_download_url() {
413 - $license = self::get_pro_license();
414 - $api = new FrmFormApi( $license );
424 + $api = new FrmFormApi( self::get_pro_license() );
415 425 $downloads = $api->get_api_info();
416 426 $pro = self::get_pro_from_addons( $downloads );
417 427
418 428 return $pro['url'] ?? '';
@@ -419,12 +429,15 @@
419 429 }
420 430
421 431 /**
422 432 * @since 4.08
433 + *
434 + * @return string
423 435 */
424 436 public static function get_pro_license() {
425 437 $pro_cred_store = 'frmpro-credentials';
426 438 $pro_wpmu_store = 'frmpro-wpmu-sitewide';
439 +
427 440 if ( is_multisite() && get_site_option( $pro_wpmu_store ) ) {
428 441 $creds = get_site_option( $pro_cred_store );
429 442 } else {
430 443 $creds = get_option( $pro_cred_store );
@@ -434,16 +447,18 @@
434 447 return '';
435 448 }
436 449
437 450 $license = $creds['license'];
438 - if ( empty( $license ) ) {
451 +
452 + if ( ! $license ) {
439 453 return '';
440 454 }
441 455
442 - if ( strpos( $license, '-' ) ) {
443 - // this is a fix for licenses saved in the past
444 - $license = strtoupper( $license );
456 + if ( str_contains( $license, '-' ) ) {
457 + // This is a fix for licenses saved in the past
458 + return strtoupper( $license );
445 459 }
460 +
446 461 return $license;
447 462 }
448 463
449 464 /**
@@ -449,8 +464,9 @@
449 464 /**
450 465 * @since 4.08
451 466 *
452 467 * @param array $addons
468 + *
453 469 * @return array
454 470 */
455 471 protected static function get_pro_from_addons( $addons ) {
456 472 return $addons['93790'] ?? array();
@@ -457,12 +473,17 @@
457 473 }
458 474
459 475 /**
460 476 * @since 4.06
477 + * @since 6.31 Added the $force_type param.
478 + *
479 + * @param bool $force_type Whether to resolve grandfathered licenses to their real license type.
480 + *
481 + * @return string
461 482 */
462 - public static function license_type() {
483 + public static function license_type( $force_type = false ) {
463 484 if ( is_callable( 'FrmProAddonsController::license_type' ) ) {
464 - return FrmProAddonsController::license_type();
485 + return FrmProAddonsController::license_type( $force_type );
465 486 }
466 487
467 488 return 'free';
468 489 }
@@ -467,8 +488,122 @@
467 488 return 'free';
468 489 }
469 490
470 491 /**
492 + * Determine the license status for payment fee decisions.
493 + *
494 + * Mirrors determine_status_from_license_details in the Stripe Connect service,
495 + * including its 2016-04-26 grandfathered cutoff. Square and PayPal Commerce use a
496 + * later cutoff, so read their fees through payment_fees_apply instead.
497 + *
498 + * @since 6.31
499 + *
500 + * @return string 'active', 'expired', or 'free'.
501 + */
502 + public static function get_payment_license_status() {
503 + $version_info = self::get_primary_license_info();
504 +
505 + if ( ! $version_info ) {
506 + return 'free';
507 + }
508 +
509 + $error = $version_info['error'] ?? array();
510 +
511 + if ( is_array( $error ) && 'expired' === ( $error['code'] ?? '' ) ) {
512 + return 'expired';
513 + }
514 +
515 + if ( self::is_grandfathered_license() ) {
516 + $expires = self::get_grandfathered_expiration( $version_info );
517 +
518 + if ( $expires && gmdate( 'Y-m-d', $expires ) < '2016-04-26' ) {
519 + return 'free';
520 + }
521 + }
522 +
523 + if ( in_array( self::license_type( true ), array( 'elite', 'business' ), true ) ) {
524 + return 'active';
525 + }
526 +
527 + return 'free';
528 + }
529 +
530 + /**
531 + * Check if a connect service charges its per-transaction fee for this license.
532 + *
533 + * Square and PayPal Commerce launched long after lifetime licenses stopped being
534 + * sold, so their services charge every grandfathered license. Stripe's predates
535 + * them and exempts most of them, which get_payment_license_status mirrors.
536 + *
537 + * @since 6.34
538 + *
539 + * @param string $service The connect service: 'stripe', 'square', or 'paypal'.
540 + *
541 + * @return bool
542 + */
543 + public static function payment_fees_apply( $service ) {
544 + if ( 'active' !== self::get_payment_license_status() ) {
545 + return true;
546 + }
547 +
548 + if ( in_array( $service, array( 'square', 'paypal' ), true ) ) {
549 + return self::is_grandfathered_license();
550 + }
551 +
552 + return false;
553 + }
554 +
555 + /**
556 + * Check if the primary license is grandfathered.
557 + *
558 + * The API reports this on the Pro add-on itself, the same place
559 + * FrmProAddonsController::license_type reads it from. Older responses put it
560 + * in the top level error instead, so check both.
561 + *
562 + * @since 6.34
563 + *
564 + * @return bool
565 + */
566 + public static function is_grandfathered_license() {
567 + $version_info = self::get_primary_license_info();
568 +
569 + if ( ! $version_info ) {
570 + return false;
571 + }
572 +
573 + $error = $version_info['error'] ?? array();
574 +
575 + if ( is_array( $error ) && 'grandfathered' === ( $error['code'] ?? '' ) ) {
576 + return true;
577 + }
578 +
579 + $pro = self::get_pro_from_addons( $version_info );
580 +
581 + return 'grandfathered' === ( $pro['code'] ?? '' );
582 + }
583 +
584 + /**
585 + * Get the expiration timestamp reported alongside a grandfathered license.
586 + *
587 + * @since 6.34
588 + *
589 + * @param array $version_info The license info from get_primary_license_info.
590 + *
591 + * @return int
592 + */
593 + private static function get_grandfathered_expiration( $version_info ) {
594 + $error = $version_info['error'] ?? array();
595 +
596 + if ( is_array( $error ) && ! empty( $error['expires'] ) ) {
597 + return (int) $error['expires'];
598 + }
599 +
600 + $pro = self::get_pro_from_addons( $version_info );
601 +
602 + return (int) ( $pro['expires'] ?? 0 );
603 + }
604 +
605 + /**
471 606 * @since 4.0.01
472 607 *
473 608 * @return bool
474 609 */
@@ -473,18 +608,21 @@
473 608 * @return bool
474 609 */
475 610 public static function is_license_expired() {
476 611 $version_info = self::get_primary_license_info();
612 +
477 613 if ( ! isset( $version_info['error'] ) ) {
478 614 return false;
479 615 }
480 616
481 617 $expires = $version_info['error']['expires'] ?? 0;
482 - if ( empty( $expires ) || $expires > time() ) {
618 +
619 + if ( ! $expires || $expires > time() ) {
483 620 return false;
484 621 }
485 622
486 623 $rate_limited = ! empty( $version_info['response_code'] ) && 429 === (int) $version_info['response_code'];
624 +
487 625 if ( $rate_limited ) {
488 626 // Do not return false positives for rate limited responses.
489 627 return false;
490 628 }
@@ -499,11 +637,13 @@
499 637 * @return array|false
500 638 */
501 639 public static function get_primary_license_info() {
502 640 $installed_addons = apply_filters( 'frm_installed_addons', array() );
503 - if ( empty( $installed_addons ) || ! isset( $installed_addons['formidable_pro'] ) ) {
641 +
642 + if ( ! $installed_addons || ! isset( $installed_addons['formidable_pro'] ) ) {
504 643 return false;
505 644 }
645 +
506 646 $installed_addons = array(
507 647 'formidable_pro' => $installed_addons['formidable_pro'],
508 648 );
509 649
@@ -511,8 +651,12 @@
511 651 }
512 652
513 653 /**
514 654 * @since 3.04.03
655 + *
656 + * @param mixed $transient
657 + *
658 + * @return object
515 659 */
516 660 public static function check_update( $transient ) {
517 661 if ( ! FrmAppHelper::pro_is_installed() ) {
518 662 // Don't make any changes if only Lite is installed.
@@ -523,9 +667,10 @@
523 667 $transient = new stdClass();
524 668 }
525 669
526 670 $installed_addons = apply_filters( 'frm_installed_addons', array() );
527 - if ( empty( $installed_addons ) ) {
671 +
672 + if ( ! $installed_addons ) {
528 673 return $transient;
529 674 }
530 675
531 676 $version_info = self::fill_update_addon_info( $installed_addons );
@@ -539,14 +684,15 @@
539 684 continue;
540 685 }
541 686
542 687 $folder = $plugin->plugin;
543 - if ( empty( $folder ) ) {
688 +
689 + if ( ! $folder ) {
544 690 continue;
545 691 }
546 692
547 693 if ( ! self::is_installed( $folder ) ) {
548 - // don't show an update if the plugin isn't installed
694 + // Don't show an update if the plugin isn't installed
549 695 continue;
550 696 }
551 697
552 698 $wp_plugin = $wp_plugins[ $folder ] ?? array();
@@ -559,9 +705,8 @@
559 705 $transient->no_update[ $folder ] = $plugin;
560 706 }
561 707
562 708 $transient->checked[ $folder ] = $wp_version;
563 -
564 709 }//end foreach
565 710
566 711 return $transient;
567 712 }
@@ -571,8 +716,9 @@
571 716 * Because this gets called on "pre_set_site_transient_update_plugins" an old version of FrmAppHelper may be loaded on plugin update.
572 717 * This means that trying to access FrmAppHelper::get_plugins when upgrading from a Lite version before v5.5 results in a one-off error.
573 718 *
574 719 * @since 5.5.2
720 + *
575 721 * @return array
576 722 */
577 723 protected static function get_plugins() {
578 724 if ( ! function_exists( 'get_plugins' ) ) {
@@ -607,43 +753,49 @@
607 753 $version_info = array();
608 754
609 755 foreach ( $installed_addons as $addon ) {
610 756 if ( $addon->store_url !== 'https://formidableforms.com' ) {
611 - // check if this is a third-party addon
757 + // Check if this is a third-party addon
612 758 continue;
613 759 }
614 760
615 761 $new_license = $addon->license;
616 - if ( empty( $new_license ) || in_array( $new_license, $checked_licenses ) ) {
762 +
763 + if ( ! $new_license || in_array( $new_license, $checked_licenses, true ) ) {
617 764 continue;
618 765 }
619 766
620 767 $checked_licenses[] = $new_license;
768 + $api = new FrmFormApi( $new_license );
621 769
622 - $api = new FrmFormApi( $new_license );
623 - if ( empty( $version_info ) ) {
770 + if ( ! $version_info ) {
624 771 $version_info = $api->get_api_info();
625 772 continue;
626 773 }
627 774
628 775 $plugin = $api->get_addon_for_license( $addon, $version_info );
629 - if ( empty( $plugin ) ) {
776 +
777 + if ( ! $plugin ) {
630 778 continue;
631 779 }
632 780
633 781 $download_id = $plugin['id'] ?? 0;
634 - if ( ! empty( $download_id ) && ! isset( $version_info[ $download_id ]['package'] ) ) {
635 - // if this addon is using its own license, get the update url
636 - $addon_info = $api->get_api_info();
637 782
638 - $version_info[ $download_id ] = $addon_info[ $download_id ];
639 - if ( isset( $addon_info['error'] ) ) {
640 - $version_info[ $download_id ]['error'] = array(
641 - 'message' => $addon_info['error']['message'],
642 - 'code' => $addon_info['error']['code'],
643 - );
644 - }
783 + if ( ! $download_id || isset( $version_info[ $download_id ]['package'] ) ) {
784 + continue;
645 785 }
786 +
787 + // If this addon is using its own license, get the update url
788 + $addon_info = $api->get_api_info();
789 +
790 + $version_info[ $download_id ] = $addon_info[ $download_id ];
791 +
792 + if ( isset( $addon_info['error'] ) ) {
793 + $version_info[ $download_id ]['error'] = array(
794 + 'message' => $addon_info['error']['message'],
795 + 'code' => $addon_info['error']['code'],
796 + );
797 + }
646 798 }//end foreach
647 799
648 800 return $version_info;
649 801 }
@@ -651,9 +803,11 @@
651 803 /**
652 804 * Get the action link for an addon that isn't active.
653 805 *
654 806 * @since 3.06.03
807 + *
655 808 * @param string $plugin The plugin slug.
809 + *
656 810 * @return array
657 811 */
658 812 public static function install_link( $plugin ) {
659 813 $link = array();
@@ -675,9 +829,9 @@
675 829 'categories' => $addon['categories'],
676 830 );
677 831 }
678 832
679 - if ( ! empty( $link ) ) {
833 + if ( $link ) {
680 834 $link['status'] = $addon['status']['type'];
681 835 }
682 836 } elseif ( current_user_can( 'activate_plugins' ) && self::is_installed( 'formidable-' . $plugin . '/formidable-' . $plugin . '.php' ) ) {
683 837 $link = array(
@@ -689,34 +843,81 @@
689 843 return $link;
690 844 }
691 845
692 846 /**
847 + * Get the JSON-encoded install data for a plugin update.
848 + *
849 + * @since 6.29
850 + *
851 + * @param string $addon_slug The addon slug (e.g. 'pro', 'dates').
852 + *
853 + * @return string JSON-encoded install data, or empty string if no URL is available.
854 + */
855 + public static function get_update_install_data( $addon_slug ) {
856 + $upgrading = self::install_link( $addon_slug );
857 +
858 + if ( isset( $upgrading['class'] ) && 'frm-install-addon' === $upgrading['class'] ) {
859 + return (string) json_encode( $upgrading );
860 + }
861 +
862 + if ( 'pro' === $addon_slug ) {
863 + $download_url = self::get_pro_download_url();
864 + $plugin_file = 'formidable-pro/formidable-pro.php';
865 + } else {
866 + $addon_data = self::get_addon( $addon_slug );
867 + $download_url = $addon_data && ! empty( $addon_data['url'] ) ? $addon_data['url'] : '';
868 + $plugin_file = $addon_data && ! empty( $addon_data['plugin'] ) ? $addon_data['plugin'] : 'formidable-' . $addon_slug . '/formidable-' . $addon_slug . '.php';
869 + }
870 +
871 + if ( ! $download_url ) {
872 + $update_plugins = get_site_transient( 'update_plugins' );
873 + $plugin_update = $update_plugins->response[ $plugin_file ] ?? null;
874 + $download_url = $plugin_update && ! empty( $plugin_update->package ) ? $plugin_update->package : '';
875 + }
876 +
877 + return $download_url ? (string) json_encode(
878 + array(
879 + 'url' => $download_url,
880 + 'class' => 'frm-install-addon',
881 + )
882 + ) : '';
883 + }
884 +
885 + /**
693 886 * @since 4.09
887 + *
694 888 * @param string $plugin The plugin slug.
889 + *
695 890 * @return array|false
696 891 */
697 892 public static function get_addon( $plugin ) {
698 893 $addons = self::get_api_addons();
699 894 self::prepare_addons( $addons );
895 +
700 896 foreach ( $addons as $addon ) {
701 897 $slug = explode( '/', $addon['plugin'] );
898 +
702 899 if ( $slug[0] === 'formidable-' . $plugin ) {
703 900 return $addon;
704 901 }
705 902 }
903 +
706 904 return false;
707 905 }
708 906
709 907 /**
710 908 * @since 4.09
909 + *
711 910 * @return string
712 911 */
713 912 protected static function get_license_type() {
714 913 $license_type = '';
715 914 $addons = self::get_api_addons();
915 +
716 916 if ( isset( $addons['error'] ) && isset( $addons['error']['type'] ) ) {
717 - $license_type = $addons['error']['type'];
917 + return $addons['error']['type'];
718 918 }
919 +
719 920 return $license_type;
720 921 }
721 922
722 923 /**
@@ -729,11 +930,12 @@
729 930 */
730 931 public static function get_addon_for_license( $addons, $license ) {
731 932 $download_id = $license->download_id;
732 933 $plugin = array();
733 - if ( empty( $download_id ) && ! empty( $addons ) ) {
934 +
935 + if ( ! $download_id && $addons ) {
734 936 foreach ( $addons as $addon ) {
735 - if ( strtolower( $license->plugin_name ) === strtolower( $addon['title'] ) ) {
937 + if ( 0 === strcasecmp( $license->plugin_name, $addon['title'] ) ) {
736 938 return $addon;
737 939 }
738 940 }
739 941 } elseif ( isset( $addons[ $download_id ] ) ) {
@@ -743,29 +945,33 @@
743 945 return $plugin;
744 946 }
745 947
746 948 /**
949 + * @param array $addons
950 + *
747 951 * @return void
748 952 */
749 953 protected static function prepare_addons( &$addons ) {
954 + // Reset categories to prevent count accumulation across multiple calls.
955 + self::$categories = array();
956 +
750 957 $activate_url = '';
958 +
751 959 if ( current_user_can( 'activate_plugins' ) ) {
752 960 $activate_url = add_query_arg( array( 'action' => 'activate' ), admin_url( 'plugins.php' ) );
753 961 }
754 962
755 963 $loop_addons = $addons;
964 +
756 965 foreach ( $loop_addons as $id => $addon ) {
757 966 if ( is_numeric( $id ) ) {
758 967 $slug = str_replace( array( '-wordpress-plugin', '-wordpress' ), '', $addon['slug'] );
759 968 $file_name = $addon['plugin'];
760 969 } else {
761 - $slug = $id;
762 - if ( isset( $addon['file'] ) ) {
763 - $base_file = $addon['file'];
764 - } else {
765 - $base_file = 'formidable-' . $slug;
766 - }
970 + $slug = $id;
971 + $base_file = $addon['file'] ?? 'formidable-' . $slug;
767 972 $file_name = $base_file . '/' . $base_file . '.php';
973 +
768 974 if ( ! isset( $addon['plugin'] ) ) {
769 975 $addon['plugin'] = $file_name;
770 976 }
771 977 }
@@ -770,10 +976,17 @@
770 976 }
771 977 }
772 978
773 979 $addon['installed'] = self::is_installed( $file_name );
980 +
981 + if ( 'highrise' === $slug && ! $addon['installed'] ) {
982 + unset( $addons[ $id ] );
983 + continue;
984 + }
985 +
774 986 if ( $addon['installed'] && 'formidable-views/formidable-views.php' === $file_name ) {
775 987 $active_views_version = self::get_active_views_version();
988 +
776 989 if ( false !== $active_views_version && $slug !== $active_views_version ) {
777 990 $addon['installed'] = false;
778 991 }
779 992 }
@@ -779,9 +992,9 @@
779 992 }
780 993
781 994 $addon['activate_url'] = '';
782 995
783 - if ( $addon['installed'] && ! empty( $activate_url ) && ! self::is_plugin_active( $file_name, $slug ) ) {
996 + if ( $addon['installed'] && $activate_url && ! self::is_plugin_active( $file_name, $slug ) ) {
784 997 $addon['activate_url'] = add_query_arg(
785 998 array(
786 999 '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $file_name ),
787 1000 'plugin' => $file_name,
@@ -807,8 +1020,11 @@
807 1020 }//end foreach
808 1021 }
809 1022
810 1023 /**
1024 + * @param string $file_name
1025 + * @param string $slug
1026 + *
811 1027 * @return bool
812 1028 */
813 1029 private static function is_plugin_active( $file_name, $slug ) {
814 1030 if ( 'formidable-views/formidable-views.php' === $file_name ) {
@@ -823,8 +1039,9 @@
823 1039 private static function get_active_views_version() {
824 1040 if ( ! is_callable( 'FrmViewsAppHelper::plugin_version' ) ) {
825 1041 return false;
826 1042 }
1043 +
827 1044 $plugin_version = FrmViewsAppHelper::plugin_version();
828 1045 return version_compare( $plugin_version, '5.0', '>=' ) ? 'visual-views' : 'views';
829 1046 }
830 1047
@@ -830,17 +1047,21 @@
830 1047
831 1048 /**
832 1049 * @since 3.04.02
833 1050 *
1051 + * @param string $link
1052 + *
834 1053 * @return void
835 1054 */
836 1055 protected static function prepare_addon_link( &$link ) {
837 1056 $site_url = 'https://formidableforms.com/';
838 - if ( strpos( $link, 'http' ) !== 0 ) {
1057 +
1058 + if ( ! str_starts_with( $link, 'http' ) ) {
839 1059 $link = $site_url . $link;
840 1060 }
841 - $link = FrmAppHelper::make_affiliate_url( $link );
842 1061
1062 + $link = FrmAppHelper::make_affiliate_url( $link );
1063 +
843 1064 $utm = array(
844 1065 'campaign' => 'addons',
845 1066 );
846 1067 $link = FrmAppHelper::maybe_add_missing_utm( $link, $utm );
@@ -851,8 +1072,10 @@
851 1072 * installed, active, not installed
852 1073 *
853 1074 * @since 3.04.02
854 1075 *
1076 + * @param array $addon
1077 + *
855 1078 * @return void
856 1079 */
857 1080 protected static function set_addon_status( &$addon ) {
858 1081 if ( ! empty( $addon['activate_url'] ) ) {
@@ -879,8 +1102,9 @@
879 1102 * @param string $plugin
880 1103 * @param string $redirect
881 1104 * @param bool $network_wide
882 1105 * @param bool $silent
1106 + *
883 1107 * @return WP_Error|null Null on success, WP_Error on invalid file.
884 1108 */
885 1109 protected static function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) {
886 1110 if ( ! function_exists( 'activate_plugin' ) ) {
@@ -895,8 +1119,9 @@
895 1119 * @since 6.8
896 1120 *
897 1121 * @param string $plugin
898 1122 * @param bool $silent
1123 + *
899 1124 * @return void
900 1125 */
901 1126 protected static function deactivate_plugin( $plugin, $silent = false ) {
902 1127 if ( ! function_exists( 'deactivate_plugins' ) ) {
@@ -910,8 +1135,9 @@
910 1135 *
911 1136 * @since 6.8
912 1137 *
913 1138 * @param string $plugin
1139 + *
914 1140 * @return true|WP_Error True on success, WP_Error on invalid file.
915 1141 */
916 1142 protected static function uninstall_plugin( $plugin ) {
917 1143 if ( ! current_user_can( 'delete_plugins' ) ) {
@@ -924,13 +1150,9 @@
924 1150
925 1151 self::deactivate_plugin( $plugin, true );
926 1152 $result = delete_plugins( array( $plugin ) );
927 1153
928 - if ( is_wp_error( $result ) ) {
929 - return $result;
930 - }
931 -
932 - return true;
1154 + return is_wp_error( $result ) ? $result : true;
933 1155 }
934 1156
935 1157 /**
936 1158 * @since 5.0.10
@@ -937,9 +1159,9 @@
937 1159 *
938 1160 * @return string
939 1161 */
940 1162 protected static function get_current_plugin() {
941 - if ( empty( self::$plugin ) ) {
1163 + if ( ! self::$plugin ) {
942 1164 self::$plugin = FrmAppHelper::get_param( 'plugin', '', 'post', 'esc_url_raw' );
943 1165 }
944 1166 return self::$plugin;
945 1167 }
@@ -946,9 +1168,9 @@
946 1168
947 1169 /**
948 1170 * @since 4.08
949 1171 *
950 - * @return array|void
1172 + * @return mixed[]|null
951 1173 */
952 1174 protected static function download_and_activate() {
953 1175 if ( is_admin() ) {
954 1176 FrmAppHelper::set_current_screen_and_hook_suffix();
@@ -956,12 +1178,14 @@
956 1178
957 1179 self::maybe_show_cred_form();
958 1180
959 1181 $installed = self::install_addon();
1182 +
960 1183 if ( is_array( $installed ) && isset( $installed['message'] ) ) {
961 1184 return $installed;
962 1185 }
963 1186 self::handle_addon_action( $installed, 'activate' );
1187 + return null;
964 1188 }
965 1189
966 1190 /**
967 1191 * @since 3.04.02
@@ -991,9 +1215,8 @@
991 1215
992 1216 if ( $show_form ) {
993 1217 $form = ob_get_clean();
994 1218 $message = __( 'Sorry, your site requires FTP authentication. Please download plugins from FormidableForms.com and install them manually.', 'formidable' );
995 - $data = $form;
996 1219 $response = array(
997 1220 'success' => false,
998 1221 'message' => $message,
999 1222 'form' => $form,
@@ -1021,8 +1244,10 @@
1021 1244 * We do not need any extra credentials if we have gotten this far,
1022 1245 * so let's install the plugin.
1023 1246 *
1024 1247 * @since 3.04.02
1248 + *
1249 + * @return array|string
1025 1250 */
1026 1251 protected static function install_addon() {
1027 1252 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1028 1253
@@ -1036,14 +1261,15 @@
1036 1261 }
1037 1262
1038 1263 // Create the plugin upgrader with our custom skin.
1039 1264 $installer = new Plugin_Upgrader( new FrmInstallerSkin() );
1040 - $installer->install( $download_url );
1265 + $installer->install( $download_url, array( 'overwrite_package' => true ) );
1041 1266
1042 1267 // Flush the cache and return the newly installed plugin basename.
1043 1268 wp_cache_flush();
1044 1269
1045 1270 $plugin = $installer->plugin_info();
1271 +
1046 1272 if ( ! $plugin ) {
1047 1273 return array(
1048 1274 'message' => 'Plugin was not installed. ' . $installer->result,
1049 1275 'success' => false,
@@ -1048,8 +1274,9 @@
1048 1274 'message' => 'Plugin was not installed. ' . $installer->result,
1049 1275 'success' => false,
1050 1276 );
1051 1277 }
1278 +
1052 1279 return $plugin;
1053 1280 }
1054 1281
1055 1282 /**
@@ -1113,8 +1340,9 @@
1113 1340 * @since 6.8
1114 1341 *
1115 1342 * @param callable $action_callback The specific add-on action to be executed.
1116 1343 * @param callable|null $response_callback Optional. The response handling callback. Default null.
1344 + *
1117 1345 * @return void
1118 1346 */
1119 1347 private static function process_addon_action( $action_callback, $response_callback = null ) {
1120 1348 self::install_addon_permissions();
@@ -1137,13 +1365,14 @@
1137 1365 * @since 6.8
1138 1366 *
1139 1367 * @param string $installed The plugin folder name with file name.
1140 1368 * @param string $action The action type ('activate', 'deactivate', 'uninstall').
1141 - * @return array|void
1369 + *
1370 + * @return mixed[]|null
1142 1371 */
1143 1372 protected static function handle_addon_action( $installed, $action ) {
1144 1373 if ( ! $installed || ! $action ) {
1145 - return;
1374 + return null;
1146 1375 }
1147 1376
1148 1377 $result = null;
1149 1378 switch ( $action ) {
@@ -1163,8 +1392,9 @@
1163 1392 if ( $result->get_error_code() !== 'no_plugin_header' ) {
1164 1393 if ( wp_doing_ajax() ) {
1165 1394 wp_send_json_error( array( 'error' => $result->get_error_message() ) );
1166 1395 }
1396 +
1167 1397 return array(
1168 1398 'message' => $result->get_error_message(),
1169 1399 'success' => false,
1170 1400 );
@@ -1178,17 +1408,14 @@
1178 1408 * @return array
1179 1409 */
1180 1410 private static function get_addon_activation_response() {
1181 1411 $activating_page = self::get_activating_page();
1412 + $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
1182 1413
1183 - $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' );
1184 -
1185 - $response = array(
1414 + return array(
1186 1415 'message' => $message,
1187 1416 'saveAndReload' => $activating_page,
1188 1417 );
1189 -
1190 - return $response;
1191 1418 }
1192 1419
1193 1420 /**
1194 1421 * Return a string that reflects the page from which the addon is being activated on,
@@ -1197,17 +1424,14 @@
1197 1424 * @return string
1198 1425 */
1199 1426 private static function get_activating_page() {
1200 1427 $referer = FrmAppHelper::get_server_value( 'HTTP_REFERER' );
1201 - if ( false !== strpos( $referer, 'frm_action=settings' ) ) {
1428 +
1429 + if ( str_contains( $referer, 'frm_action=settings' ) ) {
1202 1430 return 'settings';
1203 1431 }
1204 1432
1205 - if ( false !== strpos( $referer, 'frm_action=edit' ) ) {
1206 - return 'form_builder';
1207 - }
1208 -
1209 - return '';
1433 + return str_contains( $referer, 'frm_action=edit' ) ? 'form_builder' : '';
1210 1434 }
1211 1435
1212 1436 /**
1213 1437 * Run security checks before installing
@@ -1218,12 +1442,14 @@
1218 1442 */
1219 1443 protected static function install_addon_permissions() {
1220 1444 check_ajax_referer( 'frm_ajax', 'nonce' );
1221 1445
1222 - if ( ! current_user_can( 'activate_plugins' ) || ! self::get_current_plugin() ) {
1223 - echo json_encode( true );
1224 - wp_die();
1446 + if ( current_user_can( 'activate_plugins' ) && self::get_current_plugin() ) {
1447 + return;
1225 1448 }
1449 +
1450 + echo json_encode( true );
1451 + wp_die();
1226 1452 }
1227 1453
1228 1454 /**
1229 1455 * @since 4.08
@@ -1231,12 +1457,14 @@
1231 1457 * @return string
1232 1458 */
1233 1459 public static function connect_link() {
1234 1460 $auth = get_option( 'frm_connect_token' );
1235 - if ( empty( $auth ) ) {
1461 +
1462 + if ( ! $auth ) {
1236 1463 $auth = hash( 'sha512', wp_rand() );
1237 - update_option( 'frm_connect_token', $auth, 'no' );
1464 + update_option( 'frm_connect_token', $auth, false );
1238 1465 }
1466 +
1239 1467 $page = FrmAppHelper::simple_get( 'page', 'sanitize_title', 'formidable-settings' );
1240 1468 $link = 'https://formidableforms.com/api-connect/';
1241 1469 $args = array(
1242 1470 'v' => 2,
@@ -1264,9 +1492,10 @@
1264 1492 $post_url = FrmAppHelper::get_param( 'file_url', '', 'request', 'sanitize_text_field' );
1265 1493
1266 1494 // The download link is not required if already installed.
1267 1495 $is_installed = FrmAppHelper::pro_is_included();
1268 - $file_missing = ! $is_installed && empty( $post_url );
1496 + $file_missing = ! $is_installed && ! $post_url;
1497 +
1269 1498 if ( ! $post_auth || $file_missing ) {
1270 1499 return false;
1271 1500 }
1272 1501
@@ -1271,13 +1500,9 @@
1271 1500 }
1272 1501
1273 1502 // Verify auth.
1274 1503 $auth = get_option( 'frm_connect_token' );
1275 - if ( empty( $auth ) || ! hash_equals( $auth, $post_auth ) ) {
1276 - return false;
1277 - }
1278 -
1279 - return true;
1504 + return $auth && hash_equals( $auth, $post_auth );
1280 1505 }
1281 1506
1282 1507 /**
1283 1508 * Install and/or activate the add-on file.
@@ -1288,17 +1513,17 @@
1288 1513 */
1289 1514 public static function install_addon_api() {
1290 1515 self::$plugin = FrmAppHelper::get_param( 'file_url', '', 'request', 'esc_url_raw' );
1291 1516
1292 - $error = esc_html__( 'Could not install an upgrade. Please download from formidableforms.com and install manually.', 'formidable' );
1293 -
1294 1517 // Delete so cannot replay.
1295 1518 delete_option( 'frm_connect_token' );
1296 1519
1297 1520 // It's already installed and active.
1298 1521 $active = self::activate_plugin( 'formidable-pro/formidable-pro.php', false, false, true );
1522 +
1299 1523 if ( is_wp_error( $active ) ) {
1300 1524 $response = self::maybe_download_and_activate();
1525 +
1301 1526 if ( is_array( $response ) ) {
1302 1527 // The download failed.
1303 1528 return $response;
1304 1529 }
@@ -1304,11 +1529,12 @@
1304 1529 }
1305 1530 }
1306 1531
1307 1532 // If empty license, save it now.
1308 - if ( empty( self::get_pro_license() ) && function_exists( 'load_formidable_pro' ) ) {
1533 + if ( ! self::get_pro_license() && function_exists( 'load_formidable_pro' ) ) {
1309 1534 load_formidable_pro();
1310 1535 $license = stripslashes( FrmAppHelper::get_param( 'key', '', 'request', 'sanitize_text_field' ) );
1536 +
1311 1537 if ( ! $license ) {
1312 1538 return array(
1313 1539 'success' => false,
1314 1540 'error' => 'That site does not have a valid license key.',
@@ -1315,8 +1541,9 @@
1315 1541 );
1316 1542 }
1317 1543
1318 1544 $response = FrmAddon::activate_license_for_plugin( $license, 'formidable_pro' );
1545 +
1319 1546 if ( ! $response['success'] ) {
1320 1547 // Could not activate license.
1321 1548 return $response;
1322 1549 }
@@ -1341,8 +1568,9 @@
1341 1568 }
1342 1569
1343 1570 // Download plugin now.
1344 1571 $response = self::download_and_activate();
1572 +
1345 1573 if ( is_array( $response ) && isset( $response['success'] ) ) {
1346 1574 // The download failed.
1347 1575 return $response;
1348 1576 }
@@ -1356,8 +1584,9 @@
1356 1584 * @since 4.09
1357 1585 *
1358 1586 * @param string $plugin
1359 1587 * @param array|string $upgrade_link_args
1588 + *
1360 1589 * @return void
1361 1590 */
1362 1591 public static function conditional_action_button( $plugin, $upgrade_link_args ) {
1363 1592 if ( is_callable( 'FrmProAddonsController::conditional_action_button' ) ) {
@@ -1392,8 +1621,9 @@
1392 1621 * @type false|string $license_type
1393 1622 * @type string $plan_required
1394 1623 * @type string $upgrade_link
1395 1624 * }
1625 + *
1396 1626 * @return void
1397 1627 */
1398 1628 public static function show_conditional_action_button( $atts ) {
1399 1629 if ( is_callable( 'FrmProAddonsController::show_conditional_action_button' ) ) {
@@ -1426,16 +1656,19 @@
1426 1656 $upgrade_link = FrmAppHelper::admin_upgrade_link( 'addons', $addon['link'] );
1427 1657 }
1428 1658
1429 1659 $class = ! empty( $atts['class'] ) ? $atts['class'] : '';
1430 - if ( strpos( $class, 'frm-button' ) === false ) {
1660 +
1661 + if ( ! str_contains( $class, 'frm-button' ) ) {
1431 1662 $class .= ' frm-button-secondary frm-button-sm';
1432 1663 }
1664 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1433 1665 ?>
1434 - <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' ); ?>">
1666 + <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 ?>
1435 1667 <?php echo esc_html( $text ); ?>
1436 1668 </a>
1437 1669 <?php
1670 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1438 1671 }
1439 1672
1440 1673 /**
1441 1674 * @since 3.04.02
@@ -1445,8 +1678,9 @@
1445 1678 public static function ajax_install_addon() {
1446 1679 self::install_addon_permissions();
1447 1680
1448 1681 $result = self::download_and_activate();
1682 +
1449 1683 if ( isset( $result['success'] ) && ! $result['success'] ) {
1450 1684 echo json_encode( $result );
1451 1685 wp_die();
1452 1686 }
@@ -1479,9 +1713,8 @@
1479 1713 $allowed_url_list = apply_filters( 'frm_allowed_external_urls', $allowed_url_list );
1480 1714
1481 1715 if ( ! is_array( $allowed_url_list ) ) {
1482 1716 _doing_it_wrong( __METHOD__, 'Only an array of URLs could be used within this filter.', '6.3.1' );
1483 -
1484 1717 return array();
1485 1718 }
1486 1719
1487 1720 return $allowed_url_list;
@@ -1491,8 +1724,10 @@
1491 1724 * Gets required plan for an addon.
1492 1725 *
1493 1726 * @since 6.4.2
1494 1727 *
1728 + * @param int|string $addon_id
1729 + *
1495 1730 * @return string Empty string if no plan is required for active license.
1496 1731 */
1497 1732 public static function get_addon_required_plan( $addon_id ) {
1498 1733 $api = new FrmFormApi();
@@ -1503,9 +1738,9 @@
1503 1738 $requires = FrmFormsHelper::get_plan_required( $dates );
1504 1739 }
1505 1740
1506 1741 if ( ! isset( $requires ) || ! is_string( $requires ) ) {
1507 - $requires = '';
1742 + return '';
1508 1743 }
1509 1744
1510 1745 return $requires;
1511 1746 }