: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$license_ids[ $install->license_id ] = true;
return array_keys( $license_ids );
* @author Vova Feldman (@svovaf)
* @return FS_Plugin_Plan|false
function _get_plan_by_id( $id ) {
$this->_logger->entrance();
if ( ! is_array( $this->_plans ) || 0 === count( $this->_plans ) ) {
foreach ( $this->_plans as $plan ) {
if ( $id == $plan->id ) {
* @author Vova Feldman (@svovaf)
* @return FS_Plugin_Plan|false
private function get_plan_by_name( $name ) {
$this->_logger->entrance();
if ( ! is_array( $this->_plans ) || 0 === count( $this->_plans ) ) {
foreach ( $this->_plans as $plan ) {
if ( $name == $plan->name ) {
* Sync local licenses with remote server.
* @author Vova Feldman (@svovaf)
* @param number|bool $site_license_id
* @param number|null $blog_id
* @return FS_Plugin_License[]|object
function _sync_licenses( $site_license_id = false, $blog_id = null ) {
$this->_logger->entrance();
$is_network_admin = fs_is_network_admin();
if ( $is_network_admin && is_null( $blog_id ) ) {
$all_licenses = self::get_all_licenses( $this->_module_id );
$all_licenses = $this->get_user_licenses( $this->_user->id );
$foreign_licenses = $this->get_foreign_licenses_info( $all_licenses, $site_license_id );
$all_licenses_map = array();
foreach ( $all_licenses as $license ) {
$all_licenses_map[ $license->id ] = true;
$licenses = $this->_fetch_licenses( false, $site_license_id, $foreign_licenses, $blog_id );
if ( $this->is_array_instanceof( $licenses, 'FS_Plugin_License' ) ) {
foreach ( $licenses as $license ) {
$licenses_map[ $license->id ] = true;
// $license_ids_to_keep = $this->get_license_ids_associated_with_installs();
// foreach ( $license_ids_to_keep as $license_id ) {
// if ( isset( $licenses_map[ $license_id ] ) ) {
// $missing_license = self::_get_license_by_id( $license_id, false );
// if ( is_object( $missing_license ) ) {
// $licenses[] = $missing_license;
// $licenses_map[ $missing_license->id ] = true;
$user_license_ids = $this->get_user_linked_license_ids( $this->_user->id );
foreach ( $user_license_ids as $key => $license_id ) {
if ( ! isset( $licenses_map[ $license_id ] ) ) {
// Remove access to licenses that no longer exist.
unset( $user_license_ids[ $key ] );
if ( ! empty( $user_license_ids ) ) {
foreach ( $licenses_map as $license_id => $value ) {
if ( ! isset( $all_licenses_map[ $license_id ] ) ) {
// Associate new licenses with the user who triggered the license syncing.
$user_license_ids[] = $license_id;
$user_license_ids = array_unique( $user_license_ids );
$user_license_ids = array_keys( $licenses_map );
if ( ! $is_network_admin || ! is_null( $blog_id ) ) {
$user_licenses = array();
foreach ( $licenses as $license ) {
if ( ! in_array( $license->id, $user_license_ids ) ) {
$user_licenses[] = $license;
$this->_licenses = $user_licenses;
$this->_licenses = $licenses;
$this->set_user_linked_license_ids( $this->_user->id, $user_license_ids );
$this->_store_licenses( true, $this->_module_id, $licenses );
// Update current license.
if ( is_object( $this->_license ) ) {
$license = $this->_get_license_by_id( $this->_license->id );
if ( is_object( $license ) ) {
* `$license` can be `false` in case a user change action has just been completed and this method
* has synced the `$this->_licenses` collection for the new user. In this case, the
* `$this->_licenses` collection may have only the newly activated license that is associated with
* the new user. `set_license` will eventually be called in the same request by the logic that
* follows outside this method which will detect that the install's license has been updated, and
* then `_update_site_license` will be called which in turn will call `set_license`.
* @author Leo Fajardo (@leorw)
$this->set_license( $license );
* @author Vova Feldman (@svovaf)
* @param bool $sync_licenses
* @return FS_Plugin_License|false
function _get_license_by_id( $id, $sync_licenses = true ) {
$this->_logger->entrance();
if ( ! FS_Plugin_License::is_valid_id( $id ) ) {
* When running from the network level admin and opted-in from the network,
* check if the license exists in the network user licenses collection.
* @author Vova Feldman (@svovaf)
if ( fs_is_network_admin() &&
$this->is_network_registered() &&
( ! is_object( $this->_user ) || $this->_storage->network_user_id != $this->_user->id )
$licenses = $this->get_user_licenses( $this->_storage->network_user_id );
foreach ( $licenses as $license ) {
if ( $id == $license->id ) {
if ( ! $this->has_any_license() && $sync_licenses ) {
$this->_sync_licenses( $id );
if ( is_array( $this->_licenses ) ) {
foreach ( $this->_licenses as $license ) {
if ( $id == $license->id ) {
* Get license by ID. Unlike _get_license_by_id(), this method only checks the local storage and return any license, whether it's associated with the current context user/install or not.
* @author Vova Feldman (@svovaf)
* @return FS_Plugin_License
private function get_license_by_id( $id ) {
$licenses = self::get_all_licenses( $this->_module_id );
if ( is_array( $licenses ) && ! empty( $licenses ) ) {
foreach ( $licenses as $license ) {
if ( $id == $license->id ) {
* Synchronize the site's context license by fetching the license form the API and updating the local data with it.
* @author Vova Feldman (@svovaf)
* @return \FS_Plugin_License|mixed
private function sync_site_license() {
$api = $this->get_api_user_scope();
$result = $api->get( "/licenses/{$this->_license->id}.json?license_key=" . urlencode( $this->_license->secret_key ), true );
if ( ! $this->is_api_result_entity( $result ) ) {
$license = $this->_update_site_license( new FS_Plugin_License( $result ) );
$this->_store_licenses();
* Get all user's available licenses for the current module.
* @author Vova Feldman (@svovaf)
* @return FS_Plugin_License[]
private function get_user_licenses( $user_id ) {
$all_licenses = self::get_all_licenses( $this->_module_id );
if ( empty( $all_licenses ) ) {
$user_license_ids = $this->get_user_linked_license_ids( $user_id );
if ( empty( $user_license_ids ) ) {
foreach ( $all_licenses as $license ) {
if ( in_array( $license->id, $user_license_ids ) ) {
* Checks if the context license is network activated except on the given blog ID.
* @author Vova Feldman (@svovaf)
* @param int $except_blog_id
private function is_license_network_active( $except_blog_id = 0 ) {
$this->_logger->entrance();
if ( ! is_object( $this->_license ) ) {
$sites = self::get_sites();
if ( $this->_license->total_activations() < ( count( $sites ) - 1 ) ) {
// There are more sites than the number of activations, so license cannot be network activated.
foreach ( $sites as $site ) {
$blog_id = self::get_site_blog_id( $site );
if ( $except_blog_id == $blog_id ) {
$install = $this->get_install_by_blog_id( $blog_id );
if ( is_object( $install ) && $install->license_id != $this->_license->id ) {
* Checks if license can be activated on all the network sites (opted-in or skipped) that are not yet associated with a license. If possible, try to make the activation, if not return false.
* Notice: On success, this method will also update the license activations counters (without updating the license in the storage).
* @author Vova Feldman (@svovaf)
* @param \FS_Plugin_License $license
private function try_activate_license_on_network( FS_User $user, FS_Plugin_License $license ) {
$this->_logger->entrance();
$result = $this->can_activate_license_on_network( $license );
if ( false === $result ) {
$installs_without_license = $result['installs'];
if ( ! empty( $installs_without_license ) ) {
$this->activate_license_on_many_installs( $user, $license->secret_key, $installs_without_license );
$disconnected_site_ids = $result['sites'];
if ( ! empty( $disconnected_site_ids ) ) {
$this->activate_license_on_many_sites( $user, $license->secret_key, $disconnected_site_ids );
$this->link_license_2_user( $license->id, $user->id );
// Sync license after activations.
$license->activated += $result['production_count'];
$license->activated_local += $result['localhost_count'];
// $this->_store_licenses()
* Checks if the given license can be activated on the whole network.
* @author Vova Feldman (@svovaf)
* @param \FS_Plugin_License $license
* @type array[int]FS_Site $installs Blog ID to install map.
* @type int[] $sites Non-connected blog IDs.
* @type int $production_count Production sites count.
* @type int $localhost_count Production sites count.
private function can_activate_license_on_network( FS_Plugin_License $license ) {
$sites = self::get_sites();
$installs_without_license = array();
$disconnected_site_ids = array();
foreach ( $sites as $site ) {
$blog_id = self::get_site_blog_id( $site );
$install = $this->get_install_by_blog_id( $blog_id );
if ( is_object( $install ) ) {
if ( FS_Plugin_License::is_valid_id( $install->license_id ) ) {
// License already activated on the install.
$installs_without_license[ $blog_id ] = $install;
$url = is_object( $site ) ?
self::get_unfiltered_site_url( $blog_id );
$disconnected_site_ids[] = $blog_id;
if ( FS_Site::is_localhost_by_address( $url ) ) {
if ( ! $license->can_activate_bulk( $production_count, $localhost_count ) ) {
'installs' => $installs_without_license,
'sites' => $disconnected_site_ids,
'production_count' => $production_count,
'localhost_count' => $localhost_count,
* Activate a given license on a collection of installs.
* @author Vova Feldman (@svovaf)
* @param string $license_key
* @param array $blog_2_install_map {
* @value FS_Site Blog's associated install.
private function activate_license_on_many_installs(
array $blog_2_install_map
array( 'license_key' => $this->apply_filters( 'license_key', $license_key ) )
$install_2_blog_map = array();
foreach ( $blog_2_install_map as $blog_id => $install ) {
$params[] = array( 'id' => $install->id, 'url' => $install->url );
$install_2_blog_map[ $install->id ] = $blog_id;
$result = $this->get_api_user_scope_by_user( $user )->call(
"plugins/{$this->_plugin->id}/installs.json",
if ( ! $this->is_api_result_object( $result, 'installs' ) ) {
foreach ( $result->installs as $r_install ) {
$install = new FS_Site( $r_install );
$install->is_disconnected = false;
$install_2_blog_map[ $r_install->id ],