Refactor update check

This commit is contained in:
Tetrakern 2024-06-04 02:54:07 +02:00
parent 4bbd7b49ac
commit 5de377ad4f
5 changed files with 153 additions and 88 deletions

View File

@ -130,9 +130,13 @@ Fires after an user has been successfully created or logged-in via the OAuth 2.0
---
### `do_action( 'fictioneer_after_update' )`
### `do_action( 'fictioneer_after_update', $new_version, $previous_version )`
Fires after the theme has been updated, once per version change.
**Parameters:**
* $new_version (string) The new version tag (e.g. v5.19.1).
* $previous_version (string) The previous version tag (e.g. v5.19.0).
**Hooked Actions:**
* `fictioneer_purge_caches_after_update()` Purges selected caches and Transients. Priority 10.

View File

@ -1479,7 +1479,7 @@ define( 'CONSTANT_NAME', value );
| FICTIONEER_AJAX_LOGIN_TTL | integer | How long to cache AJAX authentications locally in _milliseconds_. Default `15000`.
| FICTIONEER_AJAX_POST_DEBOUNCE_RATE | integer | How long to debounce AJAX requests of the same type in _milliseconds_. Default `700`.
| FICTIONEER_AUTHOR_KEYWORD_SEARCH_LIMIT | integer | Maximum number of authors in the advanced search suggestions. Default `100`.
| FICTIONEER_UPDATE_CHECK_TIMEOUT | integer | Timeout between checks for theme updates in _seconds_. Default `3600`.
| FICTIONEER_UPDATE_CHECK_TIMEOUT | integer | Timeout between checks for theme updates in _seconds_. Default `43200`.
| FICTIONEER_API_STORYGRAPH_CACHE_TTL | integer | How long Storygraph responses are cached in _seconds_. Default `3600`.
| FICTIONEER_API_STORYGRAPH_STORIES_PER_PAGE | integer | How many items the Storygraph `/stories` endpoint returns. Default 10.
| FICTIONEER_MAX_CUSTOM_PAGES_PER_STORY | integer | Maximum number of story custom pages. Default `4`.

View File

