457 lines
20 KiB
PHP
457 lines
20 KiB
PHP
<?php
|
|
|
|
class Swi_Foot_REST {
|
|
|
|
public function __construct() {
|
|
add_action('rest_api_init', array($this, 'register_routes'));
|
|
}
|
|
|
|
/**
|
|
* Normalize team data for consistent API responses
|
|
* @param array $team Raw team data from API
|
|
* @return array Normalized team object
|
|
*/
|
|
private function normalize_team($team) {
|
|
return array(
|
|
'teamId' => isset($team['teamId']) ? (string)$team['teamId'] : (isset($team['id']) ? (string)$team['id'] : ''),
|
|
'teamName' => isset($team['teamName']) ? $team['teamName'] : (isset($team['name']) ? $team['name'] : ''),
|
|
'teamLeagueName' => isset($team['teamLeagueName']) ? $team['teamLeagueName'] : (isset($team['league']) ? $team['league'] : null),
|
|
'icon' => isset($team['icon']) ? $team['icon'] : null,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Normalize match data for consistent API responses
|
|
* @param array $match Raw match data from API
|
|
* @return array Normalized match object
|
|
*/
|
|
private function normalize_match($match) {
|
|
// Extract team names from teams array if not present as direct fields
|
|
$teamNameA = '';
|
|
$teamNameB = '';
|
|
|
|
if (!empty($match['teams']) && is_array($match['teams'])) {
|
|
foreach ($match['teams'] as $team) {
|
|
if (!empty($team['isHomeTeam']) && !empty($team['teamName'])) {
|
|
$teamNameA = $team['teamName'];
|
|
} elseif (empty($team['isHomeTeam']) && !empty($team['teamName'])) {
|
|
$teamNameB = $team['teamName'];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use direct fields if available, fallback to extracted values, then fallback field variations
|
|
return array(
|
|
'matchId' => isset($match['matchId']) ? (string)$match['matchId'] : (isset($match['id']) ? (string)$match['id'] : ''),
|
|
'matchDate' => isset($match['matchDate']) ? $match['matchDate'] : (isset($match['date']) ? $match['date'] : null),
|
|
'teamAId' => isset($match['teamAId']) ? (string)$match['teamAId'] : (isset($match['homeTeamId']) ? (string)$match['homeTeamId'] : ''),
|
|
'teamBId' => isset($match['teamBId']) ? (string)$match['teamBId'] : (isset($match['awayTeamId']) ? (string)$match['awayTeamId'] : ''),
|
|
'teamNameA' => isset($match['teamNameA']) ? $match['teamNameA'] : ($teamNameA ?: (isset($match['homeTeamName']) ? $match['homeTeamName'] : '')),
|
|
'teamNameB' => isset($match['teamNameB']) ? $match['teamNameB'] : ($teamNameB ?: (isset($match['awayTeamName']) ? $match['awayTeamName'] : '')),
|
|
'scoreTeamA' => isset($match['scoreTeamA']) ? $match['scoreTeamA'] : (isset($match['homeScore']) ? $match['homeScore'] : null),
|
|
'scoreTeamB' => isset($match['scoreTeamB']) ? $match['scoreTeamB'] : (isset($match['awayScore']) ? $match['awayScore'] : null),
|
|
'matchStateName' => isset($match['matchStateName']) ? $match['matchStateName'] : (isset($match['status']) ? $match['status'] : null),
|
|
'stadiumFieldName' => isset($match['stadiumFieldName']) ? $match['stadiumFieldName'] : (isset($match['stadiumPlaygroundName']) ? $match['stadiumPlaygroundName'] : null),
|
|
'leagueName' => isset($match['leagueName']) ? $match['leagueName'] : null,
|
|
'divisionName' => isset($match['divisionName']) ? $match['divisionName'] : null,
|
|
'roundNbr' => isset($match['roundNbr']) ? $match['roundNbr'] : null,
|
|
'matchTypeName' => isset($match['matchTypeName']) ? $match['matchTypeName'] : null,
|
|
'hasMatchStarted' => isset($match['hasMatchStarted']) ? $match['hasMatchStarted'] : false,
|
|
'isMatchPause' => isset($match['isMatchPause']) ? $match['isMatchPause'] : false,
|
|
'hasMatchEnded' => isset($match['hasMatchEnded']) ? $match['hasMatchEnded'] : false,
|
|
'intermediateResults' => isset($match['intermediateResults']) ? $match['intermediateResults'] : null,
|
|
'teams' => isset($match['teams']) ? $match['teams'] : array(),
|
|
);
|
|
}
|
|
|
|
public function register_routes() {
|
|
register_rest_route('swi-foot/v1', '/teams', array(
|
|
'methods' => 'GET',
|
|
'callback' => array($this, 'get_teams'),
|
|
'permission_callback' => '__return_true'
|
|
));
|
|
|
|
register_rest_route('swi-foot/v1', '/matches', array(
|
|
'methods' => 'GET',
|
|
'callback' => array($this, 'get_matches_for_team'),
|
|
'permission_callback' => '__return_true',
|
|
'args' => array(
|
|
'team_id' => array(
|
|
'validate_callback' => function($param, $request, $key) {
|
|
return is_numeric($param);
|
|
}
|
|
)
|
|
)
|
|
));
|
|
|
|
register_rest_route('swi-foot/v1', '/block-status/(?P<match_id>\d+)/(?P<endpoint>[a-z_]+)', array(
|
|
'methods' => 'GET',
|
|
'callback' => array($this, 'check_block_status'),
|
|
'permission_callback' => '__return_true'
|
|
));
|
|
|
|
// Single match details: /wp-json/swi-foot/v1/match/<match_id>
|
|
register_rest_route('swi-foot/v1', '/match/(?P<match_id>[^/]+)', array(
|
|
'methods' => 'GET',
|
|
'callback' => array($this, 'get_match_details'),
|
|
'permission_callback' => '__return_true',
|
|
'args' => array(
|
|
'match_id' => array(
|
|
'validate_callback' => function($param, $request, $key) {
|
|
return is_string($param) && strlen($param) > 0;
|
|
}
|
|
)
|
|
)
|
|
));
|
|
|
|
// Match events with status: /wp-json/swi-foot/v1/events/<match_id>
|
|
// Requires authentication to prevent abuse from non-logged-in users
|
|
register_rest_route('swi-foot/v1', '/events/(?P<match_id>[^/]+)', array(
|
|
'methods' => 'GET',
|
|
'callback' => array($this, 'get_match_events'),
|
|
'permission_callback' => function() {
|
|
return is_user_logged_in() || apply_filters('swi_foot_rest_events_public', false);
|
|
},
|
|
'args' => array(
|
|
'match_id' => array(
|
|
'validate_callback' => function($param, $request, $key) {
|
|
return is_string($param) && strlen($param) > 0;
|
|
}
|
|
)
|
|
)
|
|
));
|
|
|
|
register_rest_route('swi-foot/v1', '/common-ids', array(
|
|
'methods' => 'GET',
|
|
'callback' => array($this, 'get_common_ids'),
|
|
'permission_callback' => '__return_true'
|
|
));
|
|
|
|
// Admin actions
|
|
register_rest_route('swi-foot/v1', '/admin/refresh-teams', array(
|
|
'methods' => 'POST',
|
|
'callback' => array($this, 'admin_refresh_teams'),
|
|
'permission_callback' => function() { return current_user_can('manage_options'); }
|
|
));
|
|
|
|
register_rest_route('swi-foot/v1', '/admin/clear-cache', array(
|
|
'methods' => 'POST',
|
|
'callback' => array($this, 'admin_clear_cache'),
|
|
'permission_callback' => function() { return current_user_can('manage_options'); }
|
|
));
|
|
|
|
register_rest_route('swi-foot/v1', '/admin/test-connection', array(
|
|
'methods' => 'POST',
|
|
'callback' => array($this, 'admin_test_connection'),
|
|
'permission_callback' => function() { return current_user_can('manage_options'); }
|
|
));
|
|
|
|
register_rest_route('swi-foot/v1', '/admin/finished/(?P<match_id>[^/]+)', array(
|
|
'methods' => 'DELETE',
|
|
'callback' => array($this, 'admin_delete_finished_match'),
|
|
'permission_callback' => function() { return current_user_can('manage_options'); },
|
|
'args' => array(
|
|
'match_id' => array(
|
|
'validate_callback' => function($param, $request, $key) {
|
|
return is_string($param) && strlen($param) > 0;
|
|
}
|
|
)
|
|
)
|
|
));
|
|
|
|
register_rest_route('swi-foot/v1', '/admin/finished', array(
|
|
'methods' => 'DELETE',
|
|
'callback' => array($this, 'admin_delete_all_finished_matches'),
|
|
'permission_callback' => function() { return current_user_can('manage_options'); }
|
|
));
|
|
}
|
|
|
|
public function get_teams($request) {
|
|
$api = new Swi_Foot_API();
|
|
$teams = $api->get_teams();
|
|
if (is_wp_error($teams)) {
|
|
return new WP_REST_Response(array('error' => $teams->get_error_message()), 400);
|
|
}
|
|
// Normalize team data for consistent responses
|
|
$normalized = array();
|
|
if (is_array($teams)) {
|
|
foreach ($teams as $team) {
|
|
$normalized[] = $this->normalize_team($team);
|
|
}
|
|
}
|
|
return rest_ensure_response($normalized);
|
|
}
|
|
|
|
public function get_matches_for_team($request) {
|
|
$team_id = $request->get_param('team_id');
|
|
if (empty($team_id)) {
|
|
return new WP_REST_Response(array('error' => 'team_id required'), 400);
|
|
}
|
|
$api = new Swi_Foot_API();
|
|
$schedule = $api->get_schedule($team_id);
|
|
if (is_wp_error($schedule)) {
|
|
return new WP_REST_Response(array('error' => $schedule->get_error_message()), 400);
|
|
}
|
|
// Normalize matches for consistent responses
|
|
$normalized = array();
|
|
if (is_array($schedule)) {
|
|
foreach ($schedule as $match) {
|
|
$normalized[] = $this->normalize_match($match);
|
|
}
|
|
}
|
|
return rest_ensure_response($normalized);
|
|
}
|
|
|
|
public function get_common_ids($request) {
|
|
$api = new Swi_Foot_API();
|
|
$commons = $api->get_commons_ids();
|
|
if (is_wp_error($commons)) {
|
|
return new WP_REST_Response(array('error' => $commons->get_error_message()), 400);
|
|
}
|
|
return rest_ensure_response($commons);
|
|
}
|
|
|
|
/**
|
|
* Return full match details for a given match_id
|
|
* GET /wp-json/swi-foot/v1/match/<match_id>
|
|
*/
|
|
public function get_match_details($request) {
|
|
$match_id = $request->get_param('match_id');
|
|
if (empty($match_id)) {
|
|
return new WP_REST_Response(array('error' => 'match_id required'), 400);
|
|
}
|
|
|
|
$api = new Swi_Foot_API();
|
|
$match = $api->get_match_details($match_id);
|
|
if (is_wp_error($match)) {
|
|
return new WP_REST_Response(array('error' => $match->get_error_message()), 400);
|
|
}
|
|
// Normalize match data for consistent API responses
|
|
$normalized = $this->normalize_match($match);
|
|
return rest_ensure_response($normalized);
|
|
}
|
|
|
|
/**
|
|
* Get match events with current match status (for frontend refresh polling)
|
|
*
|
|
* Endpoint: GET /wp-json/swi-foot/v1/events/<match_id>
|
|
*
|
|
* Returns the latest match events (sorted newest first) along with critical match
|
|
* status information (hasMatchStarted, hasMatchEnded). Frontend polls this endpoint
|
|
* at configurable intervals to update live event displays.
|
|
*
|
|
* Permission: Requires logged-in user (can be overridden with swi_foot_rest_events_public filter)
|
|
*
|
|
* @param WP_REST_Request $request The REST request object containing match_id parameter
|
|
* @return WP_REST_Response Array with keys:
|
|
* - matchId (string): The match ID
|
|
* - events (array): Array of event objects with matchMinute, eventTypeName, playerName, teamName, timestamp
|
|
* - hasMatchStarted (bool): Whether the match has begun
|
|
* - hasMatchEnded (bool): Whether the match has concluded
|
|
* - matchStateName (string|null): Current match status label
|
|
*/
|
|
public function get_match_events($request) {
|
|
$match_id = $request->get_param('match_id');
|
|
if (empty($match_id)) {
|
|
return new WP_REST_Response(array('error' => 'match_id required'), 400);
|
|
}
|
|
|
|
$api = new Swi_Foot_API();
|
|
|
|
// Get match details for status
|
|
$match = $api->get_match_details($match_id);
|
|
if (is_wp_error($match)) {
|
|
return new WP_REST_Response(array('error' => $match->get_error_message()), 400);
|
|
}
|
|
|
|
// Try saved finished match data first
|
|
$events = array();
|
|
$saved = $api->get_finished_match_data($match_id);
|
|
if ($saved) {
|
|
$events = $saved['events'] ?? array();
|
|
} else {
|
|
// Fetch live events
|
|
$events = $api->get_match_events($match_id);
|
|
if (is_wp_error($events)) {
|
|
$events = array();
|
|
}
|
|
}
|
|
|
|
// Get event ordering preference from query parameter
|
|
$event_order = $request->get_param('event_order') ?? 'dynamic';
|
|
|
|
// Convert exactEventTime to timestamps for sorting
|
|
if (!empty($events)) {
|
|
foreach ($events as &$event) {
|
|
// Convert ISO 8601 format (2026-03-14T17:03:50.437) to timestamp
|
|
// Strip milliseconds if present, keep only date and time part
|
|
$event['_timestamp'] = strtotime(substr($event['exactEventTime'], 0, 19));
|
|
}
|
|
unset($event);
|
|
|
|
// Sort by timestamp
|
|
if ($event_order === 'dynamic') {
|
|
// Dynamic: newest first while match is live, chronological after match ends
|
|
$match_has_ended = !empty($match['hasMatchEnded']);
|
|
|
|
usort($events, function ($a, $b) use ($match_has_ended) {
|
|
if ($match_has_ended) {
|
|
// Chronological (ascending): oldest first
|
|
return $a['_timestamp'] - $b['_timestamp'];
|
|
} else {
|
|
// Descending: newest first
|
|
return $b['_timestamp'] - $a['_timestamp'];
|
|
}
|
|
});
|
|
} elseif ($event_order === 'newest_first') {
|
|
// Always newest first (descending)
|
|
usort($events, function ($a, $b) {
|
|
return $b['_timestamp'] - $a['_timestamp'];
|
|
});
|
|
} elseif ($event_order === 'oldest_first') {
|
|
// Always oldest first (ascending)
|
|
usort($events, function ($a, $b) {
|
|
return $a['_timestamp'] - $b['_timestamp'];
|
|
});
|
|
}
|
|
}
|
|
|
|
return rest_ensure_response(array(
|
|
'matchId' => $match_id,
|
|
'events' => (array)$events,
|
|
'hasMatchStarted' => $match['hasMatchStarted'] ?? false,
|
|
'hasMatchEnded' => $match['hasMatchEnded'] ?? false,
|
|
'matchStateName' => $match['matchStateName'] ?? null,
|
|
));
|
|
}
|
|
|
|
public function admin_refresh_teams($request) {
|
|
$api = new Swi_Foot_API();
|
|
|
|
// Collect debug information to assist troubleshooting 401 responses
|
|
$debug = array();
|
|
if (method_exists($api, 'debug_get_token_info')) {
|
|
$debug = $api->debug_get_token_info();
|
|
error_log('Swiss Football Debug: refresh-teams token_present=' . ($debug['token_present'] ? '1' : '0') . ' base=' . $debug['base_url'] . ' url=' . $debug['team_list_url']);
|
|
}
|
|
|
|
delete_transient('swi_foot_teams');
|
|
$teams = $api->get_teams();
|
|
if (is_wp_error($teams)) {
|
|
// Try to include debug information in response for admin callers
|
|
$err = $teams->get_error_message();
|
|
error_log('Swiss Football API: refresh-teams failed - ' . $err);
|
|
return new WP_REST_Response(array('error' => $err, 'debug' => $debug), 400);
|
|
}
|
|
|
|
return rest_ensure_response(array('success' => true, 'data' => $teams, 'debug' => $debug));
|
|
}
|
|
|
|
public function admin_clear_cache($request) {
|
|
$keys = get_transient('swi_foot_match_keys');
|
|
if (is_array($keys)) {
|
|
foreach ($keys as $mid) {
|
|
delete_transient('swi_foot_match_' . $mid);
|
|
}
|
|
}
|
|
delete_transient('swi_foot_match_keys');
|
|
delete_transient('swi_foot_teams');
|
|
return rest_ensure_response(array('success' => true));
|
|
}
|
|
|
|
public function admin_test_connection($request) {
|
|
$api = new Swi_Foot_API();
|
|
if (method_exists($api, 'test_connection')) {
|
|
$result = $api->test_connection();
|
|
} else {
|
|
$result = array(
|
|
'success' => false,
|
|
'error' => 'test_connection method not found',
|
|
'details' => 'Plugin may be incorrectly installed'
|
|
);
|
|
}
|
|
|
|
// Return response with full error details
|
|
if (is_array($result) && isset($result['success'])) {
|
|
if ($result['success']) {
|
|
return rest_ensure_response(array(
|
|
'success' => true,
|
|
'message' => isset($result['details']) ? $result['details'] : 'Connection successful!'
|
|
));
|
|
} else {
|
|
return new WP_REST_Response(array(
|
|
'success' => false,
|
|
'error' => isset($result['error']) ? $result['error'] : 'Connection failed',
|
|
'details' => isset($result['details']) ? $result['details'] : 'Unknown error'
|
|
), 400);
|
|
}
|
|
}
|
|
|
|
// Fallback for unexpected response format
|
|
return new WP_REST_Response(array(
|
|
'success' => false,
|
|
'error' => 'Unexpected response from API test',
|
|
'details' => 'Unable to determine connection status'
|
|
), 400);
|
|
}
|
|
|
|
public function check_block_status($request) {
|
|
$match_id = $request->get_param('match_id');
|
|
$endpoint = $request->get_param('endpoint');
|
|
|
|
if (empty($match_id) || empty($endpoint)) {
|
|
return rest_ensure_response(array('status' => 'error', 'message' => 'Missing parameters'));
|
|
}
|
|
|
|
$api = new Swi_Foot_API();
|
|
|
|
// Map endpoint names to API methods
|
|
$method_map = array(
|
|
'roster' => 'get_match_players',
|
|
'bench' => 'get_match_bench',
|
|
'referees' => 'get_match_referees',
|
|
'events' => 'get_match_events'
|
|
);
|
|
|
|
if (!isset($method_map[$endpoint])) {
|
|
return rest_ensure_response(array('status' => 'error', 'message' => __('Unknown endpoint', 'swi_foot_matchdata')));
|
|
}
|
|
|
|
$method = $method_map[$endpoint];
|
|
$result = $api->$method($match_id);
|
|
|
|
// Determine status based on result
|
|
if (is_wp_error($result)) {
|
|
$code = $result->get_error_code();
|
|
if ($code === 'data_not_available') {
|
|
return rest_ensure_response(array('status' => 'warning', 'message' => __('Data not yet available', 'swi_foot_matchdata')));
|
|
} else {
|
|
return rest_ensure_response(array('status' => 'error', 'message' => __('No data available', 'swi_foot_matchdata')));
|
|
}
|
|
} elseif (empty($result)) {
|
|
return rest_ensure_response(array('status' => 'error', 'message' => __('No data available', 'swi_foot_matchdata')));
|
|
} else {
|
|
return rest_ensure_response(array('status' => 'success', 'message' => __('Data available', 'swi_foot_matchdata')));
|
|
}
|
|
}
|
|
|
|
public function admin_delete_finished_match($request) {
|
|
$match_id = $request->get_param('match_id');
|
|
if (empty($match_id)) return new WP_REST_Response(array('error' => 'match_id required'), 400);
|
|
$data = get_option('swi_foot_finished_matches', array());
|
|
if (isset($data[$match_id])) {
|
|
unset($data[$match_id]);
|
|
update_option('swi_foot_finished_matches', $data);
|
|
return rest_ensure_response(array('success' => true));
|
|
}
|
|
return new WP_REST_Response(array('error' => 'not found'), 404);
|
|
}
|
|
|
|
public function admin_delete_all_finished_matches($request) {
|
|
delete_option('swi_foot_finished_matches');
|
|
return rest_ensure_response(array('success' => true));
|
|
}
|
|
}
|
|
|
|
new Swi_Foot_REST();
|