| @@ -156,9 +156,10 @@ | ||
| 156 | 156 | 'pro' => array( |
| 157 | 157 | 'title' => 'Formidable Forms Pro', |
| 158 | 158 | 'slug' => 'formidable-pro', |
| 159 | 159 | 'released' => '2011-02-05', |
| 160 | - 'docs' => 'knowledgebase/', | |
| 160 | + 'docs' => 'knowledgebase/what-is-the-difference-between-the-lite-free-and-pro-version/', | |
| 161 | + 'docs_label' => __( 'Why Upgrade', 'formidable' ), | |
| 161 | 162 | 'categories' => array( 'basic', 'plus', 'business', 'elite' ), |
| 162 | 163 | 'excerpt' => 'Create calculators, surveys, smart forms, and data-driven applications. Build directories, real estate listings, job boards, and much more.', |
| 163 | 164 | ), |
| 164 | 165 | ); |
| @@ -193,12 +194,14 @@ | ||
| 193 | 194 | ); |
| 194 | 195 | |
| 195 | 196 | // Extract the elements to move |
| 196 | 197 | foreach ( $plans as $plan ) { |
| 197 | - if ( isset( self::$categories[ $plan ] ) ) { | |
| 198 | - $bottom_categories[ $plan ] = self::$categories[ $plan ]; | |
| 199 | - unset( self::$categories[ $plan ] ); | |
| 198 | + if ( ! isset( self::$categories[ $plan ] ) ) { | |
| 199 | + continue; | |
| 200 | 200 | } |
| 201 | + | |
| 202 | + $bottom_categories[ $plan ] = self::$categories[ $plan ]; | |
| 203 | + unset( self::$categories[ $plan ] ); | |
| 201 | 204 | } |
| 202 | 205 | |
| 203 | 206 | $special_categories = array(); |
| 204 | 207 | |
| @@ -286,14 +289,14 @@ | ||
| 286 | 289 | $api = new FrmFormApi(); |
| 287 | 290 | $addons = $api->get_api_info(); |
| 288 | 291 | |
| 289 | 292 | if ( ! $addons ) { |
| 290 | - $addons = self::fallback_plugin_list(); | |
| 291 | - } else { | |
| 292 | - foreach ( $addons as $k => $addon ) { | |
| 293 | - if ( empty( $addon['excerpt'] ) && $k !== 'error' ) { | |
| 294 | - unset( $addons[ $k ] ); | |
| 295 | - } | |
| 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 ] ); | |
| 296 | 299 | } |
| 297 | 300 | } |
| 298 | 301 | |
| 299 | 302 | return $addons; |
| @@ -306,10 +309,9 @@ | ||
| 306 | 309 | * |
| 307 | 310 | * @return int Count of addons. |
| 308 | 311 | */ |
| 309 | 312 | public static function get_addons_count() { |
| 310 | - $addons = self::get_api_addons(); | |
| 311 | - return count( $addons ); | |
| 313 | + return count( self::get_api_addons() ); | |
| 312 | 314 | } |
| 313 | 315 | |
| 314 | 316 | /** |
| 315 | 317 | * If the API is unable to connect, show something on the addons page |
| @@ -418,10 +420,9 @@ | ||
| 418 | 420 | * |
| 419 | 421 | * @return string |
| 420 | 422 | */ |
| 421 | 423 | public static function get_pro_download_url() { |
| 422 | - $license = self::get_pro_license(); | |
| 423 | - $api = new FrmFormApi( $license ); | |
| 424 | + $api = new FrmFormApi( self::get_pro_license() ); | |
| 424 | 425 | $downloads = $api->get_api_info(); |
| 425 | 426 | $pro = self::get_pro_from_addons( $downloads ); |
| 426 | 427 | |
| 427 | 428 | return $pro['url'] ?? ''; |
| @@ -472,14 +473,17 @@ | ||
| 472 | 473 | } |
| 473 | 474 | |
| 474 | 475 | /** |
| 475 | 476 | * @since 4.06 |
| 477 | + * @since 6.31 Added the $force_type param. | |
| 476 | 478 | * |
| 479 | + * @param bool $force_type Whether to resolve grandfathered licenses to their real license type. | |
| 480 | + * | |
| 477 | 481 | * @return string |
| 478 | 482 | */ |
| 479 | - public static function license_type() { | |
| 483 | + public static function license_type( $force_type = false ) { | |
| 480 | 484 | if ( is_callable( 'FrmProAddonsController::license_type' ) ) { |
| 481 | - return FrmProAddonsController::license_type(); | |
| 485 | + return FrmProAddonsController::license_type( $force_type ); | |
| 482 | 486 | } |
| 483 | 487 | |
| 484 | 488 | return 'free'; |
| 485 | 489 | } |
| @@ -484,8 +488,122 @@ | ||
| 484 | 488 | return 'free'; |
| 485 | 489 | } |
| 486 | 490 | |
| 487 | 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 | + /** | |
| 488 | 606 | * @since 4.0.01 |
| 489 | 607 | * |
| 490 | 608 | * @return bool |
| 491 | 609 | */ |
| @@ -725,8 +843,47 @@ | ||
| 725 | 843 | return $link; |
| 726 | 844 | } |
| 727 | 845 | |
| 728 | 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 | + /** | |
| 729 | 886 | * @since 4.09 |
| 730 | 887 | * |
| 731 | 888 | * @param string $plugin The plugin slug. |
| 732 | 889 | * |
| @@ -820,8 +977,13 @@ | ||
| 820 | 977 | } |
| 821 | 978 | |
| 822 | 979 | $addon['installed'] = self::is_installed( $file_name ); |
| 823 | 980 | |
| 981 | + if ( 'highrise' === $slug && ! $addon['installed'] ) { | |
| 982 | + unset( $addons[ $id ] ); | |
| 983 | + continue; | |
| 984 | + } | |
| 985 | + | |
| 824 | 986 | if ( $addon['installed'] && 'formidable-views/formidable-views.php' === $file_name ) { |
| 825 | 987 | $active_views_version = self::get_active_views_version(); |
| 826 | 988 | |
| 827 | 989 | if ( false !== $active_views_version && $slug !== $active_views_version ) { |
| @@ -997,9 +1159,9 @@ | ||
| 997 | 1159 | * |
| 998 | 1160 | * @return string |
| 999 | 1161 | */ |
| 1000 | 1162 | protected static function get_current_plugin() { |
| 1001 | - if ( empty( self::$plugin ) ) { | |
| 1163 | + if ( ! self::$plugin ) { | |
| 1002 | 1164 | self::$plugin = FrmAppHelper::get_param( 'plugin', '', 'post', 'esc_url_raw' ); |
| 1003 | 1165 | } |
| 1004 | 1166 | return self::$plugin; |
| 1005 | 1167 | } |
| @@ -1099,9 +1261,9 @@ | ||
| 1099 | 1261 | } |
| 1100 | 1262 | |
| 1101 | 1263 | // Create the plugin upgrader with our custom skin. |
| 1102 | 1264 | $installer = new Plugin_Upgrader( new FrmInstallerSkin() ); |
| 1103 | - $installer->install( $download_url ); | |
| 1265 | + $installer->install( $download_url, array( 'overwrite_package' => true ) ); | |
| 1104 | 1266 | |
| 1105 | 1267 | // Flush the cache and return the newly installed plugin basename. |
| 1106 | 1268 | wp_cache_flush(); |
| 1107 | 1269 | |