File: /var/www/ippmt.kauko.lt/wp-content/plugins/host-analyticsjs-local/includes/admin/class-updates.php
<?php
/**
* A (kind of) portable file, which allows me to add some extra handling to updates for premium "daughters"
* of freemium (mother) plugins.
*
* Basically, what this class does is make sure that, if automatic updates seem to be failing, the user is
* informed (in a non-intrusive manner, i.e. the Plugins screen) of the fact that an update is indeed
* available and where/how to download/install it manually.
*
* @package Daan/Updates
*/
class CAOS_Admin_Updates {
const TRANSIENT_LABEL_APPENDIX = '_addons_latest_available_versions';
/** @var string $plugin_text_domain */
private $plugin_text_domain = '';
/** @var string $plugin_text_domain */
private $transient_label = '';
/** @var array $premium_plugins */
private $premium_plugins = [];
/**
* Build hooks.
*
* @return void
*/
public function __construct( $premium_plugins, $plugin_text_domain, $transient_label ) {
$this->premium_plugins = $premium_plugins;
$this->plugin_text_domain = $plugin_text_domain;
$this->transient_label = $transient_label;
$this->init();
}
/**
* Action & Filter hooks.
*
* @return void
*/
private function init() {
add_filter( 'all_plugins', [ $this, 'maybe_display_premium_update_notice' ] );
add_filter( 'wp_get_update_data', [ $this, 'maybe_add_update_count' ], 10, 1 );
add_filter( 'site_transient_update_plugins', [ $this, 'maybe_add_to_update_list' ] );
add_filter( 'site_transient_update_plugins', [ $this, 'force_update' ], PHP_INT_MAX );
}
/**
* Addresses a possible bug introduced after @see https://core.trac.wordpress.org/ticket/61055 was introduced.
*
* Goes through the list of entered premium plugins this plugin is the parent of and removes them from the "checked" array, to force a check for updates.
*
* @param $transient
*
* @return mixed
*/
public function force_update( $transient ) {
foreach ( $this->premium_plugins as $plugin ) {
$basename = $plugin[ 'basename' ];
if ( is_object( $transient ) && isset( $transient->checked[ $basename ] ) ) {
unset( $transient->checked[ $basename ] );
}
}
return $transient;
}
/**
* This function checks if:
* - Premium plugin is installed,
* - And if so, if an update is already available for it.
* - And if not, if the current version is lower than the latest available version.
* - And if so, display a custom notice with instructions to download the update manually.
*
* @param mixed $installed_plugins
*
* @return mixed
*/
public function maybe_display_premium_update_notice( $installed_plugins ) {
$plugin_slugs = array_keys( $installed_plugins );
foreach ( $this->premium_plugins as $id => $premium_plugin ) {
if ( ! in_array( $premium_plugin[ 'basename' ], $plugin_slugs ) ) {
continue;
}
if ( $this->update_already_displayed( $premium_plugin[ 'basename' ] ) ) {
continue;
}
$latest_version = $this->get_latest_version( $id, $premium_plugin[ 'transient_label' ] );
$current_version = get_plugin_data( WP_PLUGIN_DIR . '/' . $premium_plugin[ 'basename' ] )[ 'Version' ] ?? '';
if ( version_compare( $current_version, $latest_version, '<' ) ) {
$installed_plugins[ $premium_plugin[ 'basename' ] ][ 'update' ] = true;
add_action( 'after_plugin_row_' . $premium_plugin[ 'basename' ], [ $this, 'display_premium_update_notice' ], 10, 2 );
}
}
return $installed_plugins;
}
/**
* Checks if there's already an update available for the premium plugin in the Plugins screen.
*
* @return bool
*/
private function update_already_displayed( $basename ) {
$available_updates = $this->get_available_updates();
if ( ! is_object( $available_updates ) || ! isset( $available_updates->response ) || ! is_array( $available_updates->response ) ) {
return false;
}
$plugin_slugs = array_keys( $available_updates->response );
return in_array( $basename, $plugin_slugs ) && ! empty( $available_updates->response[ $basename ]->new_version );
}
/**
* Fetch available updates from database.
*
* @return mixed
*/
private function get_available_updates() {
static $available_updates;
if ( $available_updates === null ) {
$available_updates = get_site_transient( 'update_plugins' );
}
return $available_updates;
}
/**
* Gets the latest available version of the current premium plugin.
*/
private function get_latest_version( $id ) {
static $latest_versions;
/**
* This prevents duplicate DB reads.
*/
if ( $latest_versions === null ) {
$latest_versions = get_transient( $this->transient_label . self::TRANSIENT_LABEL_APPENDIX );
}
/**
* If the transient doesn't exist, is expired or has no value, the return value will be false.
*/
if ( $latest_versions === false ) {
$latest_versions = [];
}
$latest_version = $latest_versions[ $id ] ?? '';
/**
* If $latest_versions is an empty string, that probably means something went wrong before. So,
* we should try and refresh it. If $latest_versions is false, then the transient doesn't exist.
*/
if ( $latest_version === '' ) {
$response = wp_remote_get( 'https://daan.dev/?edd_action=get_version&item_id=' . $id );
$latest_version = json_decode( wp_remote_retrieve_body( $response ) )->new_version ?? '';
/**
* Don't write transient if request failed for some reason.
*/
if ( empty( $latest_version ) ) {
return '';
}
$latest_versions[ $id ] = $latest_version;
set_transient( $this->transient_label . self::TRANSIENT_LABEL_APPENDIX, $latest_versions, DAY_IN_SECONDS );
}
return $latest_version;
}
/**
* Display a notice if current version of premium plugin is outdated, but updates can't be retrieved.
*
* @action after_plugin_row_{plugin_basename}
*
* @param mixed $file
* @param mixed $plugin_data
* @param mixed $status
*
* @return void
*/
public function display_premium_update_notice( $file, $plugin_data ) {
$slug = explode( '/', $file )[ 0 ] ?? '';
$label = $plugin_data[ 'Name' ] ?? $plugin_data[ 'name' ] ?? 'this plugin';
$notice = sprintf(
__(
'An update for %1$s is available, but we\'re having trouble retrieving it. Download it from <a href="%2$s" target="_blank">your account area</a> and install it manually. <a href="%3$s" target="_blank">Need help</a>?',
$this->plugin_text_domain
),
$label,
'https://daan.dev/account/files/',
'https://daan.dev/docs/pre-sales/download-files/'
);
/**
* This snippet of JS either overwrites the contents of the update message.
*/ ?>
<script>
window.addEventListener('DOMContentLoaded', function () {
var row = document.getElementById('<?php echo esc_attr( $slug ); ?>-update');
var div = '';
if (row !== null) {
div = row.getElementsByClassName('notice-warning');
}
if (div instanceof HTMLCollection && "0" in div) {
div[0].getElementsByTagName('p')[0].innerHTML = "<?php echo wp_kses( $notice, 'post' ); ?>";
}
})
</script>
<?php
}
/**
* This function check if:
* - Premium plugin is installed,
* - And if so, if an update is already fetched and displayed by WP itself.
* - And if so, if the currently installed version is lower than the latest available version,
* - And if so, adds 1 to the little update nag next to "Plugins" in the sidebar to attract
* attention to the fact that updates seem to be failing.
*
* @param mixed $update_data
* @param mixed $plugins
*
* @return mixed
*
* @throws InvalidArgument
*/
public function maybe_add_update_count( $update_data ) {
// phpcs:ignore
if ( isset( $_GET[ 'plugin_status' ] ) && $_GET[ 'plugin_status' ] === 'upgrade' ) {
return $update_data;
}
foreach ( $this->premium_plugins as $id => $plugin ) {
if ( ! is_plugin_active( WP_PLUGIN_DIR . '/' . $plugin[ 'basename' ] ) ) {
continue;
}
if ( $this->update_already_displayed( $plugin[ 'basename' ] ) ) {
continue;
}
$latest_version = $this->get_latest_version( $id, $plugin[ 'transient_label' ] );
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin[ 'basename' ] );
$current_version = $plugin_data[ 'Version' ] ?? '';
if ( version_compare( $current_version, $latest_version, '<' ) ) {
++ $update_data[ 'counts' ][ 'plugins' ];
}
}
return $update_data;
}
/**
* Run a few checks before adding the plugin to the list of updates.
*
* @param mixed $transient
*
* @return mixed
*/
public function maybe_add_to_update_list( $transient ) {
global $pagenow;
/**
* Don't do anything if we're on the Dashboard > Updates page.
*/
if ( $pagenow === 'update-core.php' ) {
return $transient;
}
if ( $transient === false ) {
return $transient;
}
foreach ( $this->premium_plugins as $id => $plugin ) {
if ( ! is_plugin_active( $plugin[ 'basename' ] ) ) {
continue;
}
$latest_version = $this->get_latest_version( $id, $plugin[ 'transient_label' ] );
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin[ 'basename' ] );
$current_version = $plugin_data[ 'Version' ] ?? '';
// Due to stored, non-expired transients, it might be possible that the latest version is lower than the current version.
if ( version_compare( $current_version, $latest_version, '>=' ) ) {
continue;
}
$plugin_file = $plugin[ 'basename' ];
// If an update is already displayed, there's no need for us to recreate this object.
if ( is_object( $transient ) && isset( $transient->response ) && ! isset( $transient->response[ $plugin_file ] ) ) {
$transient->response[ $plugin_file ] = (object) [
'slug' => explode( '/', $plugin_file )[ 0 ],
'plugin' => $plugin_file,
'new_version' => '',
];
}
}
return $transient;
}
}