wp-plugin-swiss-football-ma.../includes/class-swi-foot-shortcodes.php
Reindl David (IT-PTR-CEN2-SL10) fbd595aa36 initial state
2026-03-18 19:58:24 +01:00

1077 lines
44 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
class Swi_Foot_Shortcodes
{
private $api;
public function __construct()
{
$this->api = new Swi_Foot_API();
// Register shortcodes
add_shortcode('swi_foot_match', array($this, 'match_shortcode'));
add_shortcode('swi_foot_match_home_team', array($this, 'match_home_team_shortcode'));
add_shortcode('swi_foot_match_away_team', array($this, 'match_away_team_shortcode'));
add_shortcode('swi_foot_match_date', array($this, 'match_date_shortcode'));
add_shortcode('swi_foot_match_time', array($this, 'match_time_shortcode'));
add_shortcode('swi_foot_match_venue', array($this, 'match_venue_shortcode'));
add_shortcode('swi_foot_match_score', array($this, 'match_score_shortcode'));
add_shortcode('swi_foot_match_status', array($this, 'match_status_shortcode'));
add_shortcode('swi_foot_match_league', array($this, 'match_league_shortcode'));
add_shortcode('swi_foot_match_round', array($this, 'match_round_shortcode'));
add_shortcode('swi_foot_standings', array($this, 'standings_shortcode'));
add_shortcode('swi_foot_roster', array($this, 'roster_shortcode'));
add_shortcode('swi_foot_events', array($this, 'events_shortcode'));
add_shortcode('swi_foot_match_data', array($this, 'match_data_shortcode'));
add_shortcode('swi_foot_match_home_team_logo', array($this, 'match_home_team_logo_shortcode'));
add_shortcode('swi_foot_match_away_team_logo', array($this, 'match_away_team_logo_shortcode'));
add_shortcode('swi_foot_match_home_team_logo_url', array($this, 'match_home_team_logo_url_shortcode'));
add_shortcode('swi_foot_match_away_team_logo_url', array($this, 'match_away_team_logo_url_shortcode'));
// Register query variable for team logo requests and handle image serving
add_filter('query_vars', array($this, 'register_query_vars'));
add_action('template_redirect', array($this, 'handle_team_logo_request'));
}
/**
* Register custom query variables
*/
public function register_query_vars($vars)
{
$vars[] = 'swi_foot_team_logo';
$vars[] = 'position';
return $vars;
}
/**
* Handle internal requests for team logo images
* URL format: /?swi_foot_team_logo=team_id&position=home|away|event
* Uses template_redirect hook which fires after WordPress determines the query
* but before loading the template/theme
*/
public function handle_team_logo_request()
{
$team_logo_id = get_query_var('swi_foot_team_logo');
error_log('Swiss Football: handle_team_logo_request() called, team_logo_id from get_query_var: ' . var_export($team_logo_id, true));
if (empty($team_logo_id)) {
error_log('Swiss Football: team_logo_id is empty, skipping image serving');
return; // Not a team logo request, continue normal WordPress flow
}
error_log('Swiss Football: Processing team logo request for team_id: ' . $team_logo_id);
// This is a team logo request, serve the image and exit
$this->serve_team_logo_image($team_logo_id);
exit; // Never reached, but for clarity
}
/**
* Serve team logo image from API base64 data
*/
private function serve_team_logo_image($team_id)
{
error_log('Swiss Football: serve_team_logo_image() called with team_id: ' . $team_id . ', type: ' . gettype($team_id));
// Validate team_id is numeric
if (!ctype_digit((string)$team_id) || empty($team_id)) {
error_log('Swiss Football: Invalid team ID provided: ' . var_export($team_id, true));
wp_die('Invalid team ID', 'Invalid Request', array('response' => 400));
}
error_log('Swiss Football: team_id validation passed, team_id: ' . $team_id);
error_log('Swiss Football: Attempting to fetch team picture for team_id: ' . $team_id);
// Fetch image from API
$image_data = $this->api->get_team_picture($team_id);
error_log('Swiss Football: API response type: ' . gettype($image_data));
if (is_wp_error($image_data)) {
error_log('Swiss Football: API returned WP_Error: ' . $image_data->get_error_message());
} else {
error_log('Swiss Football: API response length: ' . strlen(var_export($image_data, true)) . ', is_array: ' . (is_array($image_data) ? 'yes' : 'no') . ', is_string: ' . (is_string($image_data) ? 'yes' : 'no'));
if (is_string($image_data)) {
error_log('Swiss Football: String response first 200 chars: ' . substr($image_data, 0, 200));
}
}
if (is_wp_error($image_data)) {
error_log('Swiss Football: API error fetching team picture: ' . $image_data->get_error_message());
wp_die('Image not found', 'Not Found', array('response' => 404));
}
if (empty($image_data)) {
error_log('Swiss Football: Team picture returned empty for team_id: ' . $team_id);
wp_die('Image not found', 'Not Found', array('response' => 404));
}
error_log('Swiss Football: Successfully fetched image data for team_id: ' . $team_id . ', length: ' . strlen($image_data));
// Handle API response that may be an array (if JSON returned a structure)
// or a string (if JSON returned a plain string)
if (is_array($image_data)) {
error_log('Swiss Football: Image data is array, attempting to extract base64 from common fields');
// Try to find the base64 data in common field names
$potential_fields = array('picture', 'logo', 'image', 'data', 'url', 'href');
$found_data = null;
foreach ($potential_fields as $field) {
if (isset($image_data[$field]) && !empty($image_data[$field])) {
$found_data = $image_data[$field];
error_log('Swiss Football: Found image data in array field: ' . $field);
break;
}
}
if (!$found_data) {
error_log('Swiss Football: Could not find image data in array, array keys: ' . implode(', ', array_keys($image_data)));
wp_die('Image not found', 'Not Found', array('response' => 404));
}
$image_data = $found_data;
error_log('Swiss Football: Extracted base64 from array, length: ' . strlen($image_data));
}
// At this point, $image_data should be a string (base64 encoded)
// The API returns base64 encoded image data
// Detect image format from the data or default to PNG
$mime_type = 'image/png';
if (strpos($image_data, 'data:image/') === 0) {
// Already has data URI format, extract mime type
preg_match('/data:([^;]+)/', $image_data, $matches);
if (isset($matches[1])) {
$mime_type = $matches[1];
// Remove data URI prefix to get just base64
$image_data = preg_replace('/^data:[^;]+;base64,/', '', $image_data);
}
} elseif (strpos($image_data, '/') === false && strlen($image_data) > 100) {
// Looks like raw base64, assume PNG
$mime_type = 'image/png';
}
// Decode base64
$decoded = base64_decode(trim($image_data), true);
if ($decoded === false) {
error_log('Swiss Football: Failed to decode base64 image data for team_id: ' . $team_id);
wp_die('Invalid image data', 'Bad Request', array('response' => 400));
}
error_log('Swiss Football: Successfully decoded image, size: ' . strlen($decoded) . ' bytes, mime: ' . $mime_type);
// Set headers for image serving
header('Content-Type: ' . $mime_type);
header('Content-Length: ' . strlen($decoded));
header('Cache-Control: public, max-age=2592000'); // 30 days
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 2592000) . ' GMT');
header('Access-Control-Allow-Origin: *');
// Output image and exit
echo $decoded;
exit;
}
/**
* Generate internal URL for serving team logo image
*
* @param string $team_id The team ID
* @param string $position 'home' or 'away'
* @return string URL to internal team logo endpoint
*/
private function get_team_logo_url($team_id, $position = 'home')
{
return add_query_arg(array(
'swi_foot_team_logo' => $team_id,
'position' => $position
), home_url('/'));
}
public function match_shortcode($atts)
{
$atts = shortcode_atts(array(
'match_id' => '',
'team_id' => '',
'show_next' => 'false'
), $atts);
$match_data = $this->get_match_data($atts);
if (!$match_data) {
return '<span class="swi-foot-error">' . __('Match data not available', 'swi_foot_matchdata') . '</span>';
}
ob_start();
?>
<div class="swi-foot-match-container" data-match-id="<?php echo esc_attr($match_data['matchId']); ?>">
<div class="match-teams">
<?php if (!empty($match_data['teams'])): ?>
<?php
$home_team = '';
$away_team = '';
foreach ($match_data['teams'] as $team) {
if ($team['isHomeTeam']) {
$home_team = $team['teamName'];
} else {
$away_team = $team['teamName'];
}
}
?>
<span class="home-team"><?php echo esc_html($home_team); ?></span>
<span class="vs"> vs </span>
<span class="away-team"><?php echo esc_html($away_team); ?></span>
<?php endif; ?>
</div>
<div class="match-details">
<div class="match-date">
<?php echo date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($match_data['matchDate'])); ?>
</div>
<?php if (!empty($match_data['stadiumFieldName'])): ?>
<div class="match-venue"><?php echo esc_html($match_data['stadiumFieldName']); ?></div>
<?php endif; ?>
<?php if ($match_data['hasMatchStarted'] || $match_data['hasMatchEnded']): ?>
<div class="match-score">
<?php echo esc_html($match_data['scoreTeamA']); ?> : <?php echo esc_html($match_data['scoreTeamB']); ?>
</div>
<?php endif; ?>
<?php if (!empty($match_data['matchStateName'])): ?>
<div class="match-status"><?php echo esc_html($match_data['matchStateName']); ?></div>
<?php endif; ?>
<?php if (!empty($match_data['leagueName'])): ?>
<div class="match-league"><?php echo esc_html($match_data['leagueName']); ?></div>
<?php endif; ?>
</div>
</div>
<?php
return ob_get_clean();
}
public function match_home_team_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (!$match_data || empty($match_data['teams'])) {
return '';
}
foreach ($match_data['teams'] as $team) {
if ($team['isHomeTeam']) {
return '<span class="swi-foot-inline home-team">' . esc_html($team['teamName']) . '</span>';
}
}
return '';
}
public function match_away_team_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (!$match_data || empty($match_data['teams'])) {
return '';
}
foreach ($match_data['teams'] as $team) {
if (!$team['isHomeTeam']) {
return '<span class="swi-foot-inline away-team">' . esc_html($team['teamName']) . '</span>';
}
}
return '';
}
public function match_date_shortcode($atts)
{
$atts = shortcode_atts(array(
'match_id' => '',
'team_id' => '',
'show_next' => 'false',
'format' => get_option('date_format', 'd.m.Y')
), $atts);
$match_data = $this->get_match_data($atts);
if (!$match_data || empty($match_data['matchDate'])) {
return '';
}
return '<span class="swi-foot-inline date">' . date_i18n($atts['format'], strtotime($match_data['matchDate'])) . '</span>';
}
public function match_time_shortcode($atts)
{
$atts = shortcode_atts(array(
'match_id' => '',
'team_id' => '',
'show_next' => 'false',
'format' => get_option('time_format', 'H:i')
), $atts);
$match_data = $this->get_match_data($atts);
if (!$match_data || empty($match_data['matchDate'])) {
return '';
}
return '<span class="swi-foot-inline time">' . date_i18n($atts['format'], strtotime($match_data['matchDate'])) . '</span>';
}
public function match_venue_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (!$match_data) {
return '';
}
$venue = $match_data['stadiumFieldName'] ?? '';
if ($venue) {
return '<span class="swi-foot-inline venue">' . esc_html($venue) . '</span>';
}
return '';
}
public function match_score_shortcode($atts)
{
$atts = shortcode_atts(array(
'match_id' => '',
'team_id' => '',
'show_next' => 'false',
'separator' => ':'
), $atts);
$match_data = $this->get_match_data($atts);
if (!$match_data) {
return '';
}
if ($match_data['hasMatchStarted'] || $match_data['hasMatchEnded']) {
$score = esc_html($match_data['scoreTeamA']) . ' ' . $atts['separator'] . ' ' . esc_html($match_data['scoreTeamB']);
return '<span class="swi-foot-inline score">' . $score . '</span>';
}
return '';
}
public function match_status_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (!$match_data) {
return '';
}
$status = $match_data['matchStateName'] ?? '';
if ($status) {
return '<span class="swi-foot-inline status">' . esc_html($status) . '</span>';
}
return '';
}
public function match_league_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (!$match_data) {
return '';
}
$league = $match_data['leagueName'] ?? '';
if ($league) {
return '<span class="swi-foot-inline league">' . esc_html($league) . '</span>';
}
return '';
}
public function match_round_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (!$match_data) {
return '';
}
$round = $match_data['roundNbr'] ?? '';
if ($round) {
return '<span class="swi-foot-inline round">' . esc_html($round) . '</span>';
}
return '';
}
/**
* Generic inline data point shortcode
* Extracts a specific field from match data and displays it inline
* Usage: [swi_foot_match_data data_point="teamNameA"]
* Match ID is automatically picked up from container context if not specified
* Special data points: matchDate_date and matchDate_time (formatted per WordPress locale)
*/
public function match_data_shortcode($atts)
{
$atts = shortcode_atts(array(
'match_id' => '',
'team_id' => '',
'data_point' => '',
), $atts);
// If match_id not provided, get it from post context
if (empty($atts['match_id'])) {
$context = function_exists('swi_foot_resolve_context') ? swi_foot_resolve_context(get_the_ID()) : array();
$atts['match_id'] = $context['match_id'] ?? '';
}
if (empty($atts['match_id']) || empty($atts['data_point'])) {
return '';
}
$match_data = $this->get_match_data($atts);
if (!$match_data) {
return '';
}
// Normalize match data to ensure consistent field names
$match_data = $this->normalize_shortcode_match_data($match_data);
// Handle special date/time formatting
if ($atts['data_point'] === 'matchDate_date') {
$value = $this->format_match_date($match_data);
} elseif ($atts['data_point'] === 'matchDate_time') {
$value = $this->format_match_time($match_data);
} else {
// Support nested data points like "intermediateResults/scoreTeamA"
$value = $this->get_nested_value($match_data, $atts['data_point']);
}
if ($value === null || $value === '') {
return '';
}
// Convert boolean values to readable text
if (is_bool($value)) {
$value = $value ? __('Ja', 'swi_foot_matchdata') : __('Nein', 'swi_foot_matchdata');
}
return '<span class="swi-foot-inline swi-foot-data-point" data-field="' . esc_attr($atts['data_point']) . '">' . esc_html($value) . '</span>';
}
/**
* Normalize match data to ensure consistent field names
* Extracts team names from teams array if not present as direct fields
* @param array $match Raw match data
* @return array Normalized match data
*/
private function normalize_shortcode_match_data($match)
{
// If already has teamNameA/teamNameB as direct fields, return as-is
if (!empty($match['teamNameA']) || !empty($match['teamNameB'])) {
return $match;
}
// Extract team names from teams array if not present as direct fields
if (!empty($match['teams']) && is_array($match['teams'])) {
foreach ($match['teams'] as $team) {
if (!empty($team['isHomeTeam']) && !empty($team['teamName'])) {
$match['teamNameA'] = $team['teamName'];
} elseif (empty($team['isHomeTeam']) && !empty($team['teamName'])) {
$match['teamNameB'] = $team['teamName'];
}
}
}
// Normalize stadium field name
if (empty($match['stadiumFieldName']) && !empty($match['stadiumPlaygroundName'])) {
$match['stadiumFieldName'] = $match['stadiumPlaygroundName'];
}
return $match;
}
/**
* Helper to get nested array values using dot/slash notation
* @param array $array
* @param string $path e.g. "intermediateResults/scoreTeamA"
* @return mixed
*/
private function get_nested_value($array, $path)
{
if (!is_array($array) || empty($path)) {
return null;
}
$keys = explode('/', $path);
$current = $array;
foreach ($keys as $key) {
if (!is_array($current) || !isset($current[$key])) {
return null;
}
$current = $current[$key];
}
return $current;
}
/**
* Format match date according to WordPress locale settings
* Extracts date part from matchDate and formats using WordPress date format
* @param array $match Match data array
* @return string|null Formatted date or null if matchDate not available
*/
private function format_match_date($match)
{
if (empty($match['matchDate'])) {
return null;
}
// Parse the matchDate string - handle multiple formats
$timestamp = strtotime($match['matchDate']);
if ($timestamp === false) {
return null;
}
// Use WordPress date format setting
$date_format = get_option('date_format');
return wp_date($date_format, $timestamp);
}
/**
* Format match time according to WordPress locale settings
* Extracts time part from matchDate and formats using WordPress time format
* @param array $match Match data array
* @return string|null Formatted time or null if matchDate not available
*/
private function format_match_time($match)
{
if (empty($match['matchDate'])) {
return null;
}
// Parse the matchDate string - handle multiple formats
$timestamp = strtotime($match['matchDate']);
if ($timestamp === false) {
return null;
}
// Use WordPress time format setting
$time_format = get_option('time_format');
return wp_date($time_format, $timestamp);
}
private function get_match_data($atts)
{
$atts = shortcode_atts(array(
'match_id' => '',
'team_id' => '',
'show_next' => 'false'
), $atts);
$match_id = $atts['match_id'];
// If no match_id provided explicitly, try to resolve it from the post/page context
if (empty($match_id)) {
if (function_exists('swi_foot_resolve_context')) {
$ctx = swi_foot_resolve_context();
if (!empty($ctx['match_id'])) {
$match_id = $ctx['match_id'];
} elseif (empty($match_id) && !empty($ctx['team_id']) && $atts['show_next'] === 'true') {
// Use schedule to find next match for the team when show_next requested
$schedule = $this->api->get_schedule($ctx['team_id']);
if (!is_wp_error($schedule) && !empty($schedule)) {
foreach ($schedule as $match) {
if (strtotime($match['matchDate']) >= time()) {
$match_id = $match['matchId'];
break;
}
}
}
}
}
}
// If no match_id but show_next is true, try to find next match for team
if (empty($match_id) && $atts['show_next'] === 'true' && !empty($atts['team_id'])) {
$schedule = $this->api->get_schedule($atts['team_id']);
if (!is_wp_error($schedule) && !empty($schedule)) {
// Find next upcoming match
foreach ($schedule as $match) {
if (strtotime($match['matchDate']) >= time()) {
$match_id = $match['matchId'];
break;
}
}
}
}
if (empty($match_id)) {
return null;
}
// Try to get cached data first
$match_data = $this->api->get_cached_match_data($match_id);
// If no cached data, fetch from API
if (!$match_data) {
$match_data = $this->api->get_match_details($match_id);
if (is_wp_error($match_data)) {
return null;
}
}
return $match_data;
}
public function standings_shortcode($atts)
{
$atts = shortcode_atts(array(
'team_id' => ''
), $atts);
if (empty($atts['team_id'])) {
return '<span class="swi-foot-error">' . __('Team ID required', 'swi_foot_matchdata') . '</span>';
}
$standings = $this->api->get_standings($atts['team_id']);
if (is_wp_error($standings) || empty($standings)) {
return '<span class="swi-foot-error">' . __('Standings data not available', 'swi_foot_matchdata') . '</span>';
}
ob_start();
?>
<div class="swi-foot-standings">
<h3><?php _e('Current Standings', 'swi_foot_matchdata'); ?></h3>
<table class="swi-foot-table">
<thead>
<tr>
<th><?php _e('Pos', 'swi_foot_matchdata'); ?></th>
<th><?php _e('Team', 'swi_foot_matchdata'); ?></th>
<th><?php _e('P', 'swi_foot_matchdata'); ?></th>
<th><?php _e('W', 'swi_foot_matchdata'); ?></th>
<th><?php _e('D', 'swi_foot_matchdata'); ?></th>
<th><?php _e('L', 'swi_foot_matchdata'); ?></th>
<th><?php _e('GF', 'swi_foot_matchdata'); ?></th>
<th><?php _e('GA', 'swi_foot_matchdata'); ?></th>
<th><?php _e('Pts', 'swi_foot_matchdata'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($standings as $team): ?>
<tr<?php echo ($team['teamId'] == $atts['team_id']) ? ' class="highlight"' : ''; ?>>
<td><?php echo esc_html($team['position']); ?></td>
<td><?php echo esc_html($team['teamName']); ?></td>
<td><?php echo esc_html($team['matches']); ?></td>
<td><?php echo esc_html($team['wins']); ?></td>
<td><?php echo esc_html($team['draws']); ?></td>
<td><?php echo esc_html($team['losses']); ?></td>
<td><?php echo esc_html($team['goalsFor']); ?></td>
<td><?php echo esc_html($team['goalsAgainst']); ?></td>
<td><strong><?php echo esc_html($team['points']); ?></strong></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
return ob_get_clean();
}
public function roster_shortcode($atts)
{
$atts = shortcode_atts(array(
'match_id' => '',
'team_id' => '',
'show_current' => 'false',
'side' => '',
'with_bench' => 'false'
), $atts);
$side = strtolower(trim($atts['side']));
if (!in_array($side, array('home', 'away'), true)) {
return '<span class="swi-foot-error">' . __('Parameter "side" must be "home" or "away"', 'swi_foot_matchdata') . '</span>';
}
$match_id = $atts['match_id'];
if ($atts['show_current'] === 'true' && !empty($atts['team_id'])) {
$current_match = $this->api->get_current_match($atts['team_id']);
if ($current_match) {
$match_id = $current_match['matchId'];
}
}
if (empty($match_id)) {
return '<span class="swi-foot-error">' . __('Match ID required', 'swi_foot_matchdata') . '</span>';
}
// First check if we have saved final data
$saved = $this->api->get_finished_match_data($match_id);
if ($saved) {
$players = $saved['roster']['players'] ?? array();
$bench_players = $saved['roster']['bench'] ?? array();
} else {
// Live fetch
$players = $this->api->get_match_players($match_id);
if (is_wp_error($players)) {
return '<span class="swi-foot-error">' . __('Roster data not available', 'swi_foot_matchdata') . ': ' . esc_html($players->get_error_message()) . '</span>';
}
$bench_players = array();
if (strtolower($atts['with_bench']) === 'true') {
$bench = $this->api->get_match_bench($match_id);
if (!is_wp_error($bench)) {
$bench_players = $bench;
}
}
// Check match ended & possibly save events + roster together
$match_details = $this->api->get_match_details($match_id);
if (!is_wp_error($match_details) && !empty($match_details['hasMatchEnded'])) {
$events = $this->api->get_match_events($match_id);
if (is_wp_error($events)) {
$events = array();
}
// Only save if we have valid player data (not a WP_Error)
if (is_array($players)) {
$this->api->save_finished_match_data(
$match_id,
array('players' => $players, 'bench' => $bench_players),
$events
);
}
}
}
// Filter roster for side
$filtered_players = array_filter($players, function ($p) use ($side) {
return $side === 'home' ? !empty($p['isHomeTeam']) : empty($p['isHomeTeam']);
});
$filtered_bench = array_filter($bench_players, function ($p) use ($side) {
return $side === 'home' ? !empty($p['isHomeTeam']) : empty($p['isHomeTeam']);
});
ob_start();
?>
<div class="swi-foot-roster" data-match-id="<?php echo esc_attr($match_id); ?>">
<h3><?php echo esc_html(ucfirst($side) . ' ' . __('Team Roster', 'swi_foot_matchdata')); ?></h3>
<?php if (!empty($filtered_players)): ?>
<ul class="roster-list">
<?php foreach ($filtered_players as $pl): ?>
<li>
<?php
$jersey = $pl['jerseyNumber'] ?? '';
$firstname = $pl['firstname'] ?? '';
$name = $pl['name'] ?? '';
$secondName = $pl['secondName'] ?? '';
$positionName = $pl['positionName'] ?? $pl['position'] ?? '';
$displayName = trim($firstname . ' ' . $name . ' ' . $secondName);
echo esc_html($jersey . ' ' . $displayName);
if (!empty($positionName)) {
echo ' (' . esc_html($positionName) . ')';
}
?>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p><?php _e('No roster data available.', 'swi_foot_matchdata'); ?></p>
<?php endif; ?>
<?php if (!empty($filtered_bench)): ?>
<h4><?php _e('Bench', 'swi_foot_matchdata'); ?></h4>
<ul class="roster-bench-list">
<?php foreach ($filtered_bench as $pl): ?>
<li>
<?php
$jersey = $pl['jerseyNumber'] ?? '';
$firstname = $pl['firstname'] ?? '';
$name = $pl['name'] ?? '';
$secondName = $pl['secondName'] ?? '';
$positionName = $pl['positionName'] ?? $pl['position'] ?? '';
$displayName = trim($firstname . ' ' . $name . ' ' . $secondName);
echo esc_html($jersey . ' ' . $displayName);
if (!empty($positionName)) {
echo ' (' . esc_html($positionName) . ')';
}
?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
public function events_shortcode($atts)
{
error_log('[SWI_FOOT_EVENTS_DEBUG] RAW SHORTCODE ATTRIBUTES: ' . json_encode($atts, JSON_UNESCAPED_SLASHES));
$atts = shortcode_atts(array(
'match_id' => '',
'team_id' => '',
'show_current' => 'false',
'refresh_interval' => '30',
'event_order' => 'dynamic'
), $atts);
error_log('[SWI_FOOT_EVENTS_DEBUG] PARSED ATTRIBUTES: ' . json_encode($atts, JSON_UNESCAPED_SLASHES));
error_log('[SWI_FOOT_EVENTS_DEBUG] EVENT_ORDER VALUE: ' . var_export($atts['event_order'], true));
$match_id = $atts['match_id'];
if ($atts['show_current'] === 'true' && !empty($atts['team_id'])) {
$current_match = $this->api->get_current_match($atts['team_id']);
if ($current_match) {
$match_id = $current_match['matchId'];
}
}
if (empty($match_id)) {
return '<span class="swi-foot-error">' . __('Match ID required', 'swi_foot_matchdata') . '</span>';
}
// Try saved first
$saved = $this->api->get_finished_match_data($match_id);
if ($saved) {
$events = $saved['events'] ?? array();
} else {
$events = $this->api->get_match_events($match_id);
if (is_wp_error($events)) {
return '<span class="swi-foot-error">' . __('Events data not available', 'swi_foot_matchdata') . '</span>';
}
// Check if match ended and store both data sets
$match_details = $this->api->get_match_details($match_id);
if (!is_wp_error($match_details) && !empty($match_details['hasMatchEnded'])) {
$players = $this->api->get_match_players($match_id);
$bench = $this->api->get_match_bench($match_id);
if (is_wp_error($players)) $players = array();
if (is_wp_error($bench)) $bench = array();
$this->api->save_finished_match_data(
$match_id,
array('players' => $players, 'bench' => $bench),
$events
);
}
}
// Determine event sort order based on setting and match status
$event_order = $atts['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
$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_details = $this->api->get_match_details($match_id);
$match_has_ended = !is_wp_error($match_details) && !empty($match_details['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'];
});
}
}
ob_start();
?>
<div class="swi-foot-events" data-match-id="<?php echo esc_attr($match_id); ?>" data-refresh="<?php echo esc_attr($atts['refresh_interval']); ?>" data-event-order="<?php echo esc_attr($event_order); ?>" aria-live="polite" role="status" aria-label="<?php echo esc_attr(__('Live match events', 'swi_foot_matchdata')); ?>">
<h3><?php _e('Match Events', 'swi_foot_matchdata'); ?></h3>
<div class="events-timeline">
<?php if (!empty($events)): ?>
<?php foreach ($events as $event): ?>
<div class="event-item">
<div class="event-minute">
<span class="minute-time" title="<?php echo esc_attr(__('Match minute', 'swi_foot_matchdata')); ?>">
⏱ <?php
$minute = $event['minute'] ?? '';
$additionalMinute = $event['additionalMinute'] ?? 0;
if (!empty($minute)) {
echo esc_html($minute) . "'";
if ($additionalMinute > 0) {
echo '+' . esc_html($additionalMinute);
}
}
?>
</span>
</div>
<div class="event-main">
<div class="event-header">
<div class="event-type-info">
<strong class="event-type"><?php
$eventTypeName = $event['eventTypeName'] ?? '';
$eventSubTypeName = $event['eventSubTypeName'] ?? '';
if (!empty($eventTypeName)) {
echo esc_html($eventTypeName);
}
if (!empty($eventSubTypeName) && $eventSubTypeName !== '-') {
echo ' <span class="event-subtype">(' . esc_html($eventSubTypeName) . ')</span>';
}
?></strong>
</div>
<?php if (!empty($event['teamId'])): ?>
<div class="team-logo">
<?php
$team_id = $event['teamId'];
$logo_url = $this->get_team_logo_url($team_id, 'event');
echo '<img src="' . esc_url($logo_url) . '" alt="' . esc_attr($event['teamName'] ?? '') . '" title="' . esc_attr($event['teamName'] ?? '') . '" />';
?>
</div>
<?php endif; ?>
</div>
<div class="event-details">
<?php
$jerseyNumber = $event['jerseyNumber'] ?? '';
$personName = $event['personName'] ?? $event['playerName'] ?? '';
$eventTypeId = $event['eventTypeId'] ?? 0;
if (!empty($jerseyNumber) || !empty($personName)) {
echo '<span class="player-info">👤 ' . esc_html($jerseyNumber . ' ' . $personName) . '</span>';
}
// If event type is exchange (2), show substitute player
if ($eventTypeId == 2) {
$subJersey = $event['substitutePlayerJerseyNumber'] ?? '';
$subName = $event['substitutePlayerName'] ?? '';
if (!empty($subJersey) || !empty($subName)) {
echo ' <span class="substitute-info">⇄ ' . esc_html($subJersey . ' ' . $subName) . '</span>';
}
}
?>
</div>
</div>
</div>
<?php endforeach; ?>
<?php else: ?>
<p><?php _e('No events recorded yet.', 'swi_foot_matchdata'); ?></p>
<?php endif; ?>
</div>
</div>
<?php
return ob_get_clean();
}
public function match_home_team_logo_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (!$match_data || empty($match_data['teams']) || empty($match_data['teams'][0])) {
return '';
}
$home_team_id = $match_data['teams'][0]['teamId'] ?? '';
if (empty($home_team_id)) {
return '';
}
$logo_url = $this->get_team_logo_url($home_team_id, 'home');
$team_name = esc_attr($match_data['teams'][0]['teamName'] ?? 'Home Team');
return '<img src="' . esc_url($logo_url) . '" alt="' . $team_name . '" class="swi-foot-team-logo home-team" />';
}
public function match_away_team_logo_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (!$match_data || empty($match_data['teams']) || empty($match_data['teams'][1])) {
return '';
}
$away_team_id = $match_data['teams'][1]['teamId'] ?? '';
if (empty($away_team_id)) {
return '';
}
$logo_url = $this->get_team_logo_url($away_team_id, 'away');
$team_name = esc_attr($match_data['teams'][1]['teamName'] ?? 'Away Team');
return '<img src="' . esc_url($logo_url) . '" alt="' . $team_name . '" class="swi-foot-team-logo away-team" />';
}
/**
* Shortcode: Display home team logo URL only (for use in image blocks)
* Usage: [swi_foot_match_home_team_logo_url]
* Returns: Internal URL that serves the team logo image
*/
public function match_home_team_logo_url_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (empty($match_data)) {
return '';
}
$home_team_id = $match_data['teams'][0]['teamId'] ?? '';
if (empty($home_team_id)) {
return '';
}
return $this->get_team_logo_url($home_team_id, 'home');
}
/**
* Shortcode: Display away team logo URL only (for use in image blocks)
* Usage: [swi_foot_match_away_team_logo_url]
* Returns: Internal URL that serves the team logo image
*/
public function match_away_team_logo_url_shortcode($atts)
{
$match_data = $this->get_match_data($atts);
if (empty($match_data)) {
return '';
}
$away_team_id = $match_data['teams'][1]['teamId'] ?? '';
if (empty($away_team_id)) {
return '';
}
return $this->get_team_logo_url($away_team_id, 'away');
}
public function enqueue_editor_assets()
{
wp_enqueue_script(
'swi-foot-editor-blocks',
SWI_FOOT_PLUGIN_URL . 'assets/editor-blocks.js',
array('wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n', 'wp-data', 'jquery'),
SWI_FOOT_PLUGIN_VERSION,
true
);
wp_localize_script('swi-foot-editor-blocks', 'swiFootEditorData', array(
'rest_url' => esc_url_raw(rest_url('swi-foot/v1')),
'rest_nonce' => wp_create_nonce('wp_rest')
));
}
}
/**
* Replace saved inline shortcode spans with their rendered shortcode output on the front-end.
* Spans are inserted by the editor format and contain a `data-shortcode` attribute.
*/
function swi_foot_render_inline_shortcodes($content)
{
if (stripos($content, 'swi-foot-inline-shortcode') === false) {
return $content;
}
return preg_replace_callback(
'/<span[^>]*class=["\']?[^"\'>]*swi-foot-inline-shortcode[^"\'>]*["\']?[^>]*data-shortcode=["\']([^"\']+)["\'][^>]*>.*?<\/span>/is',
function ($m) {
$sc = html_entity_decode($m[1]);
return do_shortcode($sc);
},
$content
);
}
add_filter('the_content', 'swi_foot_render_inline_shortcodes', 11);
?>