: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
return fs_is_network_admin() ?
$this->is_network_activation_mode( $and_on ) :
$this->is_site_activation_mode( $and_on );
* Is plugin in activation mode.
* @author Vova Feldman (@svovaf)
function is_site_activation_mode( $and_on = true ) {
( $this->is_on() || ! $and_on ) &&
( $this->is_premium() && true === $this->_storage->require_license_activation ) ||
( ! $this->is_registered() ||
( $this->is_only_premium() && ! $this->has_features_enabled_license() ) ) &&
( ! $this->is_enable_anonymous() ||
( ! $this->is_anonymous() && ! $this->is_pending_activation() ) )
* Checks if the SDK in network activation mode.
* @author Leo Fajardo (@leorw)
private function is_network_activation_mode( $and_on = true ) {
if ( ! $this->_is_network_active ) {
// Not network activated.
if ( $this->is_network_upgrade_mode() ) {
// Special flag to enforce network activation mode to decide what to do with the sites that are not yet opted-in nor skipped.
if ( ! $this->is_site_activation_mode( $and_on ) ) {
// Whether the context is single site or the network, if the plugin is no longer in activation mode then it is not in network activation mode as well.
if ( $this->is_network_delegated_connection() ) {
// Super-admin delegated the connection to the site admins -> not activation mode.
if ( $this->is_network_anonymous() && true !== $this->_storage->require_license_activation ) {
// Super-admin skipped the connection network wide -> not activation mode.
if ( $this->is_network_registered() ) {
// Super-admin connected at least one site -> not activation mode.
* Check if current page is the opt-in/pending-activation page.
* @author Vova Feldman (@svovaf)
function is_activation_page() {
if ( $this->_menu->is_activation_page( $this->show_opt_in_on_themes_page() ) ) {
if ( ! $this->is_activation_mode() ) {
// Check if current page is matching the activation page.
return $this->is_matching_url( $this->get_activation_url() );
* Check if URL path's are matching and that all querystring
* arguments of the $sub_url exist in the $url with the same values.
* 1. This method doesn't check if the sub/domain are matching.
* 2. Ignore case sensitivity.
* @author Vova Feldman (@svovaf)
* @param string $url If argument is not set, check if the sub_url matching the current's page URL.
private function is_matching_url( $sub_url, $url = '' ) {
$url = $_SERVER['REQUEST_URI'];
$url = strtolower( $url );
$sub_url = strtolower( $sub_url );
if ( parse_url( $sub_url, PHP_URL_PATH ) !== parse_url( $url, PHP_URL_PATH ) ) {
// Different path - DO NOT OVERRIDE PAGE.
$url_params = fs_parse_url_params( $url );
$sub_url_params = fs_parse_url_params( $sub_url );
foreach ( $sub_url_params as $key => $val ) {
if ( ! isset( $url_params[ $key ] ) || $val != $url_params[ $key ] ) {
// Not matching query string - DO NOT OVERRIDE PAGE.
* Get the basenames of all active plugins for specific blog. Including network activated plugins.
* @author Vova Feldman (@svovaf)
private static function get_active_plugins_basenames( $blog_id = 0 ) {
if ( is_multisite() && $blog_id > 0 ) {
$active_basenames = get_blog_option( $blog_id, 'active_plugins' );
$active_basenames = get_option( 'active_plugins' );
if ( ! is_array( $active_basenames ) ) {
$active_basenames = array();
$network_active_basenames = get_site_option( 'active_sitewide_plugins' );
if ( is_array( $network_active_basenames ) && ! empty( $network_active_basenames ) ) {
$active_basenames = array_merge( $active_basenames, array_keys( $network_active_basenames ) );
return $active_basenames;
* @author Leo Fajardo (@leorw)
static function get_active_plugins_directories_map( $blog_id = 0 ) {
$active_basenames = self::get_active_plugins_basenames( $blog_id );
foreach ( $active_basenames as $active_basename ) {
$active_basename = fs_normalize_path( $active_basename );
if ( false === strpos( $active_basename, '/' ) ) {
$map[ dirname( $active_basename ) ] = true;
* Get collection of all active plugins. Including network activated plugins.
* @author Vova Feldman (@svovaf)
* @param int $blog_id Since 2.0.0
* @return array[string]array
private static function get_active_plugins( $blog_id = 0 ) {
self::require_plugin_essentials();
$active_plugin = array();
$all_plugins = fs_get_plugins();
$active_plugins_basenames = self::get_active_plugins_basenames( $blog_id );
foreach ( $active_plugins_basenames as $plugin_basename ) {
$active_plugin[ $plugin_basename ] = $all_plugins[ $plugin_basename ];
* Get collection of all site active plugins for a specified blog.
* @author Vova Feldman (@svovaf)
* @return array[string]array
private static function get_site_active_plugins( $blog_id = 0 ) {
$active_basenames = ( is_multisite() && $blog_id > 0 ) ?
get_blog_option( $blog_id, 'active_plugins' ) :
get_option( 'active_plugins' );
if ( ! is_array( $active_basenames ) ) {
foreach ( $active_basenames as $basename ) {
$active[ $basename ] = array(
'Version' => '1.0', // Dummy version.
'slug' => self::get_plugin_slug( $basename ),
* Get collection of all plugins with their activation status for a specified blog.
* @author Vova Feldman (@svovaf)
* @param int $blog_id Since 2.0.0
* @return array Key is the plugin file path and the value is an array of the plugin data.
private static function get_all_plugins( $blog_id = 0 ) {
self::require_plugin_essentials();
$all_plugins = fs_get_plugins();
$active_plugins_basenames = self::get_active_plugins_basenames( $blog_id );
foreach ( $all_plugins as $basename => &$data ) {
// By default set to inactive (next foreach update the active plugins).
$data['is_active'] = false;
// Enrich with plugin slug.
$data['slug'] = self::get_plugin_slug( $basename );
foreach ( $active_plugins_basenames as $basename ) {
if ( isset( $all_plugins[ $basename ] ) ) {
$all_plugins[ $basename ]['is_active'] = true;
* Get collection of all plugins and if they are network level activated.
* @author Vova Feldman (@svovaf)
* @return array Key is the plugin basename and the value is an array of the plugin data.
private static function get_network_plugins() {
self::require_plugin_essentials();
$all_plugins = fs_get_plugins();
$network_active_basenames = is_multisite() ?
get_site_option( 'active_sitewide_plugins' ) :
foreach ( $all_plugins as $basename => &$data ) {
// By default set to inactive (next foreach update the active plugins).
$data['is_active'] = false;
// Enrich with plugin slug.
$data['slug'] = self::get_plugin_slug( $basename );
foreach ( $network_active_basenames as $basename ) {
if ( isset( $all_plugins[ $basename ] ) ) {
$all_plugins[ $basename ]['is_active'] = true;
* Cached result of get_site_transient( 'update_plugins' )
* @author Vova Feldman (@svovaf)
private static $_plugins_info;
* Helper function to get specified plugin's slug.
* @author Vova Feldman (@svovaf)
private static function get_plugin_slug( $basename ) {
if ( ! isset( self::$_plugins_info ) ) {
self::$_plugins_info = get_site_transient( 'update_plugins' );
if ( is_object( self::$_plugins_info ) ) {
if ( isset( self::$_plugins_info->no_update ) &&
isset( self::$_plugins_info->no_update[ $basename ] ) &&
! empty( self::$_plugins_info->no_update[ $basename ]->slug )
$slug = self::$_plugins_info->no_update[ $basename ]->slug;
} else if ( isset( self::$_plugins_info->response ) &&
isset( self::$_plugins_info->response[ $basename ] ) &&
! empty( self::$_plugins_info->response[ $basename ]->slug )
$slug = self::$_plugins_info->response[ $basename ]->slug;
// Try to find slug from FS data.
$slug = self::find_slug_by_basename( $basename );
// Fallback to plugin's folder name.
$slug = dirname( $basename );
private static $_statics_loaded = false;
* @author Vova Feldman (@svovaf)
private static function _load_required_static() {
if ( self::$_statics_loaded ) {
self::$_static_logger = FS_Logger::get_logger( WP_FS__SLUG, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
self::$_static_logger->entrance();
self::$_accounts = FS_Options::instance( WP_FS__ACCOUNTS_OPTION_NAME, true );
$has_skipped_migration = (
// 'id_slug_type_path_map' - was never stored on older versions, therefore, not exists on the site level.
null === self::$_accounts->get_option( 'id_slug_type_path_map', null, false ) &&
// 'file_slug_map' stored on the site level, so it was running an SDK version before it was integrated with MS-network.
null !== self::$_accounts->get_option( 'file_slug_map', null, false )
* If the file_slug_map exists on the site level but doesn't exist on the
* network level storage, it means that we need to process the storage with migration.
* The code in this `if` scope will only be executed once and only for the first site that will execute it because once we migrate the storage data, file_slug_map will be already set in the network level storage.
* @author Vova Feldman (@svovaf)
( $has_skipped_migration && true !== self::$_accounts->get_option( 'ms_migration_complete', false, true ) ) ||
( null === self::$_accounts->get_option( 'file_slug_map', null, true ) &&
null !== self::$_accounts->get_option( 'file_slug_map', null, false ) )
self::migrate_options_to_network();
self::$_global_admin_notices = FS_Admin_Notices::instance( 'global' );
if ( ! WP_FS__DEMO_MODE ) {
add_action( ( fs_is_network_admin() ? 'network_' : '' ) . 'admin_menu', array(
add_action( "wp_ajax_fs_toggle_debug_mode", array( 'Freemius', '_toggle_debug_mode' ) );
self::add_ajax_action_static( 'get_debug_log', array( 'Freemius', '_get_debug_log' ) );
self::add_ajax_action_static( 'get_db_option', array( 'Freemius', '_get_db_option' ) );
self::add_ajax_action_static( 'set_db_option', array( 'Freemius', '_set_db_option' ) );
if ( 0 == did_action( 'plugins_loaded' ) ) {
add_action( 'plugins_loaded', array( 'Freemius', '_load_textdomain' ), 1 );
$clone_manager = FS_Clone_Manager::instance();
add_action( 'init', array( $clone_manager, '_init' ) );
add_action( 'admin_footer', array( 'Freemius', '_open_support_forum_in_new_page' ) );
if ( self::is_plugins_page() || self::is_themes_page() ) {
add_action( 'admin_print_footer_scripts', array( 'Freemius', '_maybe_add_beta_label_styles' ), 9 );
* Specifically use this hook so that the JS event handlers will work properly on the "Themes"
* @author Leo Fajardo (@leorw)
add_action( 'admin_footer-' . self::get_current_page(), array( 'Freemius', '_maybe_add_beta_label_to_plugins_and_handle_confirmation') );
self::$_statics_loaded = true;
#--------------------------------------------------------------------------------
#--------------------------------------------------------------------------------
* @author Leo Fajardo (@leorw)
* @param bool $only_if_manual_resolution_is_not_hidden
private function is_unresolved_clone( $only_if_manual_resolution_is_not_hidden = false ) {
if ( ! $this->is_clone( $only_if_manual_resolution_is_not_hidden ) ) {
return FS_Clone_Manager::instance()->has_temporary_duplicate_mode_expired();
* @author Leo Fajardo (@leorw)
* @param bool $only_if_manual_resolution_is_not_hidden
function is_clone( $only_if_manual_resolution_is_not_hidden = false ) {
if ( ! is_object( $this->_site ) ) {
FS_Site::is_valid_id( $this->_storage->network_install_blog_id )
// Ensure that we're comparing the network install's URL with the relevant subsite's URL.
$blog_id = $this->_storage->network_install_blog_id;