diff --git a/classes/controllers/FrmAddonsController.php b/classes/controllers/FrmAddonsController.php
index b0d90d1b37..4ac16dd37d 100644
--- a/classes/controllers/FrmAddonsController.php
+++ b/classes/controllers/FrmAddonsController.php
@@ -97,7 +97,7 @@ public static function menu() {
}
$label = __( 'Add-Ons', 'formidable' );
- $label = '' . $label . '';
+ $label = '' . esc_html( $label ) . '';
add_submenu_page( 'formidable', 'Formidable | ' . __( 'Add-Ons', 'formidable' ), $label, 'frm_view_forms', 'formidable-addons', 'FrmAddonsController::list_addons' );
@@ -105,10 +105,15 @@ public static function menu() {
remove_submenu_page( 'formidable', 'formidable' );
if ( ! FrmAppHelper::pro_is_installed() ) {
+ $cta_text = FrmSalesApi::get_best_sale_value( 'menu_cta_text' );
+ if ( ! $cta_text ) {
+ $cta_text = __( 'Upgrade', 'formidable' );
+ }
+
add_submenu_page(
'formidable',
'Formidable | ' . __( 'Upgrade', 'formidable' ),
- '',
+ '',
'frm_view_forms',
'formidable-pro-upgrade',
function () {
@@ -119,7 +124,7 @@ function () {
} elseif ( 'formidable-pro-upgrade' === FrmAppHelper::get_param( 'page' ) ) {
wp_safe_redirect( admin_url( 'admin.php?page=formidable' ) );
exit;
- }
+ }//end if
}
/**
diff --git a/classes/controllers/FrmAppController.php b/classes/controllers/FrmAppController.php
index 977dc81ad3..b00550a184 100644
--- a/classes/controllers/FrmAppController.php
+++ b/classes/controllers/FrmAppController.php
@@ -337,16 +337,31 @@ private static function get_form_nav_items( $form ) {
return (array) apply_filters( 'frm_form_nav_list', $nav_items, $nav_args );
}
- // Adds a settings link to the plugins page
/**
+ * Adds a settings link to the plugins page
+ *
+ * @param array $links
* @return array
*/
public static function settings_link( $links ) {
$settings = array();
if ( ! FrmAppHelper::pro_is_installed() ) {
- $label = FrmAddonsController::is_license_expired() ? __( 'Renew', 'formidable' ) : __( 'Upgrade to Pro', 'formidable' );
- $settings[] = '' . esc_html( $label ) . '';
+ if ( FrmAddonsController::is_license_expired() ) {
+ $label = __( 'Renew', 'formidable' );
+ } else {
+ $label = FrmSalesApi::get_best_sale_value( 'plugin_page_cta_text' );
+ if ( ! $label ) {
+ $label = __( 'Upgrade to Pro', 'formidable' );
+ }
+ }
+
+ $upgrade_link = FrmSalesApi::get_best_sale_value( 'plugin_page_cta_link' );
+ if ( ! $upgrade_link ) {
+ $upgrade_link = FrmAppHelper::admin_upgrade_link( 'plugin-row' );
+ }
+
+ $settings[] = '' . esc_html( $label ) . '';
}
$settings[] = '' . __( 'Build a Form', 'formidable' ) . '';
@@ -557,14 +572,17 @@ public static function admin_init() {
}
if ( 'formidable-pro-upgrade' === FrmAppHelper::get_param( 'page' ) && ! FrmAppHelper::pro_is_installed() && current_user_can( 'frm_view_forms' ) ) {
- wp_redirect(
- FrmAppHelper::admin_upgrade_link(
+ $redirect = FrmSalesApi::get_best_sale_value( 'menu_cta_link' );
+ if ( ! $redirect ) {
+ $redirct = FrmAppHelper::admin_upgrade_link(
array(
'medium' => 'upgrade',
'content' => 'submenu-upgrade',
)
- )
- );
+ );
+ }
+
+ wp_redirect( $redirect );
die();
}
diff --git a/classes/helpers/FrmApiHelper.php b/classes/helpers/FrmApiHelper.php
new file mode 100644
index 0000000000..f6bec2d51f
--- /dev/null
+++ b/classes/helpers/FrmApiHelper.php
@@ -0,0 +1,119 @@
+ $cutoff;
+ }
+
+ /**
+ * @since x.x
+ *
+ * @return bool
+ */
+ private static function is_free_not_first_30() {
+ return self::is_free() && ! self::is_first_30();
+ }
+
+ /**
+ * Check if the Pro plugin is active. If not, consider the user to be on the free version.
+ *
+ * @since x.x
+ *
+ * @return bool
+ */
+ private static function is_free() {
+ return ! FrmAppHelper::pro_is_included();
+ }
+}
diff --git a/classes/helpers/FrmAppHelper.php b/classes/helpers/FrmAppHelper.php
index c33f35e351..23e53bf35a 100644
--- a/classes/helpers/FrmAppHelper.php
+++ b/classes/helpers/FrmAppHelper.php
@@ -1370,16 +1370,26 @@ public static function print_admin_banner( $should_show_lite_upgrade ) {
?>
'settings-license',
- 'content' => 'lite-banner',
- )
- );
+ $cta_text = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_text' );
+ if ( ! $cta_text ) {
+ $cta_text = __( 'upgrading to PRO', 'formidable' );
+ }
+
+ $upgrade_link = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_link' );
+ if ( ! $upgrade_link ) {
+ $upgrade_link = self::admin_upgrade_link(
+ array(
+ 'medium' => 'settings-license',
+ 'content' => 'lite-banner',
+ )
+ );
+ }
+
printf(
- /* translators: %1$s: Start link HTML, %2$s: End link HTML */
- esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$supgrading to PRO%2$s.', 'formidable' ),
+ /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */
+ esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ),
'
',
+ esc_html( $cta_text ),
''
);
?>
diff --git a/classes/helpers/FrmDashboardHelper.php b/classes/helpers/FrmDashboardHelper.php
index 2cbd0116e6..f9dcd75209 100644
--- a/classes/helpers/FrmDashboardHelper.php
+++ b/classes/helpers/FrmDashboardHelper.php
@@ -196,12 +196,20 @@ class=""
* @return array
*/
public static function get_license_buttons() {
- $upgrade_link = FrmAppHelper::admin_upgrade_link(
- array(
- 'medium' => 'settings-license',
- 'content' => 'dashboard-license-box',
- )
- );
+ $cta_text = FrmSalesApi::get_best_sale_value( 'dashboard_license_cta_text' );
+ if ( ! $cta_text ) {
+ $cta_text = __( 'Get Formidable PRO', 'formidable' );
+ }
+
+ $upgrade_link = FrmSalesApi::get_best_sale_value( 'dashboard_license_cta_link' );
+ if ( ! $upgrade_link ) {
+ $upgrade_link = FrmAppHelper::admin_upgrade_link(
+ array(
+ 'medium' => 'settings-license',
+ 'content' => 'dashboard-license-box',
+ )
+ );
+ }
return array(
array(
@@ -210,7 +218,7 @@ public static function get_license_buttons() {
'classes' => 'frm-button-primary frm-show-unauthorized',
),
array(
- 'label' => __( 'Get Formidable PRO', 'formidable' ),
+ 'label' => $cta_text,
'link' => $upgrade_link,
'classes' => 'frm-button-secondary frm-show-unauthorized',
),
diff --git a/classes/models/FrmAddon.php b/classes/models/FrmAddon.php
index 3da3453484..0f52e0d891 100644
--- a/classes/models/FrmAddon.php
+++ b/classes/models/FrmAddon.php
@@ -330,6 +330,9 @@ protected function delete_cache() {
$api = new FrmApplicationApi( $this->license );
$api->reset_cached();
+
+ $api = new FrmSalesApi();
+ $api->reset_cached();
}
/**
diff --git a/classes/models/FrmInbox.php b/classes/models/FrmInbox.php
index 6aae0ea77e..43cc4840dd 100644
--- a/classes/models/FrmInbox.php
+++ b/classes/models/FrmInbox.php
@@ -19,7 +19,7 @@ class FrmInbox extends FrmFormApi {
*/
private static $banner_messages;
- public function __construct( $for_parent = null ) {
+ public function __construct() {
$this->set_cache_key();
if ( false === self::$messages ) {
@@ -205,22 +205,12 @@ private function is_expired( $message ) {
* @return bool
*/
private function is_for_user( $message ) {
- if ( ! isset( $message['who'] ) || $message['who'] === 'all' ) {
+ if ( FrmApiHelper::is_for_user( $message ) ) {
return true;
}
+
$who = (array) $message['who'];
- if ( $this->is_for_everyone( $who ) ) {
- return true;
- }
- if ( $this->is_user_type( $who ) ) {
- return true;
- }
- if ( in_array( 'free_first_30', $who, true ) && $this->is_free_first_30() ) {
- return true;
- }
- if ( in_array( 'free_not_first_30', $who, true ) && $this->is_free_not_first_30() ) {
- return true;
- }
+
/**
* Allow for other special inbox cases in other add-ons.
*
@@ -233,34 +223,6 @@ private function is_for_user( $message ) {
return (bool) apply_filters( 'frm_inbox_message_is_for_user', false, $who, $message );
}
- /**
- * @since 6.16.3
- *
- * @param array $who
- * @return bool
- */
- private function is_for_everyone( $who ) {
- return in_array( 'all', $who, true ) || in_array( 'everyone', $who, true );
- }
-
- /**
- * @since 6.16.3
- *
- * @param array $who
- * @return bool
- */
- private function is_user_type( $who ) {
- return in_array( $this->get_user_type(), $who, true );
- }
-
- private function get_user_type() {
- if ( ! FrmAppHelper::pro_is_installed() ) {
- return 'free';
- }
-
- return FrmAddonsController::license_type();
- }
-
/**
* @param string $key
*
@@ -392,54 +354,6 @@ private function update_list() {
update_option( $this->option, self::$messages, 'no' );
}
- /**
- * Check if user is still using the Lite version only, and within
- * the first 30 days of activation.
- *
- * @since 6.16
- *
- * @return bool
- */
- private function is_free_first_30() {
- return $this->is_free() && $this->is_first_30();
- }
-
- /**
- * @since 6.16.3
- *
- * @return bool
- */
- private function is_first_30() {
- $activation_timestamp = get_option( 'frm_first_activation' );
- if ( false === $activation_timestamp ) {
- // If the option does not exist, assume that it is
- // because the user was active before this option was introduced.
- return false;
- }
- $cutoff = strtotime( '-30 days' );
- return $activation_timestamp > $cutoff;
- }
-
- /**
- * @since 6.16.3
- *
- * @return bool
- */
- private function is_free_not_first_30() {
- return $this->is_free() && ! $this->is_first_30();
- }
-
- /**
- * Check if the Pro plugin is active. If not, consider the user to be on the free version.
- *
- * @since 6.16.3
- *
- * @return bool
- */
- private function is_free() {
- return ! FrmAppHelper::pro_is_included();
- }
-
/**
* Show a banner message if one is available.
*
diff --git a/classes/models/FrmSalesAPI.php b/classes/models/FrmSalesAPI.php
new file mode 100644
index 0000000000..01f87ccc9e
--- /dev/null
+++ b/classes/models/FrmSalesAPI.php
@@ -0,0 +1,229 @@
+set_cache_key();
+
+ if ( false === self::$sales ) {
+ $this->set_sales();
+ }
+ }
+
+ /**
+ * @since x.x
+ *
+ * @return void
+ */
+ protected function set_cache_key() {
+ $this->cache_key = 'frm_sales_cache';
+ }
+
+ /**
+ * @since x.x
+ *
+ * @return string
+ */
+ protected function api_url() {
+ return 'https://plapi.formidableforms.com/sales/';
+ }
+
+ /**
+ * @since x.x
+ *
+ * @return void
+ */
+ private function set_sales() {
+ self::$sales = array();
+
+ $api = $this->get_api_info();
+ if ( empty( $api ) ) {
+ return;
+ }
+
+ foreach ( $api as $sale ) {
+ $this->add_sale( $sale );
+ }
+ }
+
+ /**
+ * @param array|string $sale
+ *
+ * @return void
+ */
+ private function add_sale( $sale ) {
+ if ( ! is_array( self::$sales ) ) {
+ // This gets set in the constructor.
+ // This check is just here for Psalm analysis.
+ return;
+ }
+
+ if ( ! is_array( $sale ) || ! isset( $sale['key'] ) ) {
+ // if the API response is invalid, $sale may not be an array.
+ // if there are no sales from the API, it is returning a "No Entries Found" item with no key, so check for a key as well.
+ return;
+ }
+
+ if ( ! $this->sale_is_active( $sale ) ) {
+ return;
+ }
+
+ self::$sales[ $sale['key'] ] = $this->fill_sale( $sale );
+ }
+
+ /**
+ * @param array $sale
+ * @return array
+ */
+ private function fill_sale( $sale ) {
+ $defaults = array(
+ 'key' => '',
+ 'starts' => '',
+ 'expires' => '',
+ // Use 'free', 'personal', 'business', 'elite', 'grandfathered'.
+ 'who' => 'all',
+ 'discount_percent' => 0,
+ 'test_group' => '',
+ 'lite_banner_cta_link' => '',
+ 'lite_banner_cta_text' => '',
+ 'menu_cta_link' => '',
+ 'menu_cta_text' => '',
+ 'dashboard_license_cta_link' => '',
+ 'dashboard_license_cta_text' => '',
+ 'global_settings_license_cta_link' => '',
+ 'global_settings_license_cta_text' => '',
+ 'global_settings_unlock_more_cta_link' => '',
+ 'global_settings_unlock_more_cta_text' => '',
+ 'global_settings_upgrade_cta_link' => '',
+ 'builder_sidebar_cta_link' => '',
+ 'builder_sidebar_cta_text' => '',
+ );
+
+ return array_merge( $defaults, $sale );
+ }
+
+ /**
+ * Check if a sale is within the active period.
+ *
+ * @since x.x
+ *
+ * @param array $sale
+ * @return bool
+ */
+ private function sale_is_active( $sale ) {
+ $starts = $sale['starts'];
+ $expires = $sale['expires'] + DAY_IN_SECONDS;
+ $date = new DateTime( 'now', new DateTimeZone( 'America/New_York' ) );
+ $today = $date->getTimestamp();
+ return $today >= $starts && $today <= $expires;
+ }
+
+ /**
+ * @since x.x
+ *
+ * @return array|false
+ */
+ public function get_best_sale() {
+ if ( ! self::$sales ) {
+ return false;
+ }
+
+ if ( isset( self::$best_sale ) ) {
+ return self::$best_sale;
+ }
+
+ $best_sale = false;
+ foreach ( self::$sales as $sale ) {
+ if ( ! FrmApiHelper::is_for_user( $sale ) ) {
+ continue;
+ }
+
+ if ( ! $this->matches_ab_group( $sale ) ) {
+ continue;
+ }
+
+ if ( ! $best_sale || $sale['discount_percent'] > $best_sale['discount_percent'] ) {
+ $best_sale = $sale;
+ }
+ }
+
+ self::$best_sale = $best_sale;
+ return self::$best_sale;
+ }
+
+ /**
+ * Get text for best sale if applicable.
+ *
+ * @since x.x
+ *
+ * @param string $key
+ * @return false|string False if no sale is active.
+ */
+ public static function get_best_sale_value( $key ) {
+ if ( ! isset( self::$instance ) ) {
+ self::$instance = new FrmSalesApi();
+ }
+
+ $sale = self::$instance->get_best_sale();
+
+ return is_array( $sale ) && ! empty( $sale[ $key ] ) ? $sale[ $key ] : false;
+ }
+
+ /**
+ * @since x.x
+ *
+ * @param array $sale
+ * @return bool True if the sale is a match for the applicable group (if one is defined).
+ */
+ private function matches_ab_group( $sale ) {
+ if ( ! is_numeric( $sale['test_group'] ) ) {
+ // No test group, so return true.
+ return true;
+ }
+
+ $ab_group = $this->get_ab_group_for_current_site();
+ return $ab_group === $sale['test_group'];
+ }
+
+ /**
+ * @since x.x
+ *
+ * @return int 1 or 0.
+ */
+ private function get_ab_group_for_current_site() {
+ $option = get_option( 'frm_sale_ab_group' );
+ if ( ! is_numeric( $option ) ) {
+ // Generate either 0 or 1.
+ $option = mt_rand( 0, 1 );
+ update_option( 'frm_sale_ab_group', $option, false );
+ }
+ return (int) $option;
+ }
+}
diff --git a/classes/views/dashboard/templates/pro-features-list.php b/classes/views/dashboard/templates/pro-features-list.php
index d1e5dc70c4..1db176a37d 100644
--- a/classes/views/dashboard/templates/pro-features-list.php
+++ b/classes/views/dashboard/templates/pro-features-list.php
@@ -10,12 +10,23 @@
die( 'You are not allowed to call this page directly.' );
}
-$discount_link = FrmAppHelper::admin_upgrade_link(
- array(
- 'medium' => 'dashboard-discount',
- 'content' => 'dashboard-defy-limits-cta',
- )
+$best_discount = FrmSalesApi::get_best_sale_value( 'discount_percent' );
+$use_discount = $best_discount > 50 ? $best_discount : 50;
+$discount_text = sprintf(
+ // translators: %s is the discount percentage (ie 50%).
+ __( 'Upgrade to Pro & Get %1$s Off', 'formidable' ),
+ $use_discount . '%'
);
+
+$discount_link = FrmSalesApi::get_best_sale_value( 'global_settings_upgrade_cta_link' );
+if ( ! $discount_link ) {
+ $discount_link = FrmAppHelper::admin_upgrade_link(
+ array(
+ 'medium' => 'dashboard-discount',
+ 'content' => 'dashboard-defy-limits-cta',
+ )
+ );
+}
?>