You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1019 lines
33 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
/**
* Plugin update class
*
* @since 1.0
* @package RankMathPro
* @subpackage RankMathPro\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMathPro\Plugin_Update;
use RankMath\KB;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use MyThemeShop\Helpers\Param;
use RankMath\Admin\Admin_Helper;
defined( 'ABSPATH' ) || exit;
/**
* Plugin_Update class
*/
class Plugin_Update {
use Hooker;
/**
* Placeholder for opening tag inserted with JS.
*
* @var string
*/
const NOTICE_START_MARKER = '[[';
/**
* Placeholder for closing tag inserted with JS.
*
* @var string
*/
const NOTICE_END_MARKER = ']]';
/**
* Plugin slug.
*
* @var string
*/
private $slug = 'seo-by-rank-math-pro';
/**
* Rank Math API URL.
*
* @var string
*/
private $api_url = 'https://rankmath.com/wp-json/rankmath/v1';
/**
* Keep a log of external requests made in this thread so we can avoid
* running them multiple times.
*
* @var string
*/
private $requested = [];
/**
* The Constructor.
*
* @return void
*/
public function __construct() {
$this->action( 'admin_enqueue_scripts', 'enqueue' );
$this->action( 'admin_notices', 'admin_license_notice', 20 );
$this->action( 'in_plugin_update_message-seo-by-rank-math-pro/rank-math-pro.php', 'beta_update_message', 20, 2 );
$this->action( 'in_plugin_update_message-' . plugin_basename( RANK_MATH_PRO_FILE ), 'in_plugin_update_message', 30, 2 );
$this->action( 'add_option_rank_math_connect_data', 'check_and_inject' );
$this->action( 'update_option_rank_math_connect_data', 'check_and_inject' );
$this->action( 'delete_option_rank_math_connect_data', 'check_and_inject' );
$this->action( 'rank_math/settings/toggle_auto_update', 'toggle_auto_update', 10, 1 );
$this->action( 'update_site_option_auto_update_plugins', 'connect_auto_update_toggles', 20, 4 );
$this->filter( 'plugin_action_links_' . plugin_basename( RANK_MATH_PRO_FILE ), 'plugin_action_links', 50 );
$this->filter( 'pre_set_site_transient_update_plugins', 'maybe_inject_update', 20, 1 );
$this->filter( 'site_transient_update_plugins', 'maybe_disable_update', 90, 1 );
$this->filter( 'pre_set_site_transient_update_plugins', 'maybe_add_upgrade_notice', 120, 1 );
$this->filter( 'plugins_api', 'filter_info', 10, 3 );
$this->filter( 'admin_print_footer_scripts-plugin-install.php', 'iframe_footer_scripts', 10, 3 );
$this->filter( 'auto_update_plugin', 'auto_update_plugin', 20, 2 );
$this->filter( 'plugin_auto_update_setting_html', 'plugin_auto_update_setting_html', 10, 3 );
$this->filter( 'rank_math/admin/should_send_update_notification', 'should_send_update_notification', 10, 2 );
$this->filter( 'rank_math/admin/update_notification_products', 'update_notification_products', 10, 2 );
}
/**
* Enqueue styles and scripts.
*
* @param string $hook Page hook prefix.
*
* @return void
*/
public function enqueue( $hook ) {
if ( 'update-core.php' !== $hook ) {
return;
}
Helper::add_json( 'canUpdatePro', $this->can_update() );
Helper::add_json( 'betaOptinEnabled', Helper::get_settings( 'general.beta_optin' ) );
wp_enqueue_script( 'rank-math-pro-updates', RANK_MATH_PRO_URL . 'assets/admin/js/updates.js', [], RANK_MATH_PRO_VERSION, true );
}
/**
* Returns false if Rank Math Pro update object exists but its 'package'
* item is empty, which means that the user cannot update. Returns true in
* all other cases even if update is not available ATM.
*
* @param mixed $transient Update object to check or null to load transient.
* @return boolean
*/
public function can_update( $transient = null ) {
if ( is_null( $transient ) ) {
$transient = get_site_transient( 'update_plugins' );
}
if ( ! is_object( $transient ) ) {
return true;
}
if ( ! isset( $transient->response ) || ! isset( $transient->response['seo-by-rank-math-pro/rank-math-pro.php'] ) ) {
return true;
}
return ( ! empty( $transient->response['seo-by-rank-math-pro/rank-math-pro.php']->package ) );
}
/**
* Add connect/activation notice.
*
* @return void
*/
public function admin_license_notice() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
if ( $this->is_block_editor_page() ) {
return;
}
if ( Helper::is_site_connected() || rank_math()->registration->invalid ) {
return;
}
?>
<div class="notice notice-success rank-math-notice">
<p>
<?php
esc_html_e( 'Rank Math PRO is installed but it is not connected to your account, so you are missing out on important SEO features.', 'rank-math-pro' );
echo '<br>';
// translators: 1: opening HTML anchor tag, 2: closing HTML anchor tags.
echo wp_kses_post( sprintf( __( '%1$sConnect now%2$s. It only takes 20 seconds!', 'rank-math-pro' ), '<a href="' . esc_url( Admin_Helper::get_activate_url() ) . '">', '</a>' ) );
?>
</p>
</div>
<?php
}
/**
* Check if we are on Block Editor page.
*
* @return bool
*/
private function is_block_editor_page() {
$current_screen = get_current_screen();
if ( $current_screen instanceof \WP_Screen && method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) {
return true;
}
if ( function_exists( 'is_gutenberg_page' ) && is_gutenberg_page() ) {
return true;
}
return false;
}
/**
* Add Connect/Activate action link
*
* @param array $links Action links.
* @return array
*/
public function plugin_action_links( $links ) {
if ( ! Helper::is_site_connected() ) {
$links['activate_license'] = sprintf( '<a href="%s" class="rank-math-pro-activate-link" style="color:green">%s</a>', esc_url( Admin_Helper::get_activate_url( network_admin_url( 'plugins.php' ) ) ), __( 'Enable updates', 'rank-math-pro' ) );
}
return $links;
}
/**
* If user requested check with force-check parameter.
*
* @return bool
*/
public function is_check_requested() {
return (bool) Param::get( 'force-check' );
}
/**
* Check for updates & inject to update_plugins transient.
*
* @return void
*/
public function check_and_inject() {
$this->inject_update( $this->fetch_latest_version( true ) );
}
/**
* Inject update fetched from the rankmath.com API or pushed to this site via the REST API.
*
* @param object $transient Origial transient.
* @return mixed
*/
public function maybe_inject_update( $transient ) {
$force_check = $this->is_check_requested();
return $this->inject_update( $this->fetch_latest_version( $force_check ), $transient );
}
/**
* Remove package download URL if needed.
*
* @param object $transient Original transient.
* @return mixed
*/
public function maybe_disable_update( $transient ) {
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
return $transient;
}
// If we're in the process of updating RM Free then don't disable RM Pro update.
if (
( Param::get( 'action' ) === 'update-selected' && strpos( Param::get( 'plugins', '' ), 'seo-by-rank-math/rank-math.php' ) !== false ) ||
$this->do_filter( 'updates/remove_restrictions', false )
) {
return $transient;
}
if ( isset( $transient->response['seo-by-rank-math/rank-math.php'] )
&& isset( $transient->response['seo-by-rank-math-pro/rank-math-pro.php'] )
&& ! empty( $transient->response['seo-by-rank-math-pro/rank-math-pro.php']->package )
) {
unset( $transient->response['seo-by-rank-math-pro/rank-math-pro.php']->package );
$transient->response['seo-by-rank-math-pro/rank-math-pro.php']->unavailability_reason = 'update_free';
}
return $transient;
}
/**
* Add upgrade notice if needed, which is displayed on the Updates page (wp-admin/update-core.php)
*
* @param object $transient Original transient.
* @return mixed
*/
public function maybe_add_upgrade_notice( $transient ) {
if ( ! $this->can_update( $transient ) ) {
$before = self::NOTICE_START_MARKER;
$before .= __( 'Automatic updates are not available.', 'rank-math-pro' );
$before .= ' ';
$after = self::NOTICE_END_MARKER;
$message = $this->get_update_message( $transient->response['seo-by-rank-math-pro/rank-math-pro.php']->unavailability_reason );
$message = $before . $message . $after;
$transient->response['seo-by-rank-math-pro/rank-math-pro.php']->upgrade_notice = $message;
}
return $transient;
}
/**
* Inject our update in the update_plugins transient.
*
* @param mixed $update New update object or array, or false to clear current update.
* @param mixed $transient Original updates transient.
* @return mixed
*/
public function inject_update( $update, $transient = null ) {
$save = false;
if ( is_null( $transient ) ) {
$transient = get_site_transient( 'update_plugins' );
$save = true;
}
$plugin = plugin_basename( RANK_MATH_PRO_FILE );
if ( false !== $update ) {
$obj = $this->get_default_update_data();
$obj = (object) array_merge( (array) $obj, (array) $update );
$obj = $this->maybe_apply_beta( $obj );
// If a newer version is already present in the update_plugins transient then don't inject.
if ( $this->has_newer_version( $transient, $obj->new_version ) ) {
return $transient;
}
// Default transient data.
if ( empty( $transient ) ) {
$transient = new \stdClass();
$transient->last_checked = time();
$transient->checked = [ $plugin => RANK_MATH_PRO_VERSION ];
$transient->response = [];
}
// Inject if new data has URL and is a newer version than the one currently in use.
if ( version_compare( $obj->new_version, RANK_MATH_PRO_VERSION, '>' ) ) {
$transient->response[ $plugin ] = $obj;
if ( $save ) {
set_site_transient( 'update_plugins', $transient );
}
} else {
// No update is available.
$item = (object) [
'id' => 'seo-by-rank-math-pro/rank-math-pro.php',
'slug' => 'seo-by-rank-math-pro',
'plugin' => 'seo-by-rank-math-pro/rank-math-pro.php',
'new_version' => RANK_MATH_PRO_VERSION,
'url' => '',
'package' => '',
'icons' => [],
'banners' => [],
'banners_rtl' => [],
'tested' => '',
'requires_php' => '',
'compatibility' => new \stdClass(),
];
if ( empty( $transient->no_update ) ) {
$transient->no_update = [];
}
$transient->no_update[ $plugin ] = $item;
}
} elseif ( isset( $transient->response[ $plugin ] ) ) {
unset( $transient->response[ $plugin ] );
}
return $transient;
}
/**
* Replace `new_version` & `package` properties with beta if needed.
*
* @param object $update_object Plugin update data object.
* @return object
*/
public function maybe_apply_beta( $update_object ) {
if ( ! Helper::get_settings( 'general.beta_optin' ) ) {
return $update_object;
}
if (
empty( $update_object->new_version )
|| empty( $update_object->beta_version )
|| empty( $update_object->package )
|| empty( $update_object->beta_package )
) {
return $update_object;
}
if ( version_compare( $update_object->new_version, $update_object->beta_version, '>' ) ) {
return $update_object;
}
$update_object->is_beta = true;
// Store stable version data in case it's needed later.
$update_object->stable_version = $update_object->new_version;
$update_object->stable_package = $update_object->package;
// Override stable version data with beta.
$update_object->new_version = $update_object->beta_version;
$update_object->package = $update_object->beta_package;
// Add notice that's shown on the Updates page.
$update_object->upgrade_notice = self::NOTICE_START_MARKER . ' ' . __( 'This update will install a beta version of Rank Math SEO PRO.', 'rank-math-pro' ) . ' ' . self::NOTICE_END_MARKER;
return $update_object;
}
/**
* Show beta update message on the Plugins screen.
*
* @param array $plugin_data An array of plugin metadata.
* @param object $response An array of metadata about the available plugin update.
*/
public function beta_update_message( $plugin_data, $response ) {
if ( empty( $plugin_data['is_beta'] ) ) {
return;
}
printf(
'</p><p class="rank-math-beta-update-notice">%s',
esc_html__( 'This update will install a beta version of Rank Math SEO PRO.', 'rank-math-pro' )
);
$this->action( 'admin_footer', 'print_beta_notice_css' );
}
/**
* Print CSS related to the beta notice on the Plugins screen.
*
* @return void
*/
public function print_beta_notice_css() {
?>
<style>
.update-message .rank-math-beta-update-notice {
font-weight: bold;
margin-top: 20px;
}
.update-message p.rank-math-beta-update-notice:before {
content: "\f534";
}
</style>
<?php
}
/**
* Check if the transient already contains newer version of the plugin.
*
* @param object $transient Site transient: 'update_plugins'.
* @param string $new_version New version we check against, e.g. '1.2'.
* @return boolean
*/
public function has_newer_version( $transient, $new_version ) {
$plugin = plugin_basename( RANK_MATH_PRO_FILE );
return isset( $transient->response[ $plugin ] )
&& is_object( $transient->response[ $plugin ] )
&& isset( $transient->response[ $plugin ]->new_version )
&& version_compare( $transient->response[ $plugin ]->new_version, $new_version, '>' );
}
/**
* Filter plugin information.
*
* @param false|object|array $result The result object or array. Default false.
* @param string $action The type of information being requested from the Plugin Installation API.
* @param object $args Plugin API arguments.
* @return false|object false or Response object.
*/
public function filter_info( $result, $action, $args ) {
if ( ! isset( $args->slug ) || ! ( $this->slug === $args->slug && 'plugin_information' === $action ) ) {
return $result;
}
$information = $this->get_plugin_info();
if ( $this->has_beta_update( $information ) ) {
$information->sections['changelog'] = $information->sections['beta_changelog'];
}
unset( $information->sections['beta_changelog'] );
return $information;
}
/**
* Add JS in plugin-info iframe, to disable the Update button if necessary.
*
* @return void
*/
public function iframe_footer_scripts() {
if ( Param::get( 'plugin' ) !== 'seo-by-rank-math-pro' ) {
return;
}
$transient = get_site_transient( 'update_plugins' );
$response = isset( $transient->response['seo-by-rank-math-pro/rank-math-pro.php'] ) ? $transient->response['seo-by-rank-math-pro/rank-math-pro.php'] : new \stdClass();
if ( empty( $response->package ) && isset( $response->unavailability_reason ) ) {
$message = $this->get_update_message( $response->unavailability_reason );
?>
<script type="text/javascript">jQuery( function() {
var $button = jQuery( '#plugin_update_from_iframe' );
if ( $button.data( 'plugin' ) !== 'seo-by-rank-math-pro/rank-math-pro.php' ) {
return;
}
$button.css( 'pointer-events', 'none' ).addClass( 'disabled' ).text( '<?php echo esc_js( __( 'Cannot Update', 'rank-math-pro' ) ); ?>' );
jQuery( '#section-holder' ).prepend( '<div class="notice notice-error notice-alt"><p><strong><?php echo esc_js( $message ); ?></strong></p></div>' );
} );</script>
<?php
}
}
/**
* Check if plugin update data & info object contain a valid beta update.
*
* @param object $plugin_info
* @return boolean
*/
private function has_beta_update( $plugin_info ) {
$beta_optin_enabled = Helper::get_settings( 'general.beta_optin' );
$plugin = plugin_basename( RANK_MATH_PRO_FILE );
$transient = get_site_transient( 'update_plugins' );
return (
$beta_optin_enabled
&& ! empty( $transient->response[ $plugin ]->beta_version )
&& ! empty( $plugin_info->sections['beta_changelog'] )
&& version_compare( $transient->response[ $plugin ]->beta_version, ! empty( $transient->response[ $plugin ]->stable_version ) ? $transient->response[ $plugin ]->stable_version : $transient->response[ $plugin ]->new_version, '>=' )
);
}
/**
* Check if version check endpoint's response contains new beta verison.
*
* @param array $data Latest versions.
* @return boolean
*/
private function new_beta_version_available( $data ) {
return (
Helper::get_settings( 'general.beta_optin' )
&& version_compare( $data['beta_version'], $data['new_version'], '>' )
&& version_compare( $data['beta_version'], RANK_MATH_PRO_VERSION, '>' )
);
}
/**
* Merge default plugin data with fetched data.
*
* @param bool $force_check Disregard cached data & always re-fetch.
* @param string $locale Requested locale. Optional. Default: site locale.
*
* @return object|bool If there is an update, returns the license information.
* Otherwise returns false.
*/
private function get_plugin_info( $force_check = false, $locale = '' ) {
$information = $this->get_default_plugin_info();
$fetched = $this->fetch_plugin_info( $force_check, $locale );
if ( is_object( $fetched ) ) {
$information = (object) array_merge( (array) $information, (array) $fetched );
}
return $information;
}
/**
* Get default plugin info.
*
* @return object
*/
private function get_default_plugin_info() {
$description = '<p><strong>' . __( 'Rank Math SEO PRO For WordPress', 'rank-math-pro' ) . '</strong><br />';
$description .= '★★★★★</p>';
$description .= '<p><strong>' . __( 'SEO is the most consistent source of traffic for any website', 'rank-math-pro' ) . '.</strong> ';
// Translators: placeholders are the anchor tag opening and closing.
$description .= sprintf( __( 'We created %1$sRank Math, a WordPress SEO plugin%2$s, to help every website owner get access to the SEO tools they need to improve their SEO and attract more traffic to their website.', 'rank-math-pro' ), '<a href="' . KB::get( 'logo', 'PRO Update Popup Description Tab' ) . '" rel="nofollow ugc"><strong>', '</strong></a>' ) . '</p>';
$plugin_info = [
'external' => true,
'name' => 'Rank Math SEO PRO',
'slug' => $this->slug,
'author' => '<a href="' . KB::get( 'seo-suite', 'PRO Update Popup Author Link' ) . '">Rank Math</a>',
'homepage' => KB::get( 'seo-suite', 'PRO Update Popup Homepage Link' ),
'banners' => [
'low' => 'https://ps.w.org/seo-by-rank-math/assets/banner-772x250.png',
'high' => 'https://ps.w.org/seo-by-rank-math/assets/banner-1544x500.png',
],
'sections' => [
'description' => $description,
],
'version' => RANK_MATH_PRO_VERSION,
];
return (object) $plugin_info;
}
/**
* Get default plugin info if we can't get anything from the API.
*
* @return object
*/
private function get_default_update_data() {
$plugin = plugin_basename( RANK_MATH_PRO_FILE );
$update = [
'slug' => $this->slug,
'plugin' => $plugin,
'url' => KB::get( 'seo-suite', 'PRO Update Popup Update Home Link' ),
'icons' => [
'svg' => 'https://ps.w.org/seo-by-rank-math/assets/icon.svg?rev=2348086',
'1x' => 'https://ps.w.org/seo-by-rank-math/assets/icon-128x128.png',
'2x' => 'https://ps.w.org/seo-by-rank-math/assets/icon-256x256.png',
],
'new_version' => '',
'package' => '',
];
return (object) $update;
}
/**
* Checks the license manager to see if there is an update available for this product.
*
* @param bool $force_check Disregard cached data & always re-fetch.
* @return object|bool If there is an update, returns the license information.
* Otherwise returns false.
*/
public function fetch_latest_version( $force_check = false ) {
$stored_versions = get_site_transient( 'rank_math_pro_versions' );
if ( empty( $stored_versions ) || $force_check ) {
// Check the latest versions.
$versions_response = wp_remote_get(
$this->api_url . '/versionCheck/',
[
'timeout' => defined( 'DOING_CRON' ) && DOING_CRON ? 30 : 10,
'body' => [
'product_slug' => $this->slug,
],
]
);
if ( is_wp_error( $versions_response ) ) {
return false;
}
$versions_response_body = wp_remote_retrieve_body( $versions_response );
$versions_result = json_decode( $versions_response_body, true );
if ( ! is_array( $versions_result ) || ! isset( $versions_result['new_version'] ) ) {
return false;
}
$stored_versions = $versions_result;
set_site_transient( 'rank_math_pro_versions', $versions_result, 3600 * 3 );
update_site_option( 'rank_math_pro_google_updates', $versions_result['g_updates'] );
}
$stored = get_site_transient( 'rank_math_pro_updates' );
if (
! $force_check
&& ! empty( $stored )
&& version_compare( $stored_versions['new_version'], RANK_MATH_PRO_VERSION, '<=' )
&& ! $this->new_beta_version_available( $stored_versions )
) {
return $stored;
}
if ( ! empty( $this->requested['version'] ) ) {
return ( false === $stored ? [] : $stored );
}
$params = [
'site_url' => is_multisite() ? network_site_url() : home_url(),
'product_slug' => $this->slug,
'new_api' => 2,
'v' => RANK_MATH_PRO_VERSION,
];
$this->maybe_add_auth_params( $params );
if ( ! isset( $params['username'] ) || Admin_Helper::is_plan_expired() ) {
return false;
}
$response = wp_remote_post(
add_query_arg( 'v', RANK_MATH_PRO_VERSION, $this->api_url . '/updateCheck2/' ),
[
'timeout' => defined( 'DOING_CRON' ) && DOING_CRON ? 30 : 10,
'body' => $params,
]
);
$this->requested['version'] = true;
if ( is_wp_error( $response ) ) {
return false;
}
$response_body = wp_remote_retrieve_body( $response );
$result = json_decode( $response_body, true );
if ( ! is_array( $result ) || ! isset( $result['new_version'] ) ) {
return false;
}
set_site_transient( 'rank_math_pro_updates', $result, 3600 * 12 );
return $result;
}
/**
* Checks the license manager to see if there is an update available for this product.
*
* @param bool $force_check Disregard cached data & always re-fetch.
* @param string $locale Requested locale. Optional. Default: site locale.
* @return object|bool If there is an update, returns the license information.
* Otherwise returns false.
*/
public function fetch_plugin_info( $force_check = false, $locale = '' ) {
if ( ! $locale ) {
$locale = get_locale();
}
$stored = get_site_transient( 'rank_math_pro_info_' . $locale );
if ( ! $force_check && ! empty( $stored ) ) {
return $stored;
}
if ( ! empty( $this->requested['info'] ) ) {
return ( false === $stored ? (object) [] : $stored );
}
$params = [
'product_slug' => $this->slug,
'locale' => $locale,
'site_url' => is_multisite() ? network_site_url() : home_url(),
];
$this->maybe_add_auth_params( $params );
// Send the request.
$response = wp_remote_post(
$this->api_url . '/pluginInfo/',
[
'timeout' => defined( 'DOING_CRON' ) && DOING_CRON ? 30 : 10,
'body' => $params,
]
);
$this->requested['info'] = true;
if ( is_wp_error( $response ) ) {
return false;
}
$response_body = wp_remote_retrieve_body( $response );
// We do assoc=true and then cast to object to keep sub-items as array.
$result = (object) json_decode( $response_body, true );
if ( ! is_object( $result ) ) {
return false;
}
set_site_transient( 'rank_math_pro_info_' . $locale, $result, 3600 * 3 );
return $result;
}
/**
* Add username & api key if the site is connected.
*
* @param array $params Params passed by reference.
* @return void
*/
private function maybe_add_auth_params( &$params ) {
$registered = Admin_Helper::get_registration_data();
if ( $registered && isset( $registered['username'] ) && isset( $registered['api_key'] ) ) {
$params['username'] = $registered['username'];
$params['api_key'] = $registered['api_key'];
}
}
/**
* Add additional text to notice if download is not available and account is connected.
*
* @param array $plugin_data An array of plugin metadata.
* @param object $response An array of metadata about the available plugin update.
* @return void
*/
public function in_plugin_update_message( $plugin_data, $response ) {
if ( current_user_can( 'update_plugins' ) ) {
if ( empty( $response->package ) && isset( $response->unavailability_reason ) ) {
$message = $this->get_update_message( $response->unavailability_reason );
echo ' <strong>' . wp_kses_post( $message ) . '</strong>';
}
}
}
/**
* Get unavailability reason message.
*
* @param string $reason Unavailability reason ID, like 'not_connected'.
* @param mixed $default Default text to return when specified ID has no message attached to it.
* @return string
*/
public function get_update_message( $reason = '', $default = null ) {
if ( is_null( $default ) ) {
$default = '';
}
$unavailability_reasons = [
'update_free' => __( 'Please update the free version before updating Rank Math SEO PRO.', 'rank-math-pro' ),
'not_subscribed' => sprintf(
/* translators: 1: Plugin name, 2: Pricing Link's opening HTML anchor tag, 3: Pricing Link's closing HTML anchor tag. */
__( 'It seems that you don\'t have an active subscription for %1$s. Please see %2$sdetails and pricing%3$s.', 'rank-math-pro' ),
'Rank Math SEO PRO',
'<a href="' . KB::get( 'pro', 'PRO Update Popup Upgrade Notice' ) . '">',
'</a>'
),
'not_connected' => sprintf(
/* translators: 1: Link's opening HTML anchor tag, 2: Link's closing HTML anchor tag. */
__( 'Please %1$s connect Rank Math SEO PRO %2$s for automatic updates.', 'rank-math-pro' ),
'<a href="' . Helper::get_connect_url() . '">',
'</a>'
),
];
$unavailability_reasons = $this->do_filter( 'updates/unavailability_reasons', $unavailability_reasons );
if ( isset( $unavailability_reasons[ $reason ] ) ) {
return $unavailability_reasons[ $reason ];
}
return $default;
}
/**
* Toggle auto updates option.
* Hooked to run when the option is changed in the free version.
*
* @param string $toggle New status.
* @return void
*/
public function toggle_auto_update( $toggle ) {
$auto_updates = (array) get_site_option( 'auto_update_plugins', [] );
if ( ! empty( $toggle ) && 'off' !== $toggle ) {
$auto_updates[] = 'seo-by-rank-math-pro/rank-math-pro.php';
update_site_option( 'auto_update_plugins', array_unique( $auto_updates ) );
return;
}
update_site_option( 'auto_update_plugins', array_diff( $auto_updates, [ 'seo-by-rank-math-pro/rank-math-pro.php' ] ) );
}
/**
* Don't auto-update if it's a beta version.
*
* @param bool $update Whether to update the plugin or not.
* @param array $item The update plugin object.
*
* @return bool
*/
public function auto_update_plugin( $update, $item ) {
// Show auto-updates control on Plugins page.
if ( did_action( 'load-plugins.php' ) ) {
return $update;
}
if ( $this->is_rm_pro_update( $item ) && $this->is_beta_update( $item ) ) {
return false;
}
return $update;
}
/**
* Check if updatable object is RM.
*
* @param object $item Updatable object.
* @return boolean
*/
public function is_rm_pro_update( $item ) {
return isset( $item->slug ) &&
'seo-by-rank-math-pro' === $item->slug &&
isset( $item->new_version );
}
/**
* Check if given version is beta.
*
* @param object $item Update data.
* @return boolean
*/
public function is_beta_update( $item ) {
return ( is_object( $item ) && isset( $item->new_version ) && false !== stripos( $item->new_version, 'beta' ) );
}
/**
* Hide "update scheduled in X hours" message if update is a beta version because we don't auto-update those.
*
* @param string $html HTML string.
* @param string $plugin_file Plugin file relative to the plugin directory.
* @param array $plugin_data Plugin update data.
* @return string
*/
public function plugin_auto_update_setting_html( $html, $plugin_file, $plugin_data ) {
if ( 'seo-by-rank-math-pro/rank-math-pro.php' !== $plugin_file ) {
return $html;
}
if ( ! empty( $plugin_data['is_beta'] ) ) {
$html = str_replace( 'class="auto-update-time"', 'class="auto-update-time hidden"', $html );
}
return $html;
}
/**
* Set should_send to true if Pro update is available, to send the notification email.
* Notification feature is only available if auto-updates are disabled.
*
* @param boolean $should_send Original should_send value.
* @param mixed $transient update_plugins site ransient value.
* @return boolean
*/
public function should_send_update_notification( $should_send, $transient ) {
if ( $this->get_auto_update_setting() ) {
return $should_send;
}
if ( ! $this->is_update_available( $transient ) ) {
return $should_send;
}
$new_version = $transient->response['seo-by-rank-math-pro/rank-math-pro.php']->new_version;
// Now let's check if we've already sent this email.
$sent = get_option( 'rank_math_update_notifications_sent', [ 'pro' => [ 'new_version' => '1.0' ] ] );
if ( ! isset( $sent['pro'] ) ) {
$sent['pro'] = [ 'new_version' => '1.0' ];
}
if ( version_compare( $sent['pro']['new_version'], $new_version, '>=' ) ) {
return $should_send;
}
return true;
}
/**
* Add Pro to notification product list if an update is available.
*
* @param array $products Products data array.
* @param mixed $transient Transient value.
* @return array
*/
public function update_notification_products( $products, $transient ) {
if ( $this->get_auto_update_setting() ) {
return $products;
}
if ( ! $this->is_update_available( $transient ) ) {
return $products;
}
$products['pro'] = [
'name' => __( 'Rank Math PRO', 'rank-math-pro' ),
'old_version' => RANK_MATH_PRO_VERSION,
'new_version' => $transient->response['seo-by-rank-math-pro/rank-math-pro.php']->new_version,
'changelog' => KB::get( 'changelog', 'PRO Update Popup Chagelog Link' ),
];
return $products;
}
/**
* Check if update is available for the PRO version.
*
* @param mixed $transient The update_plugins transient value.
* @return boolean
*/
public function is_update_available( $transient ) {
if ( ! is_object( $transient )
|| empty( $transient->response )
|| empty( $transient->response['seo-by-rank-math-pro/rank-math-pro.php'] )
|| empty( $transient->response['seo-by-rank-math-pro/rank-math-pro.php']->new_version )
) {
return false;
}
return true;
}
/**
* Get Auto update setting status.
*
* @return bool
*/
public function get_auto_update_setting() {
return in_array( 'seo-by-rank-math-pro/rank-math-pro.php', (array) get_site_option( 'auto_update_plugins', [] ), true );
}
/**
* Connect auto-update toggles: if we enable it for the Pro, then Free should be enabled too,
* and if we disable it for Free then it should be disabled for the Pro too.
*
* @param string $option Option name.
* @param mixed $value Option value.
* @param mixed $old_value Previous option value before the change.
* @param int $network_id Network ID.
* @return void
*/
public function connect_auto_update_toggles( $option, $value, $old_value, $network_id ) {
$this->remove_action( 'update_site_option_auto_update_plugins', 'connect_auto_update_toggles', 20 );
if ( ! is_array( $value ) ) {
return;
}
$free_file = 'seo-by-rank-math/rank-math.php';
$pro_file = 'seo-by-rank-math-pro/rank-math-pro.php';
// If we just enabled it for Rank Math SEO Pro.
if ( in_array( $pro_file, $value, true ) && ! in_array( $pro_file, $old_value, true ) && ! in_array( $free_file, $value, true ) ) {
$value[] = $free_file;
update_site_option( $option, $value );
return;
}
// If we just disabled it for Rank Math SEO Free.
if ( ! in_array( $free_file, $value, true ) && in_array( $free_file, $old_value, true ) && in_array( $pro_file, $value, true ) ) {
$value = array_diff( $value, [ $pro_file ] );
update_site_option( $option, $value );
return;
}
}
}