: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param string $text Translatable string.
* @param string $context Context information for the translators.
* @param string $key String key for overrides.
function get_text_x_inline( $text, $context, $key ) {
return _fs_text_x_inline( $text, $context, $key, $this->_slug );
* @author Vova Feldman (@svovaf)
* @param string $text Translatable string.
* @param string $key String key for overrides.
function esc_html_inline( $text, $key ) {
return esc_html( _fs_text_inline( $text, $key, $this->_slug ) );
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
* Check if Freemius in SDK upgrade mode.
* @author Vova Feldman (@svovaf)
function is_sdk_upgrade_mode() {
return isset( $this->_storage->sdk_upgrade_mode ) ?
$this->_storage->sdk_upgrade_mode :
* Turn SDK upgrade mode off.
* @author Vova Feldman (@svovaf)
function set_sdk_upgrade_complete() {
$this->_storage->sdk_upgrade_mode = false;
* Check if plugin upgrade mode.
* @author Vova Feldman (@svovaf)
function is_plugin_upgrade_mode() {
return isset( $this->_storage->plugin_upgrade_mode ) ?
$this->_storage->plugin_upgrade_mode :
* Turn plugin upgrade mode off.
* @author Vova Feldman (@svovaf)
function set_plugin_upgrade_complete() {
$this->_storage->plugin_upgrade_mode = false;
$license_migration = ! empty( $this->_storage->license_migration ) ?
$this->_storage->license_migration :
$license_migration['is_migrating'] = false;
$this->_storage->license_migration = $license_migration;
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
* Check if specific permission requested.
* @author Vova Feldman (@svovaf)
* @param string $permission
function is_permission_requested( $permission ) {
return isset( $this->_permissions[ $permission ] ) && ( true === $this->_permissions[ $permission ] );
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
* Hints the SDK if running an auto-installation.
private $_isAutoInstall = false;
* After upgrade callback to install and auto activate a plugin.
* This code will only be executed on explicit request from the user,
* following the practice Jetpack are using with their theme installations.
* @link https://make.wordpress.org/plugins/2017/03/16/clarification-of-guideline-8-executable-code-and-installs/
* @author Vova Feldman (@svovaf)
function _install_premium_version_ajax_action() {
$this->_logger->entrance();
$this->check_ajax_referer( 'install_premium_version' );
if ( ! $this->is_registered() ) {
self::shoot_ajax_failure( array(
'message' => $this->get_text_inline( 'Auto installation only works for opted-in users.', 'auto-install-error-not-opted-in' ),
'code' => 'premium_installed',
$plugin_id = fs_request_get( 'target_module_id', $this->get_id() );
if ( ! FS_Plugin::is_valid_id( $plugin_id ) ) {
self::shoot_ajax_failure( array(
'message' => $this->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
'code' => 'invalid_module_id',
if ( $plugin_id == $this->get_id() ) {
if ( $this->is_premium() ) {
// Already using the premium code version.
self::shoot_ajax_failure( array(
'message' => $this->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ),
'code' => 'premium_installed',
if ( ! $this->can_use_premium_code() ) {
// Don't have access to the premium code.
self::shoot_ajax_failure( array(
'message' => $this->get_text_inline( 'You do not have a valid license to access the premium version.', 'auto-install-error-invalid-license' ),
'code' => 'invalid_license',
if ( ! $this->has_release_on_freemius() ) {
// Plugin is a serviceware, no premium code version.
self::shoot_ajax_failure( array(
'message' => $this->get_text_inline( 'Plugin is a "Serviceware" which means it does not have a premium code version.', 'auto-install-error-serviceware' ),
'code' => 'premium_version_missing',
$addon = $this->get_addon( $plugin_id );
if ( ! is_object( $addon ) ) {
self::shoot_ajax_failure( array(
'message' => $this->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
'code' => 'invalid_module_id',
if ( $this->is_addon_activated( $plugin_id, true ) ) {
// Premium add-on version is already activated.
self::shoot_ajax_failure( array(
'message' => $this->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ),
'code' => 'premium_installed',
$this->_isAutoInstall = true;
// Try to install and activate.
$updater = FS_Plugin_Updater::instance( $this );
$result = $updater->install_and_activate_plugin( $plugin_id );
if ( is_array( $result ) && ! empty( $result['message'] ) ) {
self::shoot_ajax_failure( array(
'message' => $result['message'],
'code' => $result['code'],
self::shoot_ajax_success( $result );
* Displays module activation dialog box after a successful upgrade
* where the user explicitly requested to auto download and install
* @author Vova Feldman (@svovaf)
function _add_auto_installation_dialog_box() {
$this->_logger->entrance();
if ( ! $this->is_registered() ) {
$plugin_id = fs_request_get( 'plugin_id', $this->get_id() );
if ( ! FS_Plugin::is_valid_id( $plugin_id ) ) {
if ( $plugin_id == $this->get_id() ) {
if ( $this->is_premium() ) {
// Already using the premium code version.
if ( ! $this->can_use_premium_code() ) {
// Don't have access to the premium code.
if ( ! $this->has_release_on_freemius() ) {
// Plugin is a serviceware, no premium code version.
$addon = $this->get_addon( $plugin_id );
if ( ! is_object( $addon ) ) {
if ( $this->is_addon_activated( $plugin_id, true ) ) {
// Premium add-on version is already activated.
'id' => $this->_module_id,
'target_module_id' => $plugin_id,
fs_require_template( 'auto-installation.php', $vars );
#--------------------------------------------------------------------------------
#--------------------------------------------------------------------------------
#region Module's Original Tabs
* Inject a JavaScript logic to capture the theme tabs HTML.
* @author Vova Feldman (@svovaf)
function _tabs_capture() {
$this->_logger->entrance();
! $this->is_product_settings_page() ||
! $this->should_page_include_tabs() ||
! $this->is_matching_url( $this->main_menu_url() )
'id' => $this->_module_id,
fs_require_once_template( 'tabs-capture-js.php', $params );
* Cache theme's tabs HTML for a week. The cache will also be set as expired
* after version and type (free/premium) changes, in addition to the week period.
* @author Vova Feldman (@svovaf)
function _store_tabs_ajax_action() {
$this->_logger->entrance();
$this->check_ajax_referer( 'store_tabs' );
// Init filesystem if not yet initiated.
// Get POST body HTML data.
$tabs_html = $wp_filesystem->get_contents( "php://input" );
if ( is_string( $tabs_html ) ) {
$tabs_html = trim( $tabs_html );
if ( ! is_string( $tabs_html ) || empty( $tabs_html ) ) {
self::shoot_ajax_failure();
$this->_cache->set( 'tabs', $tabs_html, 7 * WP_FS__TIME_24_HOURS_IN_SEC );
self::shoot_ajax_success();
* Cache theme's settings page custom styles. The cache will also be set as expired
* after version and type (free/premium) changes, in addition to the week period.
* @author Vova Feldman (@svovaf)
function _store_tabs_styles() {
$this->_logger->entrance();
! $this->is_product_settings_page() ||
! $this->should_page_include_tabs() ||
! $this->is_matching_url( $this->main_menu_url() )
$wp_styles = wp_styles();
$theme_styles_url = get_template_directory_uri();
foreach ( $wp_styles->queue as $handler ) {
if ( fs_starts_with( $handler, 'fs_' ) ) {
// Assume that stylesheets that their handler starts with "fs_" belong to the SDK.
* @var _WP_Dependency $stylesheet
$stylesheet = $wp_styles->registered[ $handler ];
if ( fs_starts_with( $stylesheet->src, $theme_styles_url ) ) {
$stylesheets[] = $stylesheet->src;
if ( ! empty( $stylesheets ) ) {
$this->_cache->set( 'tabs_stylesheets', $stylesheets, 7 * WP_FS__TIME_24_HOURS_IN_SEC );
* Check if module's original settings page has any tabs.
* @author Vova Feldman (@svovaf)
private function has_tabs() {
return $this->_cache->has( 'tabs' );
* Get module's settings page HTML content, starting
* from the beginning of the <div class="wrap"> element,
* until the tabs HTML (including).
* @author Vova Feldman (@svovaf)
private function get_tabs_html() {
$this->_logger->entrance();
return $this->_cache->get( 'tabs' );
* Check if page should include tabs.
* @author Vova Feldman (@svovaf)
private function should_page_include_tabs() {
if ( ! $this->has_settings_menu() ) {
// Don't add tabs if no settings at all.
if ( self::NAVIGATION_TABS !== $this->_navigation ) {
// Only add tabs to themes for now.
if ( $this->is_theme() && ! $this->has_paid_plan() && ! $this->has_addons() ) {
// Only add tabs to monetizing themes.
if ( ! $this->is_product_settings_page() ) {
// Only add tabs if browsing one of the product's setting pages.
if ( $this->is_activation_mode() && $this->is_activation_page() ) {
// Don't include tabs in the activation page.
if ( $this->is_admin_page( 'pricing' ) && fs_request_get_bool( 'checkout' ) ) {
// Don't add tabs on checkout page, we want to reduce distractions
* Add the tabs HTML before the setting's page content and
* enqueue any required stylesheets.
* @author Vova Feldman (@svovaf)
* @return bool If tabs were included.
function _add_tabs_before_content() {
$this->_logger->entrance();
if ( ! $this->should_page_include_tabs() ) {
$tabs_html = $this->get_tabs_html();
if ( empty( $tabs_html ) ) {
* Enqueue the original stylesheets that are included in the
* theme settings page. That way, if the theme settings has
* some custom _styled_ content above the tabs UI, this
* will make sure that the styling is preserved.
$stylesheets = $this->_cache->get( 'tabs_stylesheets', array() );
if ( is_array( $stylesheets ) ) {
for ( $i = 0, $len = count( $stylesheets ); $i < $len; $i ++ ) {
wp_enqueue_style( "fs_{$this->_module_id}_tabs_{$i}", $stylesheets[ $i ] );
// Cut closing </div> tag.
echo substr( trim( $tabs_html ), 0, - 6 );
* Add the tabs closing HTML after the setting's page content.
* @author Vova Feldman (@svovaf)
* @return bool If tabs closing HTML was included.
function _add_tabs_after_content() {
$this->_logger->entrance();
if ( ! $this->should_page_include_tabs() ) {