: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$this->setup_user( $user_id, $user_public_key, $user_secret_key );
if ( ! is_null( $is_marketing_allowed ) ) {
$this->disable_opt_in_notice_and_lock_user();
FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array(
FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed,
FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed,
foreach ( $installs as $install ) {
$install_ids[] = $install->id;
$left = count( $install_ids );
$result = $this->get_api_user_scope()->get( "/plugins/{$this->_module_id}/installs.json?ids=" . implode( ',', array_slice( $install_ids, $offset, $items_per_request ) ) );
if ( ! $this->is_api_result_object( $result, 'installs' ) ) {
// @todo Handle API error.
$installs = array_merge( $installs, $result->installs );
$left -= $items_per_request;
$offset += $items_per_request;
foreach ( $installs as &$install ) {
$install = new FS_Site( $install );
return $this->setup_network_account(
* @author Vova Feldman (@svovaf)
* @param string|bool $email
* @param string|bool $license_key Since 1.2.1.5
* @param bool $is_pending_trial Since 1.2.1.5
* @param bool $is_suspicious_email Since 2.5.0
* @param bool $has_upgrade_context Since 2.5.3
* @param bool|string $support_email_address Since 2.5.3
* @return string Since 1.2.1.5 if $redirect is `false`, return the pending activation page.
private function set_pending_confirmation(
$is_pending_trial = false,
$is_suspicious_email = false,
$has_upgrade_context = false,
$support_email_address = false
$is_network_admin = fs_is_network_admin();
if ( $this->_ignore_pending_mode && ! $has_upgrade_context ) {
* If explicitly asked to ignore pending mode, set to anonymous mode
* if require confirmation before finalizing the opt-in except after completing a purchase (otherwise, in this case, they wouldn't see any notice telling them that they should receive their license key via email).
$this->skip_connection( $is_network_admin );
// Install must be activated via email since
// user with the same email already exist.
$this->_storage->is_pending_activation = true;
$this->_add_pending_activation_notice(
if ( ! empty( $license_key ) ) {
$this->_storage->pending_license_key = $license_key;
// Remove the opt-in sticky notice.
$this->_admin_notices->remove_sticky( array(
$next_page = $this->get_after_activation_url( 'after_pending_connect_url' );
// Reload the page with a pending activation message.
fs_redirect( $next_page );
* Install plugin with current logged WP user info.
* @author Vova Feldman (@svovaf)
function _install_with_current_user() {
$this->_logger->entrance();
if ( $this->is_registered() ) {
if ( fs_request_is_action( $this->get_unique_affix() . '_activate_existing' ) && fs_request_is_post() ) {
check_admin_referer( $this->get_unique_affix() . '_activate_existing' );
* @author Vova Feldman (@svovaf)
* @since 1.1.9 Add license key if given.
$license_key = fs_request_get_raw( 'license_secret_key' );
FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array(
FS_Permission_Manager::PERMISSION_DIAGNOSTIC => fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ),
FS_Permission_Manager::PERMISSION_EXTENSIONS => fs_request_get_bool( 'is_extensions_tracking_allowed', null ),
$this->install_with_current_user( $license_key );
* @author Vova Feldman (@svovaf)
* @param string|bool $license_key
* @param number|bool $trial_plan_id
* @param array $sites Since 2.0.0
* @return object|string If redirect is `false`, returns the next page the user should be redirected to, or the API error object if failed to install.
function install_with_current_user(
// Get current logged WP user.
$current_user = self::_get_current_wp_user();
// Find the relevant FS user by the email.
$user = self::_get_user_by_email( $current_user->user_email );
return $this->install_with_user( $user, $license_key, $trial_plan_id, $redirect, true, $sites );
* @author Vova Feldman (@svovaf)
* @param string|bool $license_key
* @param number|bool $trial_plan_id
* @param bool $setup_account Since 2.0.0. When set to FALSE, executes a light installation without setting up the account as if it's the first opt-in.
* @param array $sites Since 2.0.0. If not empty, should be a collection of site details for the bulk install API request.
* @return \FS_Site|object|string If redirect is `false`, returns the next page the user should be redirected to, or the API error object if failed to install. If $setup_account is set to `false`, return the newly created install.
function install_with_user(
// We have to set the user before getting user scope API handler.
$result = $this->create_installs_with_user(
if ( ! $this->is_api_result_entity( $result ) &&
! $this->is_api_result_object( $result, 'installs' )
// @todo Handler potential API error of the $result
$site = new FS_Site( $result );
if ( ! $setup_account ) {
$this->sync_plan_if_not_exist( $site->plan_id );
if ( ! empty( $license_key ) && FS_Plugin_License::is_valid_id( $site->license_id ) ) {
$this->sync_license_if_not_exist( $site->license_id, $license_key );
$this->_admin_notices->remove_sticky( 'connect_account', false );
return $this->setup_account( $this->_user, $this->_site, $redirect );
foreach ( $result->installs as $install ) {
$installs[] = new FS_Site( $install );
return $this->setup_network_account(
* Initiate an API request to create a collection of installs.
* @author Vova Feldman (@svovaf)
* @param bool $license_key
* @param bool $trial_plan_id
private function create_installs_with_user(
$extra_install_params = array(
'uid' => $this->get_anonymous_id(),
'is_disconnected' => false,
if ( ! empty( $license_key ) ) {
$extra_install_params['license_key'] = $this->apply_filters( 'license_key', $license_key );
$extra_install_params['ignore_license_owner'] = true;
} else if ( FS_Plugin_Plan::is_valid_id( $trial_plan_id ) ) {
$extra_install_params['trial_plan_id'] = $trial_plan_id;
if ( ! empty( $sites ) ) {
$extra_install_params['sites'] = $sites;
$args = $this->get_install_data_for_api( $extra_install_params, false, false );
$result = $this->get_api_user_scope_by_user( $user )->call(
"/plugins/{$this->get_id()}/installs.json",
if ( ! $this->is_api_result_entity( $result ) &&
! $this->is_api_result_object( $result, 'installs' )
if ( ! empty( $args['license_key'] ) ) {
// Pass the fully entered license key to the failure handler.
$args['license_key'] = $license_key;
$result = $this->apply_filters( 'after_install_failure', $result, $args );
$this->_admin_notices->add(
sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' .
$this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . $result->error->message . '</b>',
$this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
* We set the user before getting the user scope API handler, so the user became temporarily
* registered (`is_registered() = true`). Since the API returned an error and we will redirect,
* we have to set the user to `null`, otherwise, the user will be redirected to the wrong
* activation page based on the return value of `is_registered()`. In addition, in case the
* context plugin doesn't have a settings menu and the default page is the `Plugins` page,
* misleading plugin activation errors will be shown on the `Plugins` page.
* @author Leo Fajardo (@leorw)
fs_redirect( $this->get_activation_url( array( 'error' => $result->error->message ) ) );
* Tries to activate add-on account based on parent plugin info.
* @author Vova Feldman (@svovaf)
* @param Freemius $parent_fs
* @param bool|int|null $network_level_or_blog_id True for network level opt-in and integer for opt-in for specified blog in the network.
* @param FS_Plugin_License $bundle_license Since 2.4.0. If provided, this license will be activated for the add-on.
private function _activate_addon_account(
$network_level_or_blog_id = null,
FS_Plugin_License $bundle_license = null
if ( $this->is_registered() ) {
$permission_ids = FS_Permission_Manager::get_all_permission_ids();
foreach ( $permission_ids as $permission_id ) {
$permissions[ $permission_id ] = FS_Permission_Manager::instance( $parent_fs )->is_permission( $permission_id, true );
FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( $permissions );
* Do not override the `uid` if network-level opt-in since the call to `get_sites_for_network_level_optin()`
* already returns the data for the current blog.
* @author Leo Fajardo (@leorw)
$uid_param_to_override = ( true === $network_level_or_blog_id ) ?
array( 'uid' => $this->get_anonymous_id() );
$params = $this->get_install_data_for_api(
* Do not include the data for the current blog if network-level opt-in since the call to `get_sites_for_network_level_optin`
* already includes the data for it.
* @author Leo Fajardo (@leorw)
( true !== $network_level_or_blog_id )
if ( true === $network_level_or_blog_id ) {
$params['sites'] = $this->get_sites_for_network_level_optin();
if ( empty( $params['sites'] ) ) {
if ( is_object( $bundle_license ) ) {
$params['license_key'] = $bundle_license->secret_key;
// Activate add-on with parent plugin credentials.
$result = $parent_fs->get_api_site_scope()->call(
"/addons/{$this->_plugin->id}/installs.json",
if ( ! $this->is_api_result_object( $result, 'installs' ) ) {
if ( is_object( $bundle_license ) ) {
* When a license object is provided, it's an attempt by the SDK to activate a bundle license and not a user-initiated action, therefore, do not show any admin notice to avoid confusion (e.g.: the notice will show up just above the opt-in link). If the license activation fails, the admin will see an opt-in link instead.
* @author Leo Fajardo (@leorw)
$error_message = FS_Api::is_api_error_object( $result ) ?
$result->error->message :
$this->get_text_inline( 'An unknown error has occurred.', 'unknown-error' );
$this->_admin_notices->add(
sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' .
$this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . $error_message . '</b>',
$this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
$addon_installs = $result->installs;
foreach ( $addon_installs as $key => $addon_install ) {
$addon_installs[ $key ] = new FS_Site( $addon_install );
$first_install = $addon_installs[0];
// Get user information based on parent's plugin.
$user = $parent_fs->get_user();
// First of all, set site and user info - otherwise we won't
// be able to invoke API calls.
$this->_site = $first_install;
$this->handle_account_connection( $addon_installs, ! fs_is_network_admin() );
// Get site's current plan.
//$this->_site->plan = $this->_get_plan_by_id( $this->_site->plan->id );
if ( ! fs_is_network_admin() ) {
// Try to activate premium license.
$this->_activate_license( true, $bundle_license );
if ( is_object( $bundle_license ) ) {
$this->maybe_activate_bundle_license( $bundle_license );
if ( is_object( $bundle_license ) ) {
$premium_license = $bundle_license;
$license_id = fs_request_get( 'license_id' );
if ( is_object( $this->_site ) &&
FS_Plugin_License::is_valid_id( $license_id ) &&
$license_id == $this->_site->license_id
// License is already activated.
$premium_license = FS_Plugin_License::is_valid_id( $license_id ) ?
$this->_get_license_by_id( $license_id ) :
$this->_get_available_premium_license();
if ( is_object( $premium_license ) ) {
$this->maybe_network_activate_addon_license( $premium_license );
* @author Leo Fajardo (@leorw)
* @param FS_Site[] $installs
* @param bool $is_site_level
private function handle_account_connection( $installs, $is_site_level ) {
$first_install = $installs[0];
$this->_set_account( $this->_user, $first_install );