: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @author Vova Feldman (@svovaf)
function _addons_page_render() {
$this->_logger->entrance();
$vars = array( 'id' => $this->_module_id );
* Added filter to the template to allow developers wrapping the template
* in custom HTML (e.g. within a wizard/tabs).
* @author Vova Feldman (@svovaf)
echo $this->apply_filters( 'templates/add-ons.php', fs_get_template( 'add-ons.php', $vars ) );
------------------------------------------------------------------------------------------------------------------*/
* @author Vova Feldman (@svovaf)
function _pricing_page_render() {
$this->_logger->entrance();
$vars = array( 'id' => $this->_module_id );
if ( 'true' === fs_request_get( 'checkout', false ) ) {
echo $this->apply_filters( 'templates/checkout.php', fs_get_template( 'checkout.php', $vars ) );
echo $this->apply_filters( 'templates/pricing.php', fs_get_template( 'pricing.php', $vars ) );
* @author Leo Fajardo (@leorw)
function _maybe_add_pricing_ajax_handler() {
if ( ! $this->should_use_external_pricing() ) {
$this->add_ajax_action( 'pricing_ajax_action', array( &$this, '_fs_pricing_ajax_action_handler' ) );
* @author Leo Fajardo (@leorw)
function _fs_pricing_ajax_action_handler() {
$this->check_ajax_referer( 'pricing_ajax_action' );
$pricing_action = fs_request_get( 'pricing_action' );
switch ( $pricing_action ) {
case 'fetch_pricing_data':
'trial' => fs_request_get_bool( 'trial' ),
'sandbox' => fs_request_get_raw( 'sandbox' ),
's_ctx_type' => fs_request_get_raw( 's_ctx_type' ),
's_ctx_id' => fs_request_get_raw( 's_ctx_id' ),
's_ctx_ts' => fs_request_get_raw( 's_ctx_ts' ),
's_ctx_secure' => fs_request_get_raw( 's_ctx_secure' ),
$bundle_id = $this->get_bundle_id();
$bundle_public_key = $this->get_bundle_public_key();
$has_bundle_context = ( FS_Plugin::is_valid_id( $bundle_id ) && ! empty( $bundle_public_key ) );
if ( ! $has_bundle_context ) {
$api = $this->get_api_plugin_scope();
$params['plugin_id'] = $this->get_id();
$params['plugin_public_key'] = $this->get_public_key();
$result = $api->get( 'pricing.json?' . http_build_query( $params ) );
fs_request_get( 'plan_id' )
if ( is_object( $result ) && $this->is_api_error( $result ) ) {
$this->_logger->api_error( $result );
self::shoot_ajax_failure(
isset( $result->error ) ?
( is_string( $result->error ) ? $result->error : $result->error->message ) :
var_export( $result, true )
$this->shoot_ajax_success( $result );
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
* Render contact-us page.
* @author Vova Feldman (@svovaf)
function _contact_page_render() {
$this->_logger->entrance();
$vars = array( 'id' => $this->_module_id );
* Added filter to the template to allow developers wrapping the template
* in custom HTML (e.g. within a wizard/tabs).
* @author Vova Feldman (@svovaf)
echo $this->apply_filters( 'templates/contact.php', fs_get_template( 'contact.php', $vars ) );
#endregion ------------------------------------------------------------------------
* Hide all admin notices to prevent distractions.
* @author Vova Feldman (@svovaf)
* @uses remove_all_actions()
private static function _hide_admin_notices() {
remove_all_actions( 'admin_notices' );
remove_all_actions( 'network_admin_notices' );
remove_all_actions( 'all_admin_notices' );
remove_all_actions( 'user_admin_notices' );
static function _clean_admin_content_section_hook() {
$hide_admin_notices = true;
if ( fs_request_is_action( 'allow_clone_resolution_notice' ) ) {
check_admin_referer( 'fs_allow_clone_resolution_notice' );
$hide_admin_notices = false;
if ( $hide_admin_notices ) {
self::_hide_admin_notices();
echo '<style>#wpfooter { display: none !important; }</style>';
* Attach to admin_head hook to hide all admin notices.
* @author Vova Feldman (@svovaf)
static function _clean_admin_content_section() {
add_action( 'admin_head', 'Freemius::_clean_admin_content_section_hook' );
------------------------------------------------------------------------------------------------------------------*/
/* function _enqueue_script($handle, $src) {
$url = plugins_url( substr( WP_FS__DIR_JS, strlen( $this->_plugin_dir_path ) ) . '/assets/js/' . $src );
$this->_logger->entrance( 'script = ' . $url );
wp_enqueue_script( $handle, $url );
------------------------------------------------------------------------------------------------------------------*/
* @author Vova Feldman (@svovaf)
function get_api_user_scope( $flush = false ) {
if ( ! isset( $this->_user_api ) || $flush ) {
$this->_user_api = $this->get_api_user_scope_by_user( $this->_user );
* @author Vova Feldman (@svovaf)
private function get_api_user_scope_by_user( FS_User $user ) {
* @author Leo Fajardo (@leorw)
private function get_current_or_network_user_api_scope( $flush = false ) {
if ( ! $this->_is_network_active ||
( isset( $this->_user ) && $this->_user instanceof FS_User )
return $this->get_api_user_scope( $flush );
$user = $this->get_current_or_network_user();
$this->_user_api = FS_Api::instance(
* @author Vova Feldman (@svovaf)
private function get_api_site_scope( $flush = false ) {
if ( ! isset( $this->_site_api ) || $flush ) {
$this->_site_api = FS_Api::instance(
$this->_site->public_key,
$this->_site->secret_key,
$this->get_sdk_version(),
self::get_unfiltered_site_url()
* @author Leo Fajardo (@leorw)
* @param bool $flush_instance
* @return array|mixed|string|void
* @throws Freemius_Exception
private function api_site_call( $path, $method = 'GET', $params = array(), $flush_instance = false ) {
$result = $this->get_api_site_scope( $flush_instance )->call( $path, $method, $params );
* Checks if the local install's URL is different from the remote install's URL, update the local install if necessary, and then run the clone handler if the install's URL is different from the URL of the site.
* @author Leo Fajardo (@leorw)
$this->is_registered() &&
FS_Api::is_api_result_entity( $result ) &&
$stored_local_url = trailingslashit( $this->_site->url );
$stored_remote_url = trailingslashit( $result->url );
if ( $stored_local_url !== $stored_remote_url ) {
$this->_site->url = $result->url;
if ( fs_strip_url_protocol( $stored_remote_url ) !== self::get_unfiltered_site_url( null, true, true ) ) {
FS_Clone_Manager::instance()->maybe_run_clone_resolution();
* Get plugin public API scope.
* @author Vova Feldman (@svovaf)
function get_api_plugin_scope() {
if ( ! isset( $this->_plugin_api ) ) {
$this->_plugin_api = FS_Api::instance(
$this->_plugin->public_key,
return $this->_plugin_api;
* Get bundle public API scope.
* @author Vova Feldman (@svovaf)
function get_api_bundle_scope() {
$this->get_bundle_public_key(),
* Get site API scope object (fallback to public plugin scope when not registered).
* @author Vova Feldman (@svovaf)
function get_api_site_or_plugin_scope() {
return $this->is_registered() ?
$this->get_api_site_scope() :
$this->get_api_plugin_scope();
* @author Leo Fajardo (@leorw)
private function maybe_modify_api_curl_error_message( $result ) {
'cUrlMissing' !== $result->error->type &&
( 'CurlException' !== $result->error->type || CURLE_COULDNT_CONNECT != $result->error->code ) &&
( 'HttpRequestFailed' !== $result->error->type || false === strpos( $result->error->message, 'cURL error ' . CURLE_COULDNT_CONNECT ) )
$result->error->message = $this->esc_html_inline( 'We use PHP cURL library for the API calls, which is a very common library and usually installed and activated out of the box. Unfortunately, cURL is not activated (or disabled) on your server.', 'curl-missing-message' ) .
'Please contact your hosting provider and ask them to whitelist %s for external connection.',
$this->apply_filters( 'api_domains', array(
$this->esc_html_inline( 'Once you are done, deactivate the %s and activate it again.', 'connectivity-reactivate-module' ),
* Show trial promotional notice (if any trial exist).
* @author Vova Feldman (@svovaf)
* @param FS_Plugin_Plan[] $plans
function _check_for_trial_plans( $plans ) {
* For some reason core's do_action() flattens arrays when it has a single object item. Therefore, we need to restructure the array as expected.
* @author Vova Feldman (@svovaf)
if ( ! is_array( $plans ) && is_object( $plans ) ) {
$plans = array( $plans );
if ( ! $this->is_array_instanceof( $plans, 'FS_Plugin_Plan' ) ) {
$this->_storage->has_trial_plan = FS_Plan_Manager::instance()->has_trial_plan( $plans );
* During trial promotion the "upgrade" submenu item turns to
* "start trial" to encourage the trial. Since we want to keep
* the same menu item handler and there's no robust way to
* add new arguments to the menu item link's querystring,
* use JavaScript to find the menu item and update the href of
* @author Vova Feldman (@svovaf)
function _fix_start_trial_menu_item_url() {
$template_args = array( 'id' => $this->_module_id );
fs_require_template( 'add-trial-to-pricing.php', $template_args );
* Check if module is currently in a trial promotion mode.
* @author Vova Feldman (@svovaf)
function is_in_trial_promotion() {
return $this->_admin_notices->has_sticky( 'trial_promotion' );
* Show trial promotional notice (if any trial exist).