From 31adb032152968f685fc8eeefb82a7d5fff1154d Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Wed, 6 Aug 2025 12:32:22 +0800 Subject: [PATCH 1/4] add no-op function --- plugin.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugin.php b/plugin.php index a5ef877083..66d08eab2b 100644 --- a/plugin.php +++ b/plugin.php @@ -19,8 +19,13 @@ // Freemius SDK: Auto deactivate the free version when activating the paid one. if ( function_exists( 'sugb_fs' ) ) { - sugb_fs()->set_basename( true, __FILE__ ); - return; + // Prevent PHP errors by checking sugb_fs return value. + // In the free version, sugb_fs() is a no-op function that returns null. + // Only proceed if sugb_fs() returns an object (i.e., the Freemius SDK is loaded). + if ( is_object( sugb_fs() ) ) { + sugb_fs()->set_basename( false, __FILE__ ); + return; + } } defined( 'STACKABLE_SHOW_PRO_NOTICES' ) || define( 'STACKABLE_SHOW_PRO_NOTICES', true ); @@ -213,6 +218,11 @@ function is_frontend() { */ if ( STACKABLE_BUILD !== 'free' ) { require_once( plugin_dir_path( __FILE__ ) . 'freemius.php' ); +} else if ( ! function_exists( 'sugb_fs' ) ) { + // Provide a no-op sugb_fs() stub for compatibility when Freemius SDK is not loaded. + // This allows themes and plugins to safely check for Stackable's presence. + // The function returns null and performs no actions. + function sugb_fs() {}; } /** From 11e5f098c0f742abc5c874c5036ed90e0c782b70 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Wed, 6 Aug 2025 22:42:00 +0800 Subject: [PATCH 2/4] prevent multiple active stackable plugins --- plugin.php | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/plugin.php b/plugin.php index 66d08eab2b..653f91322d 100644 --- a/plugin.php +++ b/plugin.php @@ -17,15 +17,27 @@ exit; } +if ( ! function_exists( 'stackable_multiple_plugins_check' ) ) { + // Prevent multiple Stackable plugin versions from being active simultaneously. + function stackable_multiple_plugins_check() { + if ( is_plugin_active( $GLOBALS['OTHER_STACKABLE_FILE'] ) ) { + deactivate_plugins( $GLOBALS['OTHER_STACKABLE_FILE'] ); + } + } +} + +if ( defined('STACKABLE_FILE') && STACKABLE_FILE !== __FILE__ && ! isset( $GLOBALS['OTHER_STACKABLE_FILE'] ) ) { + // Get relative file path of the other Stackable version. + preg_match( "#([^\/]*?\/plugin.php)$#", STACKABLE_FILE, $matches ); + $GLOBALS['OTHER_STACKABLE_FILE'] = $matches[1]; + + register_activation_hook( __FILE__, 'stackable_multiple_plugins_check' ); +} + // Freemius SDK: Auto deactivate the free version when activating the paid one. if ( function_exists( 'sugb_fs' ) ) { - // Prevent PHP errors by checking sugb_fs return value. - // In the free version, sugb_fs() is a no-op function that returns null. - // Only proceed if sugb_fs() returns an object (i.e., the Freemius SDK is loaded). - if ( is_object( sugb_fs() ) ) { - sugb_fs()->set_basename( false, __FILE__ ); - return; - } + sugb_fs()->set_basename( true, __FILE__ ); + return; } defined( 'STACKABLE_SHOW_PRO_NOTICES' ) || define( 'STACKABLE_SHOW_PRO_NOTICES', true ); @@ -218,11 +230,6 @@ function is_frontend() { */ if ( STACKABLE_BUILD !== 'free' ) { require_once( plugin_dir_path( __FILE__ ) . 'freemius.php' ); -} else if ( ! function_exists( 'sugb_fs' ) ) { - // Provide a no-op sugb_fs() stub for compatibility when Freemius SDK is not loaded. - // This allows themes and plugins to safely check for Stackable's presence. - // The function returns null and performs no actions. - function sugb_fs() {}; } /** From e30a8465aa2c0469df6598333c5b3df8387dc792 Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Thu, 7 Aug 2025 12:10:10 +0800 Subject: [PATCH 3/4] Update plugin.php --- plugin.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugin.php b/plugin.php index 653f91322d..5d43ae1755 100644 --- a/plugin.php +++ b/plugin.php @@ -28,8 +28,7 @@ function stackable_multiple_plugins_check() { if ( defined('STACKABLE_FILE') && STACKABLE_FILE !== __FILE__ && ! isset( $GLOBALS['OTHER_STACKABLE_FILE'] ) ) { // Get relative file path of the other Stackable version. - preg_match( "#([^\/]*?\/plugin.php)$#", STACKABLE_FILE, $matches ); - $GLOBALS['OTHER_STACKABLE_FILE'] = $matches[1]; + $GLOBALS['OTHER_STACKABLE_FILE'] = plugin_basename( STACKABLE_FILE ); register_activation_hook( __FILE__, 'stackable_multiple_plugins_check' ); } From e49ce39cd296d98652223850afaea88fd50e0bd3 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Thu, 7 Aug 2025 13:44:54 +0800 Subject: [PATCH 4/4] add notice --- plugin.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/plugin.php b/plugin.php index 5d43ae1755..fdee2bde51 100644 --- a/plugin.php +++ b/plugin.php @@ -30,6 +30,11 @@ function stackable_multiple_plugins_check() { // Get relative file path of the other Stackable version. $GLOBALS['OTHER_STACKABLE_FILE'] = plugin_basename( STACKABLE_FILE ); + // Use a temporary option to store the other Stackable Plugin info needed for the admin notice. This will be deleted later. + // Note: We cannot use add_action in the register_activation_hook callback so we use this temporary option. + // See https://developer.wordpress.org/reference/functions/register_activation_hook/#process-flow for more info. + add_option( 'stackable_other_stackable_plugin_info', [ 'BUILD' => STACKABLE_BUILD, 'VERSION' => STACKABLE_VERSION ] ); + register_activation_hook( __FILE__, 'stackable_multiple_plugins_check' ); } @@ -174,6 +179,28 @@ function stackable_notice_gutenberg_plugin_ignore() { add_action( 'wp_ajax_stackable_notice_gutenberg_plugin_ignore', 'stackable_notice_gutenberg_plugin_ignore' ); } +/** + * Show notice if another Stackable plugin has been deactivated. + * + * @since 3.18.1 + */ +if ( ! function_exists( 'stackable_notice_other_stackable_plugin_deactivated' ) ) { + function stackable_notice_other_stackable_plugin_deactivated() { + $OTHER_STACKABLE_INFO = get_option( 'stackable_other_stackable_plugin_info', false ); + if ( $OTHER_STACKABLE_INFO ) { + printf( + '

%s

', + sprintf( esc_html__( '%sStackable Notice%s: The Stackable plugin (%s version %s) has been deactivated. Only one active Stackable plugin is needed.', STACKABLE_I18N ), '', '', $OTHER_STACKABLE_INFO['BUILD'], $OTHER_STACKABLE_INFO['VERSION'] ) + ); + delete_option( 'stackable_other_stackable_plugin_info' ); + } + } + + if ( get_option( 'stackable_other_stackable_plugin_info', false ) ) { + add_action( 'admin_notices', 'stackable_notice_other_stackable_plugin_deactivated' ); + } +} + /******************************************************************************************** * END Activation & PHP version checks. ********************************************************************************************/