196 lines
6.5 KiB
PHP
196 lines
6.5 KiB
PHP
<?php
|
|
// If this file is called directly, abort.
|
|
if ( ! defined( 'WPINC' ) ) {
|
|
die;
|
|
}
|
|
|
|
/**
|
|
* GitHub adapter for the Repository_Provider interface.
|
|
*
|
|
* Preserves the GitHub URL/header/asset behavior from the original
|
|
* helper-functions.php verbatim.
|
|
*
|
|
* @since 1.1.0
|
|
*/
|
|
class GitHub_Provider implements Repository_Provider {
|
|
|
|
private string $repo_url;
|
|
private string $api_token;
|
|
|
|
public function __construct( string $repo_url, string $api_token = '' ) {
|
|
$this->repo_url = rtrim( $repo_url, '/' );
|
|
$this->api_token = $api_token;
|
|
}
|
|
|
|
public function get_id(): string {
|
|
return 'github';
|
|
}
|
|
|
|
public function get_label(): string {
|
|
return 'GitHub';
|
|
}
|
|
|
|
public function get_repo_data(): ?array {
|
|
if ( empty( $this->repo_url ) ) {
|
|
error_log( 'Projects Portfolio: GitHub URL is empty.' );
|
|
return null;
|
|
}
|
|
|
|
$api_url = str_replace( 'https://github.com/', 'https://api.github.com/repos/', $this->repo_url );
|
|
|
|
$cache_key = 'projects_portfolio_provider_data_' . md5( $api_url );
|
|
$cached_data = get_transient( $cache_key );
|
|
if ( $cached_data ) {
|
|
return $cached_data;
|
|
}
|
|
|
|
$headers = [ 'Accept' => 'application/vnd.github.v3+json' ];
|
|
if ( ! empty( $this->api_token ) ) {
|
|
$headers['Authorization'] = 'token ' . $this->api_token;
|
|
} else {
|
|
error_log( 'Projects Portfolio: GitHub API token is missing. Using unauthenticated requests.' );
|
|
}
|
|
|
|
$response = wp_remote_get( $api_url, [ 'headers' => $headers ] );
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
error_log( 'Projects Portfolio: GitHub API error: ' . $response->get_error_message() );
|
|
return null;
|
|
}
|
|
|
|
$code = wp_remote_retrieve_response_code( $response );
|
|
if ( 403 === $code ) {
|
|
$remaining = wp_remote_retrieve_header( $response, 'x-ratelimit-remaining' );
|
|
$reset = wp_remote_retrieve_header( $response, 'x-ratelimit-reset' );
|
|
error_log( 'Projects Portfolio: GitHub API 403: Rate limit exceeded. Remaining: ' . $remaining . ' Reset at: ' . date( 'Y-m-d H:i:s', (int) $reset ) );
|
|
return null;
|
|
}
|
|
if ( 200 !== $code ) {
|
|
error_log( 'Projects Portfolio: GitHub API error: Received status ' . $code );
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode( wp_remote_retrieve_body( $response ), true );
|
|
if ( empty( $data ) || ! is_array( $data ) ) {
|
|
error_log( 'Projects Portfolio: GitHub API error: Invalid data received.' );
|
|
return null;
|
|
}
|
|
|
|
set_transient( $cache_key, $data, HOUR_IN_SECONDS );
|
|
return $data;
|
|
}
|
|
|
|
public function get_release_url(): ?string {
|
|
$api_url = $this->release_api_url();
|
|
if ( null === $api_url ) {
|
|
return null;
|
|
}
|
|
|
|
$cache_key = 'projects_portfolio_provider_release_' . md5( $api_url );
|
|
$cached = get_transient( $cache_key );
|
|
if ( $cached ) {
|
|
return $cached;
|
|
}
|
|
|
|
$headers = [ 'Accept' => 'application/vnd.github.v3+json' ];
|
|
if ( ! empty( $this->api_token ) ) {
|
|
$headers['Authorization'] = 'token ' . $this->api_token;
|
|
}
|
|
|
|
$response = wp_remote_get( $api_url, [ 'headers' => $headers ] );
|
|
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode( wp_remote_retrieve_body( $response ), true );
|
|
if ( ! is_array( $data ) ) {
|
|
return null;
|
|
}
|
|
|
|
if ( ! empty( $data['assets'] ) && is_array( $data['assets'] ) ) {
|
|
foreach ( $data['assets'] as $asset ) {
|
|
if ( isset( $asset['name'] ) && 'zip' === pathinfo( $asset['name'], PATHINFO_EXTENSION ) ) {
|
|
$url = $asset['browser_download_url'] ?? null;
|
|
if ( $url ) {
|
|
set_transient( $cache_key, $url, HOUR_IN_SECONDS );
|
|
return $url;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$zipball = $data['zipball_url'] ?? null;
|
|
if ( $zipball ) {
|
|
set_transient( $cache_key, $zipball, HOUR_IN_SECONDS );
|
|
}
|
|
return $zipball;
|
|
}
|
|
|
|
public function get_latest_version(): string {
|
|
$api_url = $this->release_api_url();
|
|
if ( null === $api_url ) {
|
|
return 'Unknown';
|
|
}
|
|
|
|
$cache_key = 'projects_portfolio_provider_release_version_' . md5( $api_url );
|
|
$cached = get_transient( $cache_key );
|
|
if ( $cached ) {
|
|
return $cached;
|
|
}
|
|
|
|
$response = wp_remote_get( $api_url, [] );
|
|
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
|
return 'Unknown';
|
|
}
|
|
|
|
$data = json_decode( wp_remote_retrieve_body( $response ), true );
|
|
$tag = is_array( $data ) && isset( $data['tag_name'] ) ? (string) $data['tag_name'] : 'Unknown';
|
|
|
|
set_transient( $cache_key, $tag, HOUR_IN_SECONDS );
|
|
return $tag;
|
|
}
|
|
|
|
public function get_repo_browse_url(): string {
|
|
return $this->repo_url;
|
|
}
|
|
|
|
public function get_owner_data( string $owner_login ): ?array {
|
|
if ( empty( $owner_login ) ) {
|
|
return null;
|
|
}
|
|
|
|
$api_url = 'https://api.github.com/users/' . $owner_login;
|
|
$ctx = stream_context_create( [
|
|
'http' => [
|
|
'method' => 'GET',
|
|
'header' => "User-Agent: WORDPRESS\r\n",
|
|
],
|
|
] );
|
|
$body = @file_get_contents( $api_url, false, $ctx );
|
|
|
|
if ( false === $body ) {
|
|
error_log( 'Projects Portfolio: Error fetching data from GitHub API.' );
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode( $body, true );
|
|
if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $data ) ) {
|
|
error_log( 'Projects Portfolio: Error decoding JSON data: ' . json_last_error_msg() );
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'avatar_url' => $data['avatar_url'] ?? '',
|
|
'login' => $data['login'] ?? $owner_login,
|
|
'html_url' => $data['html_url'] ?? ( 'https://github.com/' . $owner_login ),
|
|
];
|
|
}
|
|
|
|
private function release_api_url(): ?string {
|
|
if ( empty( $this->repo_url ) ) {
|
|
return null;
|
|
}
|
|
return str_replace( 'https://github.com/', 'https://api.github.com/repos/', $this->repo_url ) . '/releases/latest';
|
|
}
|
|
}
|