: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
private function sync_addons( $flush = false ) {
$this->_logger->entrance();
$api = $this->get_api_site_or_plugin_scope();
$path = $this->add_show_pending( '/addons.json?enriched=true&count=50' );
* If there's a cached version of the add-ons and not asking
* for a flush, just use the currently stored add-ons.
if ( ! $flush && $api->is_cached( $path ) ) {
$addons = self::get_all_addons();
return isset( $addons[ $this->_plugin->id ] ) ?
$addons[ $this->_plugin->id ] :
$result = $api->get( $path, $flush );
if ( $this->is_api_result_object( $result, 'plugins' ) &&
is_array( $result->plugins )
for ( $i = 0, $len = count( $result->plugins ); $i < $len; $i ++ ) {
$addons[ $i ] = new FS_Plugin( $result->plugins[ $i ] );
$this->_store_addons( $addons, true );
* Handle user email update.
* @author Vova Feldman (@svovaf)
* @param string $new_email
private function update_email( $new_email ) {
$this->_logger->entrance();
$api = $this->get_api_user_scope();
$user = $api->call( "?plugin_id={$this->_plugin->id}&fields=id,email,is_verified", 'put', array(
'after_email_confirm_url' => $this->_get_admin_page_url(
array( 'fs_action' => 'sync_user' )
if ( ! isset( $user->error ) ) {
$this->_user->email = $user->email;
$this->_user->is_verified = $user->is_verified;
// handle different error cases.
#----------------------------------------------------------------------------------
#region API Error Handling
#----------------------------------------------------------------------------------
* @author Vova Feldman (@svovaf)
* @return bool Is API result contains an error.
private function is_api_error( $result ) {
return FS_Api::is_api_error( $result );
* Checks if given API result is a non-empty and not an error object.
* @author Vova Feldman (@svovaf)
* @param string|null $required_property Optional property we want to verify that is set.
function is_api_result_object( $result, $required_property = null ) {
return FS_Api::is_api_result_object( $result, $required_property );
* Checks if given API result is a non-empty entity object with non-empty ID.
* @author Vova Feldman (@svovaf)
private function is_api_result_entity( $result ) {
return FS_Api::is_api_result_entity( $result );
* Make sure a given argument is an array of a specific type.
* @author Vova Feldman (@svovaf)
private function is_array_instanceof( $array, $class ) {
return ( is_array( $array ) && ( empty( $array ) || $array[0] instanceof $class ) );
* Start install ownership change.
* @author Vova Feldman (@svovaf)
* @param string $candidate_email
* @param string $transfer_type
* @return bool Is ownership change successfully initiated.
private function init_change_owner( $candidate_email, $transfer_type ) {
$this->_logger->entrance();
$installs_info_by_slug_map = $this->get_parent_and_addons_installs_info();
foreach ( $installs_info_by_slug_map as $slug => $install_info ) {
$install = $install_info['install'];
if ( $this->_user->id != $install->user_id ) {
// Skip add-on installs that are not owned by the parent product's install's owner.
$install_ids[ $slug ] = $install->id;
$api = $this->get_api_site_scope();
$result = $api->call( "/users/{$this->_user->id}.json", 'put', array(
'email' => $candidate_email,
'transfer_type' => $transfer_type,
'install_ids' => implode( ',', array_values( $install_ids ) ),
'after_confirm_url' => $this->_get_admin_page_url(
array( 'fs_action' => 'change_owner' )
return ! $this->is_api_error( $result );
* Handle install ownership change.
* @author Vova Feldman (@svovaf)
* @return bool Was ownership change successfully complete.
private function complete_change_owner() {
$this->_logger->entrance();
$install_ids = fs_request_get( 'install_ids' );
if ( ! empty( $install_ids ) ) {
$install_ids = explode( ',', $install_ids );
foreach ( $install_ids as $key => $install_id ) {
if ( ! FS_Site::is_valid_id( $install_id ) ) {
unset( $install_ids[ $key ] );
if ( ! is_array( $install_ids ) ) {
$user->id = fs_request_get( 'user_id' );
$user->public_key = fs_request_get_raw( 'user_public_key' );
$user->secret_key = fs_request_get_raw( 'user_secret_key' );
$prev_user = $this->_user;
$result = $this->get_api_user_scope( true )->get(
"/installs.json?install_ids=" . implode( ',', $install_ids )
$current_blog_sites = self::get_all_sites( $this->get_module_type() );
if ( $this->is_api_result_object( $result, 'installs' ) ) {
$site_id_slug_map = array();
foreach ( $current_blog_sites as $slug => $site ) {
$site_id_slug_map[ $site->id ] = $slug;
foreach ( $result->installs as $install ) {
$site = new FS_Site( $install );
if ( ! isset( $site_id_slug_map[ $install->id ] ) ) {
$current_blog_sites[ $site_id_slug_map[ $install->id ] ] = clone $site;
if ( $this->_site->id == $site->id ) {
// Validate install's user and given user.
if ( $user->id != $this->_site->user_id ) {
$this->_user = $prev_user;
$this->set_account_option( 'sites', $current_blog_sites, true );
// Fetch new user information.
$user_result = $this->get_api_user_scope( true )->get();
$user = new FS_User( $user_result );
$this->_set_account( $user, $this->_site );
$all_modules_sites = self::get_all_modules_sites();
foreach ( $all_modules_sites as $sites_by_module_type ) {
foreach ( $sites_by_module_type as $sites_by_slug ) {
foreach ( $sites_by_slug as $site ) {
if ( $prev_user->id == $site->user_id ) {
$users = self::get_all_users();
if ( isset( $users[ $prev_user->id ] ) ) {
unset( $users[ $prev_user->id ] );
// If the prev user wasn't found by the key, iterate over the users collection.
foreach ( $users as $key => $user ) {
if ( $user->id == $prev_user->id ) {
$this->set_account_option( 'users', $users, true );
* Completes ownership change by license.
* @author Leo Fajardo (@leorw)
* @param array[string]number $install_ids_by_slug_map
private function complete_ownership_change_by_license( $user_id, $install_ids_by_slug_map ) {
$this->_logger->entrance();
$this->sync_user_by_current_install( $user_id );
$result = $this->get_api_user_scope( true )->get(
"/installs.json?install_ids=" . implode( ',', $install_ids_by_slug_map )
if ( $this->is_api_result_object( $result, 'installs' ) ) {
$sites = self::get_all_sites( $this->get_module_type() );
$install_ids_by_slug_map = array_flip( $install_ids_by_slug_map );
foreach ( $result->installs as $install ) {
$site = new FS_Site( $install );
$sites[ $install_ids_by_slug_map[ $site->id ] ] = clone $site;
$this->set_account_option( 'sites', $sites, true );
* Handle user name update.
* @author Vova Feldman (@svovaf)
private function update_user_name() {
$this->_logger->entrance();
$name = fs_request_get( 'fs_user_name_' . $this->get_unique_affix(), '' );
$api = $this->get_api_user_scope();
$user = $api->call( "?plugin_id={$this->_plugin->id}&fields=id,first,last", 'put', array(
if ( ! isset( $user->error ) ) {
$this->_user->first = $user->first;
$this->_user->last = $user->last;
// handle different error cases.
* @author Vova Feldman (@svovaf)
private function verify_email() {
$this->_handle_account_user_sync();
if ( $this->_user->is_verified() ) {
$api = $this->get_api_site_scope();
$result = $api->call( "/users/{$this->_user->id}/verify.json", 'put', array(
'after_email_confirm_url' => $this->_get_admin_page_url(
array( 'fs_action' => 'sync_user' )
if ( ! isset( $result->error ) ) {
$this->_admin_notices->add( sprintf(
$this->get_text_inline( 'Verification mail was just sent to %s. If you can\'t find it after 5 min, please check your spam box.', 'verification-email-sent-message' ),
sprintf( '<a href="mailto:%1$s">%2$s</a>', esc_url( $this->_user->email ), $this->_user->email )
// handle different error cases.
* @author Vova Feldman (@svovaf)
* @param bool|null $network
function get_activation_url( $params = array(), $network = null ) {
if ( $this->is_addon() && $this->has_free_plan() ) {
* @author Vova Feldman (@svovaf)
* @since 1.2.1.7 Add-on's activation is the parent's module activation.
return $this->get_parent_instance()->get_activation_url( $params );
return $this->apply_filters( 'connect_url', $this->_get_admin_page_url( '', $params, $network ) );
* @author Vova Feldman (@svovaf)
function get_reconnect_url( $params = array() ) {
$params['fs_action'] = 'reset_anonymous_mode';
$params['fs_unique_affix'] = $this->get_unique_affix();
return $this->get_activation_url( $params );
* Get the URL of the page that should be loaded after the user connect
* or skip in the opt-in screen.
* @author Vova Feldman (@svovaf)
* @param string $filter Filter name.
* @param array $params Since 1.2.2.7
* @param bool|null $network
function get_after_activation_url( $filter, $params = array(), $network = null ) {
if ( $this->show_opt_in_on_themes_page() &&
( fs_request_has( 'pending_activation' ) ||
// For cases when the first time path is set, even though it's a WP.org theme.
fs_request_get_bool( $this->get_unique_affix() . '_show_optin' ) )
$first_time_path = $this->_menu->get_first_time_path(
fs_is_network_admin() && $this->_is_network_active
if ( $this->_is_network_active &&
! $this->_menu->has_network_menu() &&
$this->is_network_registered()
$target_url = $this->get_account_url();
// Default plugin's page.
$target_url = $this->_get_admin_page_url( '', array(), $network );
return add_query_arg( $params, $this->apply_filters(
empty( $first_time_path ) ?
* Handle account page updates / edits / actions.
* @author Vova Feldman (@svovaf)
private function _handle_account_edits() {
if ( ! $this->is_user_admin() ) {
$action = fs_get_action();
if ( empty( $action ) ) {
$plugin_id = fs_request_get( 'plugin_id', $this->get_id() );
$install_id = fs_request_get( 'install_id', '' );
$oops_text = $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...';