plugin_file = $plugin_file; $this->plugin_slug = plugin_basename( $plugin_file ); $headers = get_file_data( $plugin_file, array( 'Version' => 'Version' ) ); $this->current_version = ! empty( $headers['Version'] ) ? $headers['Version'] : '0.0.0'; } /** * Register WordPress hooks. * * @return void */ public function register() { add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'inject_update' ) ); add_filter( 'plugins_api', array( $this, 'plugin_info' ), 20, 3 ); add_filter( 'upgrader_process_complete', array( $this, 'purge_transient' ), 10, 2 ); } /** * Inject update data into WordPress's update_plugins transient. * * @param object $transient The update_plugins transient value. * @return object */ public function inject_update( $transient ) { if ( empty( $transient->checked ) ) { return $transient; } $release = $this->fetch_latest_release(); if ( ! $release ) { return $transient; } $remote_version = ltrim( $release['tag_name'], 'v' ); if ( version_compare( $this->current_version, $remote_version, '>=' ) ) { return $transient; } $download_url = $this->get_download_url( $release ); if ( ! $download_url ) { return $transient; } $transient->response[ $this->plugin_slug ] = (object) array( 'slug' => dirname( $this->plugin_slug ), 'plugin' => $this->plugin_slug, 'new_version' => $remote_version, 'url' => 'https://github.com/' . self::GITHUB_USER . '/' . self::GITHUB_REPO, 'package' => $download_url, ); return $transient; } /** * Supply plugin info for the "View version details" modal in the admin. * * @param false|object|array $result The result object or array, or false. * @param string $action The type of info being requested. * @param object $args Plugin API arguments. * @return false|object */ public function plugin_info( $result, $action, $args ) { if ( 'plugin_information' !== $action ) { return $result; } if ( ! isset( $args->slug ) || dirname( $this->plugin_slug ) !== $args->slug ) { return $result; } $release = $this->fetch_latest_release(); if ( ! $release ) { return $result; } $remote_version = ltrim( $release['tag_name'], 'v' ); $download_url = $this->get_download_url( $release ); $changelog = ! empty( $release['body'] ) ? nl2br( esc_html( $release['body'] ) ) : ''; return (object) array( 'name' => 'Logo Soup', 'slug' => dirname( $this->plugin_slug ), 'version' => $remote_version, 'author' => 'Vincent Design Inc', 'homepage' => 'https://github.com/' . self::GITHUB_USER . '/' . self::GITHUB_REPO, 'download_link' => $download_url, 'trunk' => $download_url, 'requires' => '6.1', 'requires_php' => '7.4', 'sections' => array( 'description' => esc_html__( 'Build balanced logo grids and marquees directly inside the WordPress block editor.', 'logo-soup' ), 'changelog' => $changelog, ), ); } /** * Delete the cached release transient after a successful plugin update. * * @param \WP_Upgrader $upgrader Upgrader instance. * @param array $hook_extra Extra data about the upgrade. * @return void */ public function purge_transient( $upgrader, $hook_extra ) { if ( ! empty( $hook_extra['action'] ) && 'update' === $hook_extra['action'] && ! empty( $hook_extra['type'] ) && 'plugin' === $hook_extra['type'] && ! empty( $hook_extra['plugins'] ) && in_array( $this->plugin_slug, (array) $hook_extra['plugins'], true ) ) { delete_transient( self::TRANSIENT ); } } /** * Fetch the latest GitHub release, with a 12-hour transient cache. * * @return array|null Decoded release data or null on failure. */ private function fetch_latest_release() { $cached = get_transient( self::TRANSIENT ); if ( is_array( $cached ) ) { return $cached; } $url = sprintf( 'https://api.github.com/repos/%s/%s/releases/latest', self::GITHUB_USER, self::GITHUB_REPO ); $response = wp_remote_get( $url, array( 'timeout' => 10, 'user-agent' => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ), 'headers' => array( 'Accept' => 'application/vnd.github+json', ), ) ); if ( is_wp_error( $response ) ) { return null; } if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { return null; } $data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $data ) || empty( $data['tag_name'] ) ) { return null; } set_transient( self::TRANSIENT, $data, self::TRANSIENT_TTL ); return $data; } /** * Extract the zip download URL from a release. * * Prefers an attached release asset (.zip); falls back to the auto-generated * GitHub source zipball. * * @param array $release Decoded GitHub release data. * @return string|null */ private function get_download_url( array $release ) { if ( ! empty( $release['assets'] ) && is_array( $release['assets'] ) ) { foreach ( $release['assets'] as $asset ) { if ( isset( $asset['browser_download_url'] ) && '.zip' === substr( $asset['browser_download_url'], -4 ) ) { return $asset['browser_download_url']; } } } return isset( $release['zipball_url'] ) ? $release['zipball_url'] : null; } }