: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Delete plugin's plans information.
* @param bool $store Flush to Database if true.
* @param bool $keep_associated_plans If set to false, delete all plans, even if a plan is associated with an install.
* @author Vova Feldman (@svovaf)
private function _delete_plans( $store = true, $keep_associated_plans = true ) {
$this->_logger->entrance();
$plans = self::get_all_plans( $this->_module_type );
$plans_to_keep = array();
if ( $keep_associated_plans ) {
$plans_ids_to_keep = $this->get_plans_ids_associated_with_installs();
foreach ( $plans_ids_to_keep as $plan_id ) {
$plan = self::_get_plan_by_id( $plan_id );
if ( is_object( $plan ) ) {
$plans_to_keep[] = self::_encrypt_entity( $plan );
if ( ! empty( $plans_to_keep ) ) {
$plans[ $this->_slug ] = $plans_to_keep;
unset( $plans[ $this->_slug ] );
$this->set_account_option( 'plans', $plans, $store );
* Delete all plugin licenses.
* @author Vova Feldman (@svovaf)
private function _delete_licenses( $store = true ) {
$this->_logger->entrance();
$all_licenses = self::get_all_licenses();
unset( $all_licenses[ $this->_module_id ] );
self::$_accounts->set_option( 'all_licenses', $all_licenses, $store );
* Check if Freemius was added on new plugin installation.
* @author Vova Feldman (@svovaf)
function is_plugin_new_install() {
return isset( $this->_storage->is_plugin_new_install ) &&
$this->_storage->is_plugin_new_install;
* Check if it's the first plugin release that is running Freemius.
* @author Vova Feldman (@svovaf)
function is_first_freemius_powered_version() {
return empty( $this->_storage->plugin_last_version );
* @author Leo Fajardo (@leorw)
private function get_previous_theme_slug() {
return isset( $this->_storage->previous_theme ) ?
$this->_storage->previous_theme :
* @author Leo Fajardo (@leorw)
private function can_activate_previous_theme() {
return $this->can_activate_theme( $this->get_previous_theme_slug() );
* @author Leo Fajardo (@leorw)
private function can_activate_theme( $slug ) {
if ( false !== $slug && current_user_can( 'switch_themes' ) ) {
$theme_instance = wp_get_theme( $slug );
return $theme_instance->exists();
* @author Leo Fajardo (@leorw)
private function activate_previous_theme() {
switch_theme( $this->get_previous_theme_slug() );
unset( $this->_storage->previous_theme );
if ( 'themes.php' === $pagenow ) {
* Refresh the active theme information.
* @author Leo Fajardo (@leorw)
fs_redirect( $this->admin_url( $pagenow ) );
* @author Leo Fajardo (@leorw)
function get_previous_theme_activation_url() {
if ( ! $this->can_activate_previous_theme() ) {
* @author Leo Fajardo (@leorw)
$this->admin_url( 'themes.php?action=activate&stylesheet=' . urlencode( $this->get_previous_theme_slug() ) ),
'switch-theme_' . $this->get_previous_theme_slug()
* Saves the slug of the previous theme if it still exists so that it can be used by the logic in the opt-in
* form that decides whether to add a close button to the opt-in dialog or not. So after a premium-only theme is
* activated, the close button will appear and will reactivate the previous theme if clicked. If the previous
* theme doesn't exist, then there will be no close button.
* @author Leo Fajardo (@leorw)
* @param string $slug_or_name Old theme's slug or name.
* @param bool|WP_Theme $old_theme WP_Theme instance of the old theme if it still exists.
function _activate_theme_event_hook( $slug_or_name, $old_theme = false ) {
$this->_storage->previous_theme = ( false !== $old_theme ) ?
$old_theme->get_stylesheet() :
$this->_activate_plugin_event_hook();
* @author Vova Feldman (@svovaf)
function _activate_plugin_event_hook() {
$this->_logger->entrance( 'slug = ' . $this->_slug );
if ( ! $this->is_user_admin() ) {
$this->unregister_uninstall_hook();
// Clear API cache on activation.
$is_premium_version_activation = $this->is_plugin() ?
( current_filter() !== ( 'activate_' . $this->_free_plugin_basename ) ) :
if ( $is_premium_version_activation && $this->is_pending_activation() ) {
$this->clear_pending_activation_mode();
$this->_logger->info( 'Activating ' . ( $is_premium_version_activation ? 'premium' : 'free' ) . ' plugin version.' );
if ( $this->is_plugin() ) {
// This logic is relevant only to plugins since both the free and premium versions of a plugin can be active at the same time.
// 1. If running in the activation of the FREE module, get the basename of the PREMIUM.
// 2. If running in the activation of the PREMIUM module, get the basename of the FREE.
$other_version_basename = $is_premium_version_activation ?
$this->_free_plugin_basename :
$this->premium_plugin_basename();
if ( ! $this->_is_network_active ) {
* Themes are always network activated, but the ACTUAL activation is per site.
* During the activation, the plugin isn't yet active, therefore,
* _is_network_active will be set to false even if it's a network level
* activation. So we need to fix that by looking at the is_network_admin() value.
$this->_is_network_active = (
$this->_is_multisite_integrated &&
* If the other module version is active, deactivate it.
* is_plugin_active() checks if the plugin is active on the site or the network level and
* deactivate_plugins() deactivates the plugin whether it's activated on the site or network level.
* @author Leo Fajardo (@leorw)
is_plugin_active( $other_version_basename ) &&
$this->apply_filters( 'deactivate_on_activation', true )
deactivate_plugins( $other_version_basename );
if ( $this->is_registered() ) {
if ( $is_premium_version_activation ) {
$this->reconnect_locally();
// Schedule re-activation event and sync.
// $this->sync_install( array(), true );
$this->schedule_install_sync();
// If activating the premium module version, add an admin notice to congratulate for an upgrade completion.
if ( $is_premium_version_activation ) {
$this->_admin_notices->add(
sprintf( $this->get_text_inline( 'The upgrade of %s was successfully completed.', 'successful-version-upgrade-message' ), sprintf( '<b>%s</b>', $this->_plugin->title ) ),
$this->get_text_x_inline( 'W00t',
'Used to express elation, enthusiasm, or triumph (especially in electronic communication).', 'woot' ) . '!'
} else if ( $this->is_anonymous() ) {
if ( isset( $this->_storage->is_anonymous_ms ) && $this->_storage->is_anonymous_ms['is'] ) {
$plugin_version = $this->_storage->is_anonymous_ms['version'];
$plugin_version = isset( $this->_storage->is_anonymous ) ?
$this->_storage->is_anonymous['version'] :
* Reset "skipped" click cache on the following:
* 2. WordPress DEBUG mode.
* 3. If a plugin and the user skipped the exact same version before.
* @since 1.2.2.7 Ulrich Pogson (@grapplerulrich) asked to not reset the SKIPPED flag if the exact same THEME version was activated before unless the developer is running with WP_DEBUG on, or Freemius debug mode on (WP_FS__DEV_MODE).
* @todo 4. If explicitly asked to retry after every activation.
( $this->is_plugin() || ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) &&
$this->get_plugin_version() == $plugin_version
$this->reset_anonymous_mode( $network );
$is_trial_or_has_features_enabled_license = ( $this->is_trial() || $this->has_features_enabled_license() );
if ( $this->is_addon() && ! $is_trial_or_has_features_enabled_license ) {
* When activating an add-on, try to also activate a license.
* @author Leo Fajardo (@leorw)
if ( ! $this->_is_network_active ) {
$this->maybe_activate_addon_license();
$this->maybe_network_activate_addon_license();
* Avoid redirecting to the license activation screen after automatically activating an add-on license.
* @author Leo Fajardo (@leorw)
$is_trial_or_has_features_enabled_license = ( $this->is_trial() || $this->has_features_enabled_license() );
if ( $is_trial_or_has_features_enabled_license && true === $this->_storage->require_license_activation ) {
$this->_storage->require_license_activation = false;
$is_premium_version_activation &&
( ! $this->is_registered() && $this->is_anonymous() ) ||
$this->is_registered() &&
! $is_trial_or_has_features_enabled_license
$this->_storage->require_license_activation = true;
if ( ! isset( $this->_storage->is_plugin_new_install ) ) {
* If no previous version of plugin's version exist, it means that it's either
* the first time that the plugin installed on the site, or the plugin was installed
* before but didn't have Freemius integrated.
* Since register_activation_hook() do NOT fires on updates since 3.1, and only fires
* on manual activation via the dashboard, is_plugin_activation() is TRUE
* only after immediate activation.
* @link https://make.wordpress.org/core/2010/10/27/plugin-activation-hooks-no-longer-fire-for-updates/
$this->_storage->is_plugin_new_install = empty( $this->_storage->plugin_last_version );
* Also flush when activating the premium version so that even if Freemius was off before, the API
* connectivity test can be run again.
* @author Leo Fajardo (@leorw)
$has_api_connectivity = $this->has_api_connectivity( WP_FS__DEV_MODE || $is_premium_version_activation );
if ( ! $this->_anonymous_mode &&
( false !== $has_api_connectivity ) &&
// Store hint that the plugin was just activated to enable auto-redirection to settings.
set_transient( "fs_{$this->_module_type}_{$this->_slug}_activated", true, 60 );
* Activation hook is executed after the plugin's main file is loaded, therefore,
* after the plugin was loaded. The logic is located at activate_plugin()
* ./wp-admin/includes/plugin.php.
* @author Vova Feldman (@svovaf)
$this->_storage->was_plugin_loaded = true;
* @author Leo Fajardo (@leorw)
private function maybe_activate_addon_license() {
$parent_fs = $this->get_parent_instance();
! is_object( $parent_fs ) ||
( ! $parent_fs->is_registered() && ! $parent_fs->is_network_registered() )
// Try to activate a license only if the parent plugin is active and has a valid `install`.
$license = $this->get_active_parent_license();
if ( ! is_object( $license ) ) {
$this->is_bundle_license_auto_activation_enabled() &&
! empty( $license->products )
$this->activate_bundle_license( $license );
if ( ! $this->is_registered() ) {
// Opt in with a license key.
$parent_fs->get_current_or_network_user()->email,
$install = $this->api_site_call(
array( 'license_key' => $this->apply_filters( 'license_key', $license->secret_key ) )
if ( ! FS_Api::is_api_error( $install ) ) {
$this->_sync_addon_license( $this->get_id(), true );
* @author Leo Fajardo (@leorw)
* @param FS_Plugin_License $license
private function maybe_network_activate_addon_license( $license = null ) {
$parent_fs = $this->get_parent_instance();
if ( ! is_object( $parent_fs ) || ( ! $parent_fs->is_registered() && ! $parent_fs->is_network_registered() ) ) {
// Try to activate a license only if the parent plugin is active and has a valid `install`.
$license = ( ! is_null( $license ) ) ?
$this->get_active_parent_license();
if ( ! is_object( $license ) ) {
$this->is_bundle_license_auto_activation_enabled() &&
! empty( $license->products )
$this->activate_bundle_license( $license );
if ( ! $this->is_network_registered() ) {
$sites = $this->get_sites_for_network_level_optin();
if ( count( $sites ) > $license->left() ) {
// If the add-on is network active, try to activate the license only if it can be activated on all sites.
// Opt in with a license key.
$parent_fs->get_user()->email,
$blog_2_install_map = array();
$all_sites = Freemius::get_sites();
foreach ( $all_sites as $site ) {
$blog_id = Freemius::get_site_blog_id( $site );
$install = $this->get_install_by_blog_id( $blog_id );
if ( is_object( $install ) && FS_Plugin_License::is_valid_id( $install->license_id ) ) {
// Skip license activation for installs that are already associated with a license.