//releases/latest endpoint * and return a normalised array, or null on irrecoverable error. * * Cached for 12h. Negative responses (404 = no releases yet) cached * for 1h so a freshly-tagged release shows up quickly. * * @param bool $force_refresh skip the transient cache. * @return array|null { version, html_url, download_url, body, published_at, error_code? } */ function wp_notes_fetch_latest_release( $force_refresh = false ) { $cache_key = 'wp_notes_gitea_latest'; if ( ! $force_refresh ) { $cached = get_site_transient( $cache_key ); if ( is_array( $cached ) ) { return $cached; } } $api_url = WP_NOTES_GITEA_HOST . '/api/v1/repos/' . WP_NOTES_GITEA_OWNER . '/' . WP_NOTES_GITEA_REPO . '/releases/latest'; $response = wp_remote_get( $api_url, array( 'timeout' => 8 ) ); if ( is_wp_error( $response ) ) { return null; } $code = (int) wp_remote_retrieve_response_code( $response ); // 404 = repo has no releases yet, OR private. Cache briefly and surface // a friendly status to the UI. if ( $code !== 200 ) { $info = array( 'version' => null, 'html_url' => wp_notes_gitea_releases_url(), 'download_url' => null, 'body' => '', 'published_at' => null, 'error_code' => $code, ); set_site_transient( $cache_key, $info, HOUR_IN_SECONDS ); return $info; } $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $body ) || empty( $body['tag_name'] ) ) { return null; } // "v3.3.0" → "3.3.0" so version_compare() against WP_NOTES_VERSION works cleanly. $version = ltrim( (string) $body['tag_name'], 'vV' ); // Prefer an explicit .zip asset attached to the release; fall back to // Gitea's source-archive zip URL pattern. $download_url = null; if ( ! empty( $body['assets'] ) && is_array( $body['assets'] ) ) { foreach ( $body['assets'] as $asset ) { if ( isset( $asset['name'], $asset['browser_download_url'] ) && substr( strtolower( $asset['name'] ), -4 ) === '.zip' ) { $download_url = $asset['browser_download_url']; break; } } } if ( ! $download_url ) { $download_url = wp_notes_gitea_repo_url() . '/archive/' . rawurlencode( $body['tag_name'] ) . '.zip'; } $info = array( 'version' => $version, 'html_url' => isset( $body['html_url'] ) ? esc_url_raw( $body['html_url'] ) : '', 'download_url' => esc_url_raw( $download_url ), 'body' => isset( $body['body'] ) ? wp_strip_all_tags( $body['body'] ) : '', 'published_at' => isset( $body['published_at'] ) ? $body['published_at'] : null, ); set_site_transient( $cache_key, $info, 12 * HOUR_IN_SECONDS ); return $info; } /** * Compare a fetched release against WP_NOTES_VERSION and return a * status payload suitable for the Settings UI. * * @return array { status: 'available'|'up-to-date'|'unknown', current, latest?, message, ... } */ function wp_notes_update_status( $force_refresh = false ) { $current = defined( 'WP_NOTES_VERSION' ) ? WP_NOTES_VERSION : '0.0.0'; $latest = wp_notes_fetch_latest_release( $force_refresh ); if ( ! $latest || empty( $latest['version'] ) ) { $msg = 'No releases tagged on the Gitea repo yet. Once a release is published, this check will start returning a version.'; if ( $latest && ! empty( $latest['error_code'] ) && (int) $latest['error_code'] !== 404 ) { $msg = 'Could not reach Gitea (HTTP ' . (int) $latest['error_code'] . '). Try again in a few minutes.'; } return array( 'status' => 'unknown', 'current' => $current, 'message' => $msg, 'repo_url' => wp_notes_gitea_repo_url(), ); } if ( version_compare( $latest['version'], $current, '>' ) ) { return array( 'status' => 'available', 'current' => $current, 'latest' => $latest['version'], 'html_url' => $latest['html_url'], 'download_url' => $latest['download_url'], 'published_at' => $latest['published_at'], 'body' => $latest['body'], 'message' => sprintf( 'A new version (v%s) is available — you are on v%s.', $latest['version'], $current ), ); } return array( 'status' => 'up-to-date', 'current' => $current, 'latest' => $latest['version'], 'message' => sprintf( 'You are up to date (v%s).', $current ), 'repo_url' => wp_notes_gitea_repo_url(), ); } /** * AJAX: force a fresh check, return the status payload. */ add_action( 'wp_ajax_wp_notes_check_updates', 'wp_notes_ajax_check_updates' ); function wp_notes_ajax_check_updates() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( 'Insufficient permissions.', 403 ); } check_ajax_referer( 'wp_notes_check_updates', 'nonce' ); delete_site_transient( 'wp_notes_gitea_latest' ); wp_send_json_success( wp_notes_update_status( true ) ); } /** * Render the "Updates" panel on the Settings page. Called from the * settings render function via wp_notes_settings_page(). */ function wp_notes_render_updates_panel() { $status = wp_notes_update_status( false ); // use the cached value on initial load $nonce = wp_create_nonce( 'wp_notes_check_updates' ); $repo_url = wp_notes_gitea_repo_url(); $rel_url = wp_notes_gitea_releases_url(); ?>

Updates

WP Logbook is self-hosted on Gitea. Click Check now to ask the repo whether there's a newer release than the one you're running.

Status:
Download v (.zip) View release notes →

View on Gitea View all releases

Manual update path: download the .zip, deactivate the plugin in WordPress, upload via Plugins → Add New → Upload, reactivate. Your notes are stored in wp_options and survive the upgrade.