@ -111,114 +111,130 @@ add_action( 'admin_enqueue_scripts', 'fictioneer_admin_scripts' );
// CHECK FOR UPDATES
// =============================================================================
if ( ! function_exists( 'fictioneer_check_for_updates' ) ) {
/**
* Check Github repository for a new release
*
* Makes a remote request to the Github API to check the latest release and,
* if a newer version tag than what is installed is returned, updates the
* 'fictioneer_latest_version' option. Only one request is made every 12 hours
* unless you are on the updates page.
*
* @since 5.0.0
* @since 5.7.5 - Refactored.
*
* @return boolean True if there is a newer version, false if not.
*/
/**
* Check Github repository for a new release
*
* @since 5.0.0
* @since 5.7.5 - Refactored.
* @since 5.19.1 - Refactored again.
*
* @return boolean True if there is a newer version, false if not.
*/
function fictioneer_check_for_updates() {
global $pagenow;
function fictioneer_check_for_updates() {
global $pagenow;
// Setup
$last_check = (int) get_option( 'fictioneer_update_check_timestamp', 0 );
$latest_version = get_option( 'fictioneer_latest_version', FICTIONEER_RELEASE_TAG );
$is_updates_page = $pagenow === 'update-core.php';
// Setup
$theme_info = fictioneer_get_theme_info();
$last_check_timestamp = strtotime( $theme_info['last_update_check'] ?? 0 );
$remote_version = $theme_info['last_update_version'];
$is_updates_page = $pagenow === 'update-core.php';
// Only call API every n seconds, otherwise check database
if ( ! empty( $latest_version ) && time() < $last_check + FICTIONEER_UPDATE_CHECK_TIMEOUT && ! $is_updates_page ) {
return version_compare( $latest_version, FICTIONEER_RELEASE_TAG, '>' );
}
// Remember this check
update_option( 'fictioneer_update_check_timestamp', time() );
// Request to repository
$response = wp_remote_get(
'https://api.github.com/repos/Tetrakern/fictioneer/releases/latest',
array(
'headers' => array(
'User-Agent' => 'FICTIONEER',
'Accept' => 'application/vnd.github+json',
'X-GitHub-Api-Version' => '2022-11-28'
)
)
);
// Abort if request failed or is not a 2xx success status code
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) >= 300 ) {
return false;
}
// Decode JSON to array
$release = json_decode( wp_remote_retrieve_body( $response ), true );
$release_tag = sanitize_text_field( $release['tag_name'] ?? '' );
// Abort if request did not return expected data
if ( ! $release_tag ) {
return false;
}
// Remember latest version
update_option( 'fictioneer_latest_version', $release_tag );
// Compare with currently installed version
return version_compare( $release_tag, FICTIONEER_RELEASE_TAG, '>' );
// Only call API every n seconds, otherwise check database
if (
$remote_version &&
! $is_updates_page &&
current_time( 'timestamp', true ) < $last_check_timestamp + FICTIONEER_UPDATE_CHECK_TIMEOUT
) {
return version_compare( $remote_version, FICTIONEER_RELEASE_TAG, '>' );
}
// Remember this check
$theme_info['last_update_check'] = current_time( 'mysql', 1 );
// Request to repository
$response = wp_remote_get(
'https://api.github.com/repos/Tetrakern/fictioneer/releases/latest',
array(
'headers' => array(
'User-Agent' => 'FICTIONEER',
'Accept' => 'application/vnd.github+json',
'X-GitHub-Api-Version' => '2022-11-28'
)
)
);
// Abort if request failed or is not a 2xx success status code
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) >= 300 ) {
return false;
}
// Decode JSON to array
$release = json_decode( wp_remote_retrieve_body( $response ), true );
$release_tag = sanitize_text_field( $release['tag_name'] ?? '' );
// Abort if request did not return expected data
if ( ! $release_tag ) {
return false;
}
// Add to theme info
$theme_info['last_update_version'] = $release_tag;
$theme_info['last_update_notes'] = sanitize_textarea_field( $release['body'] ?? '' );
$theme_info['last_update_nag'] = ''; // Reset
if ( $release['assets'] ?? 0 ) {
$theme_info['last_version_download_url'] = sanitize_url( $release['assets'][0]['browser_download_url'] ?? '' );
} else {
$theme_info['last_version_download_url'] = '';
}
// Update info in database
update_option( 'fictioneer_theme_info', $theme_info );
// Compare with currently installed version
return version_compare( $release_tag, FICTIONEER_RELEASE_TAG, '>' );
}
/**
* Show notice when a newer version is available
*
* @since 5.0.0
* @since 5.19.1 - Refactored.
*/
function fictioneer_admin_update_notice() {
global $pagenow;
// Setup
$last_notice = (int) get_option( 'fictioneer_update_notice_timestamp', 0 );
$is_updates_page = $pagenow == 'update-core.php';
// Show only once every n seconds
if ( $last_notice + FICTIONEER_UPDATE_CHECK_TIMEOUT > time() && ! $is_updates_page ) {
// Guard
if ( ! current_user_can( 'install_themes' ) || ! fictioneer_check_for_updates() ) {
return;
}
// Update?
if ( ! fictioneer_check_for_updates() ) {
global $pagenow;
// Setup
$theme_info = fictioneer_get_theme_info();
$last_update_nag = strtotime( $theme_info['last_update_nag'] ?? 0 );
$is_updates_page = $pagenow == 'update-core.php';
// Show only once every n seconds
if ( ! $is_updates_page && current_time( 'timestamp', true ) < $last_update_nag + 60 ) {
return;
}
// Render notice
$notes = fictioneer_prepare_release_notes( $theme_info['last_update_notes'] ?? '' );
wp_admin_notice(
sprintf(
__( '<strong>Fictioneer %1$s</strong> is available. Please <a href="%2$s" target="_blank">download</a> and install the latest version at your next convenience.', 'fictioneer' ),
get_option( 'fictioneer_latest_version', FICTIONEER_RELEASE_TAG ),
'https://github.com/Tetrakern/fictioneer/releases'
__( '<strong>Fictioneer %1$s</strong> is available. Please <a href="%2$s" target="_blank">download</a> and install the latest version at your next convenience.%3$s', 'fictioneer' ),
$theme_info['last_update_version'],
'https://github.com/Tetrakern/fictioneer/releases',
$notes ? '<br><details><summary>' . __( 'Release Notes', 'fictioneer' ) . '</summary>' . $notes . '</details>' : ''
),
array(
'type' => 'warning',
'dismissible' => true
'dismissible' => true,
'additional_classes' => ['fictioneer-update-notice']
)
);
// Remember notice
update_option( 'fictioneer_update_notice_timestamp', time() );
}
$theme_info['last_update_nag'] = current_time( 'mysql', 1 );
if ( current_user_can( 'install_themes' ) ) {
add_action( 'admin_notices', 'fictioneer_admin_update_notice' );
// Update info in database
update_option( 'fictioneer_theme_info', $theme_info );
}
add_action( 'admin_notices', 'fictioneer_admin_update_notice' );
/**
* Extracts the release notes from the update message

View File

@ -13,10 +13,10 @@
function fictioneer_bring_out_legacy_trash() {
// Setup
$options = wp_cache_get( 'alloptions', 'options' );
$obsolete = ['fictioneer_disable_html_in_comments', 'fictioneer_block_subscribers_from_admin', 'fictioneer_admin_restrict_menus', 'fictioneer_admin_restrict_private_data', 'fictioneer_admin_reduce_subscriber_profile', 'fictioneer_enable_subscriber_self_delete', 'fictioneer_strip_shortcodes_for_non_administrators', 'fictioneer_restrict_media_access', 'fictioneer_subscription_enabled', 'fictioneer_patreon_badge_map', 'fictioneer_patreon_tier_as_badge', 'fictioneer_patreon_campaign_ids', 'fictioneer_patreon_campaign_id', 'fictioneer_mount_wpdiscuz_theme_styles', 'fictioneer_base_site_width', 'fictioneer_featherlight_enabled', 'fictioneer_tts_enabled', 'fictioneer_log', 'fictioneer_enable_ajax_nonce', 'fictioneer_flush_object_cache', 'fictioneer_enable_all_block_styles', 'fictioneer_light_mode_as_default', 'fictioneer_remove_wp_svg_filters'];
$obsolete = ['fictioneer_disable_html_in_comments', 'fictioneer_block_subscribers_from_admin', 'fictioneer_admin_restrict_menus', 'fictioneer_admin_restrict_private_data', 'fictioneer_admin_reduce_subscriber_profile', 'fictioneer_enable_subscriber_self_delete', 'fictioneer_strip_shortcodes_for_non_administrators', 'fictioneer_restrict_media_access', 'fictioneer_subscription_enabled', 'fictioneer_patreon_badge_map', 'fictioneer_patreon_tier_as_badge', 'fictioneer_patreon_campaign_ids', 'fictioneer_patreon_campaign_id', 'fictioneer_mount_wpdiscuz_theme_styles', 'fictioneer_base_site_width', 'fictioneer_featherlight_enabled', 'fictioneer_tts_enabled', 'fictioneer_log', 'fictioneer_enable_ajax_nonce', 'fictioneer_flush_object_cache', 'fictioneer_enable_all_block_styles', 'fictioneer_light_mode_as_default', 'fictioneer_remove_wp_svg_filters', 'fictioneer_update_check_timestamp', 'fictioneer_latest_version', 'fictioneer_update_notice_timestamp', 'fictioneer_theme_status'];
// Check for most recent obsolete option...
if ( isset( $options['fictioneer_remove_wp_svg_filters'] ) ) {
if ( isset( $options['fictioneer_theme_status'] ) ) {
// Looping everything is not great but it only happens once!
foreach ( $obsolete as $trash ) {
delete_option( $trash );
@ -120,19 +120,62 @@ function fictioneer_theme_setup() {
add_image_size( 'snippet', 0, 200 );
// After update actions
$theme_status = get_option( 'fictioneer_theme_status' );
$theme_info = fictioneer_get_theme_info();
if ( empty( $theme_status ) || ( $theme_status['version'] ?? 0 ) !== FICTIONEER_VERSION ) {
$theme_status = is_array( $theme_status ) ? $theme_status : [];
$theme_status['version'] = FICTIONEER_VERSION;
if ( ( $theme_info['version'] ?? 0 ) !== FICTIONEER_VERSION ) {
$previous_version = $theme_info['version'];
$theme_info['version'] = FICTIONEER_VERSION;
update_option( 'fictioneer_theme_status', $theme_status );
update_option( 'fictioneer_theme_info', $theme_info, 'yes' );
do_action( 'fictioneer_after_update' );
do_action( 'fictioneer_after_update', $theme_info['version'], $previous_version );
}
}
add_action( 'after_setup_theme', 'fictioneer_theme_setup' );
/**
* Get basic theme info
*
* @since 5.19.1
*
* @return array Associative array with theme info.
*/
function fictioneer_get_theme_info() {
// Setup
$info = get_option( 'fictioneer_theme_info' ) ?: [];
// Set up if missing
if ( ! $info || ! is_array( $info ) ) {
$info = array(
'last_update_check' => current_time( 'mysql', 1 ),
'last_update_version' => '',
'last_update_nag' => current_time( 'mysql', 1 ),
'last_update_notes' => '',
'last_version_download_url' => '',
'version' => FICTIONEER_VERSION
);
update_option( 'fictioneer_theme_info', $info, 'yes' );
}
// Merge with defaults (in case of incomplete data)
$info = array_merge(
array(
'last_update_check' => current_time( 'mysql', 1 ),
'last_update_version' => '',
'last_update_nag' => '',
'last_update_notes' => '',
'last_version_download_url' => '',
'version' => FICTIONEER_VERSION
),
$info
);
// Return info
return $info;
}
// =============================================================================
// AFTER UPDATE
// =============================================================================

View File

@ -26,9 +26,11 @@
<div class="fictioneer-card__row">
<p><?php
$theme_info = fictioneer_get_theme_info();
printf(
__( '<strong>Fictioneer %1$s</strong> is available. Please <a href="%2$s" target="_blank">download</a> and install the latest version at your next convenience.', 'fictioneer' ),
get_option( 'fictioneer_latest_version', FICTIONEER_RELEASE_TAG ),
$theme_info['last_update_version'],
'https://github.com/Tetrakern/fictioneer/releases'
);
?></p>