595 lines
21 KiB
PHP
595 lines
21 KiB
PHP
<?php
|
|
/*
|
|
Plugin Name: Live Rugby Union Sportspress Scoreboard
|
|
Plugin URI: http://wattsyproductions.co.uk:23000/Chris/ScoreboardPlugin
|
|
Description: Adds live scoreboard for Rugby Union events
|
|
Version: 1.0
|
|
Author: Chris Watts
|
|
*/
|
|
|
|
class LiveScoreboardShortcode {
|
|
|
|
public function __construct() {
|
|
add_shortcode('live_scoreboard', array($this, 'render_scoreboard'));
|
|
add_shortcode('live_fixtures', array($this, 'render_live_fixtures'));
|
|
|
|
// Public endpoints (no auth required)
|
|
add_action('wp_ajax_get_scoreboard_data', array($this, 'ajax_get_scoreboard_data'));
|
|
add_action('wp_ajax_nopriv_get_scoreboard_data', array($this, 'ajax_get_scoreboard_data'));
|
|
add_action('wp_ajax_get_live_fixtures', array($this, 'ajax_get_live_fixtures'));
|
|
add_action('wp_ajax_nopriv_get_live_fixtures', array($this, 'ajax_get_live_fixtures'));
|
|
|
|
// Admin-only endpoints
|
|
add_action('wp_ajax_update_scoreboard_data', array($this, 'ajax_update_scoreboard_data'));
|
|
add_action('wp_ajax_add_team_event', array($this, 'ajax_add_team_event'));
|
|
add_action('wp_ajax_delete_event', array($this, 'ajax_delete_event'));
|
|
add_action('wp_ajax_update_game_state', array($this, 'ajax_update_game_state'));
|
|
}
|
|
|
|
/**
|
|
* Render single scoreboard shortcode
|
|
* Usage: [live_scoreboard event_id="123" auto_update="true" update_interval="5000"]
|
|
*/
|
|
public function render_scoreboard($atts) {
|
|
$atts = shortcode_atts(array(
|
|
'event_id' => '',
|
|
'auto_update' => 'true',
|
|
'update_interval' => '5000',
|
|
'width' => '100%',
|
|
'height' => 'auto',
|
|
'show_controls' => 'false'
|
|
), $atts);
|
|
|
|
if (empty($atts['event_id'])) {
|
|
return '<div class="scoreboard-error">Error: Event ID is required for the scoreboard.</div>';
|
|
}
|
|
|
|
$event = get_post($atts['event_id']);
|
|
if (!$event || $event->post_type !== 'sp_event') {
|
|
return '<div class="scoreboard-error">Error: Invalid event ID or not a SportsPress event.</div>';
|
|
}
|
|
|
|
$this->enqueue_scoreboard_assets($atts);
|
|
$container_id = 'scoreboard-' . $atts['event_id'] . '-' . uniqid();
|
|
|
|
return sprintf(
|
|
'<div id="%s" class="live-scoreboard-container" data-event-id="%s" data-mode="single" data-show-controls="%s" style="width: %s; height: %s;"></div>',
|
|
esc_attr($container_id),
|
|
esc_attr($atts['event_id']),
|
|
esc_attr($atts['show_controls']),
|
|
esc_attr($atts['width']),
|
|
esc_attr($atts['height'])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Render live fixtures shortcode (all active matches)
|
|
* Usage: [live_fixtures auto_update="true" update_interval="10000" max_fixtures="5"]
|
|
*/
|
|
public function render_live_fixtures($atts) {
|
|
$atts = shortcode_atts(array(
|
|
'auto_update' => 'true',
|
|
'update_interval' => '10000',
|
|
'max_fixtures' => '10',
|
|
'width' => '100%',
|
|
'show_finished' => 'false'
|
|
), $atts);
|
|
|
|
$this->enqueue_scoreboard_assets($atts);
|
|
$container_id = 'live-fixtures-' . uniqid();
|
|
|
|
return sprintf(
|
|
'<div id="%s" class="live-fixtures-container" data-mode="fixtures" data-max-fixtures="%s" data-show-finished="%s" style="width: %s;"></div>',
|
|
esc_attr($container_id),
|
|
esc_attr($atts['max_fixtures']),
|
|
esc_attr($atts['show_finished']),
|
|
esc_attr($atts['width'])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Enqueue necessary scripts and styles
|
|
*/
|
|
private function enqueue_scoreboard_assets($atts) {
|
|
wp_enqueue_script('jquery');
|
|
|
|
wp_enqueue_script(
|
|
'live-scoreboard-js',
|
|
plugin_dir_url(__FILE__) . 'js/live-scoreboard.js',
|
|
array('jquery'),
|
|
'1.0.1',
|
|
true
|
|
);
|
|
|
|
wp_enqueue_style(
|
|
'live-scoreboard-css',
|
|
plugin_dir_url(__FILE__) . 'css/live-scoreboard.css',
|
|
array(),
|
|
'1.0.1'
|
|
);
|
|
|
|
// Localize script with AJAX data
|
|
wp_localize_script('live-scoreboard-js', 'scoreboardConfig', array(
|
|
'ajaxUrl' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('scoreboard_nonce'),
|
|
'eventId' => isset($atts['event_id']) ? $atts['event_id'] : '',
|
|
'autoUpdate' => $atts['auto_update'] === 'true',
|
|
'updateInterval' => intval($atts['update_interval']),
|
|
'maxFixtures' => isset($atts['max_fixtures']) ? intval($atts['max_fixtures']) : 10,
|
|
'showFinished' => isset($atts['show_finished']) ? ($atts['show_finished'] === 'true') : false,
|
|
'showControls' => isset($atts['show_controls']) ? ($atts['show_controls'] === 'true') : false,
|
|
'canEdit' => current_user_can('manage_scoreboard')
|
|
));
|
|
}
|
|
|
|
/**
|
|
* AJAX handler to get live fixtures data
|
|
*/
|
|
public function ajax_get_live_fixtures() {
|
|
$max_fixtures = isset($_POST['max_fixtures']) ? intval($_POST['max_fixtures']) : 10;
|
|
$show_finished = isset($_POST['show_finished']) ? (bool)$_POST['show_finished'] : false;
|
|
|
|
$fixtures_data = $this->get_live_fixtures_data($max_fixtures, $show_finished);
|
|
|
|
if ($fixtures_data !== false) {
|
|
wp_send_json_success($fixtures_data);
|
|
} else {
|
|
wp_send_json_error('Failed to retrieve fixtures data');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* AJAX handler to get single scoreboard data
|
|
*/
|
|
public function ajax_get_scoreboard_data() {
|
|
$event_id = intval($_POST['event_id']);
|
|
$last_update = isset($_POST['last_update']) ? intval($_POST['last_update']) : 0;
|
|
|
|
if (!$event_id) {
|
|
wp_send_json_error('Invalid event ID');
|
|
}
|
|
|
|
$scoreboard_data = $this->get_scoreboard_data($event_id);
|
|
|
|
if ($scoreboard_data) {
|
|
wp_send_json_success($scoreboard_data);
|
|
} else {
|
|
wp_send_json_error('Failed to retrieve scoreboard data');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get live fixtures data
|
|
*/
|
|
private function get_live_fixtures_data($max_fixtures = 10, $show_finished = false) {
|
|
// Query for SportsPress events
|
|
$meta_query = array(
|
|
array(
|
|
'key' => '_scoreboard_data',
|
|
'compare' => 'EXISTS'
|
|
)
|
|
);
|
|
|
|
if (!$show_finished) {
|
|
$meta_query[] = array(
|
|
'key' => '_game_state',
|
|
'value' => 'Finished',
|
|
'compare' => '!='
|
|
);
|
|
}
|
|
|
|
$events = get_posts(array(
|
|
'post_type' => 'sp_event',
|
|
'posts_per_page' => $max_fixtures,
|
|
'meta_query' => $meta_query,
|
|
'orderby' => 'meta_value',
|
|
'meta_key' => '_scoreboard_last_update',
|
|
'order' => 'DESC'
|
|
));
|
|
|
|
$fixtures = array();
|
|
foreach ($events as $event) {
|
|
$scoreboard_data = $this->get_scoreboard_data($event->ID);
|
|
if ($scoreboard_data && ($show_finished || $scoreboard_data['game_state'] !== 'Finished')) {
|
|
$scoreboard_data['event_id'] = $event->ID;
|
|
$scoreboard_data['event_title'] = $event->post_title;
|
|
$fixtures[] = $scoreboard_data;
|
|
}
|
|
}
|
|
|
|
return array(
|
|
'fixtures' => $fixtures,
|
|
'timestamp' => time(),
|
|
'total_count' => count($fixtures)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get scoreboard data from event metadata
|
|
*/
|
|
private function get_scoreboard_data($event_id) {
|
|
$event = get_post($event_id);
|
|
if (!$event) {
|
|
return false;
|
|
}
|
|
|
|
// Get SportsPress event data
|
|
$teams = get_post_meta($event_id, 'sp_team', false);
|
|
$results = get_post_meta($event_id, 'sp_results', true);
|
|
|
|
// Get custom scoreboard metadata
|
|
$scoreboard_meta = get_post_meta($event_id, '_scoreboard_data', true);
|
|
if (!is_array($scoreboard_meta)) {
|
|
$scoreboard_meta = array();
|
|
}
|
|
|
|
// Get game state and events
|
|
$game_state = get_post_meta($event_id, '_game_state', true) ?: 'WaitingForStart';
|
|
$event_list = get_post_meta($event_id, '_match_events', true);
|
|
if (!is_array($event_list)) {
|
|
$event_list = array();
|
|
}
|
|
|
|
// Default scoreboard data structure
|
|
$default_data = array(
|
|
'game_on' => false,
|
|
'game_state' => $game_state,
|
|
'timer_value' => '00:00',
|
|
'half_indicator' => $this->get_half_indicator($game_state),
|
|
'home_team_points' => 0,
|
|
'away_team_points' => 0,
|
|
'event_list' => $event_list,
|
|
'timestamp' => time()
|
|
);
|
|
|
|
// Merge with saved metadata
|
|
$scoreboard_data = array_merge($default_data, $scoreboard_meta);
|
|
|
|
// Calculate if game is on
|
|
$scoreboard_data['game_on'] = in_array($game_state, array('FirstHalf', 'SecondHalf', 'TimeOff'));
|
|
|
|
// Get team information from SportsPress
|
|
if (!empty($teams)) {
|
|
$home_team_id = isset($teams[0]) ? $teams[0] : null;
|
|
$away_team_id = isset($teams[1]) ? $teams[1] : null;
|
|
|
|
if ($home_team_id) {
|
|
$scoreboard_data['home_team_name'] = get_the_title($home_team_id);
|
|
$scoreboard_data['home_team_logo'] = $this->get_team_logo($home_team_id);
|
|
$scoreboard_data['home_team_color'] = get_post_meta($home_team_id, '_team_color', true) ?: '#1e3c72';
|
|
}
|
|
|
|
if ($away_team_id) {
|
|
$scoreboard_data['away_team_name'] = get_the_title($away_team_id);
|
|
$scoreboard_data['away_team_logo'] = $this->get_team_logo($away_team_id);
|
|
$scoreboard_data['away_team_color'] = get_post_meta($away_team_id, '_team_color', true) ?: '#2a5298';
|
|
}
|
|
}
|
|
|
|
// Calculate points from events
|
|
$scores = $this->calculate_scores_from_events($event_list);
|
|
$scoreboard_data['home_team_points'] = $scores['home'];
|
|
$scoreboard_data['away_team_points'] = $scores['away'];
|
|
|
|
return $scoreboard_data;
|
|
}
|
|
|
|
/**
|
|
* Calculate scores from match events
|
|
*/
|
|
private function calculate_scores_from_events($events) {
|
|
$scores = array('home' => 0, 'away' => 0);
|
|
|
|
foreach ($events as $event) {
|
|
if (!isset($event['category']) || !isset($event['eventType'])) {
|
|
continue;
|
|
}
|
|
|
|
$team = strtolower($event['category']);
|
|
if (!in_array($team, array('home', 'away'))) {
|
|
continue;
|
|
}
|
|
|
|
switch ($event['eventType']) {
|
|
case 'Try':
|
|
$scores[$team] += 5;
|
|
break;
|
|
case 'Conversion':
|
|
$scores[$team] += 2;
|
|
break;
|
|
case 'Penalty':
|
|
case 'DropGoal':
|
|
$scores[$team] += 3;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $scores;
|
|
}
|
|
|
|
/**
|
|
* Get half indicator text based on game state
|
|
*/
|
|
private function get_half_indicator($game_state) {
|
|
switch ($game_state) {
|
|
case 'WaitingForStart':
|
|
return 'Pre-Match';
|
|
case 'FirstHalf':
|
|
return '1st Half';
|
|
case 'HalfTime':
|
|
return 'Half Time';
|
|
case 'SecondHalf':
|
|
return '2nd Half';
|
|
case 'TimeOff':
|
|
return 'Time Off';
|
|
case 'Finished':
|
|
return 'Full Time';
|
|
default:
|
|
return 'Pre-Match';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* AJAX handler to add team event (admin only)
|
|
*/
|
|
public function ajax_add_team_event() {
|
|
if (!current_user_can('manage_scoreboard')) {
|
|
wp_send_json_error('Insufficient permissions');
|
|
}
|
|
|
|
if (!wp_verify_nonce($_POST['nonce'], 'scoreboard_nonce')) {
|
|
wp_send_json_error('Security check failed');
|
|
}
|
|
|
|
$event_id = intval($_POST['event_id']);
|
|
$team = sanitize_text_field($_POST['team']);
|
|
$event_type = sanitize_text_field($_POST['event_type']);
|
|
|
|
if (!$event_id || !$team || !$event_type) {
|
|
wp_send_json_error('Invalid data');
|
|
}
|
|
|
|
$result = $this->add_team_event($event_id, $team, $event_type);
|
|
|
|
if ($result) {
|
|
wp_send_json_success('Event added successfully');
|
|
} else {
|
|
wp_send_json_error('Failed to add event');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add team event to match
|
|
*/
|
|
private function add_team_event($event_id, $team, $event_type) {
|
|
$events = get_post_meta($event_id, '_match_events', true);
|
|
if (!is_array($events)) {
|
|
$events = array();
|
|
}
|
|
|
|
$new_event = array(
|
|
'id' => uniqid(),
|
|
'category' => $team,
|
|
'eventType' => $event_type,
|
|
'timestamp' => time(),
|
|
'minute' => $this->get_current_match_minute($event_id)
|
|
);
|
|
|
|
$events[] = $new_event;
|
|
|
|
$result = update_post_meta($event_id, '_match_events', $events);
|
|
|
|
// Update last modified timestamp
|
|
update_post_meta($event_id, '_scoreboard_last_update', time());
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get current match minute (placeholder - you'd implement based on your timer logic)
|
|
*/
|
|
private function get_current_match_minute($event_id) {
|
|
// This would calculate based on your match start time and current time
|
|
// For now, return a placeholder
|
|
return '00:00';
|
|
}
|
|
|
|
/**
|
|
* AJAX handler to delete event (admin only)
|
|
*/
|
|
public function ajax_delete_event() {
|
|
if (!current_user_can('manage_scoreboard')) {
|
|
wp_send_json_error('Insufficient permissions');
|
|
}
|
|
|
|
if (!wp_verify_nonce($_POST['nonce'], 'scoreboard_nonce')) {
|
|
wp_send_json_error('Security check failed');
|
|
}
|
|
|
|
$event_id = intval($_POST['event_id']);
|
|
$match_event_id = sanitize_text_field($_POST['match_event_id']);
|
|
|
|
if (!$event_id || !$match_event_id) {
|
|
wp_send_json_error('Invalid data');
|
|
}
|
|
|
|
$result = $this->delete_match_event($event_id, $match_event_id);
|
|
|
|
if ($result) {
|
|
wp_send_json_success('Event deleted successfully');
|
|
} else {
|
|
wp_send_json_error('Failed to delete event');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a specific match event
|
|
*/
|
|
private function delete_match_event($event_id, $match_event_id) {
|
|
$events = get_post_meta($event_id, '_match_events', true);
|
|
if (!is_array($events)) {
|
|
return false;
|
|
}
|
|
|
|
$updated_events = array_filter($events, function($event) use ($match_event_id) {
|
|
return $event['id'] !== $match_event_id;
|
|
});
|
|
|
|
$result = update_post_meta($event_id, '_match_events', array_values($updated_events));
|
|
|
|
// Update last modified timestamp
|
|
update_post_meta($event_id, '_scoreboard_last_update', time());
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* AJAX handler to update game state (admin only)
|
|
*/
|
|
public function ajax_update_game_state() {
|
|
if (!current_user_can('manage_scoreboard')) {
|
|
wp_send_json_error('Insufficient permissions');
|
|
}
|
|
|
|
if (!wp_verify_nonce($_POST['nonce'], 'scoreboard_nonce')) {
|
|
wp_send_json_error('Security check failed');
|
|
}
|
|
|
|
$event_id = intval($_POST['event_id']);
|
|
$new_state = sanitize_text_field($_POST['game_state']);
|
|
|
|
if (!$event_id || !$new_state) {
|
|
wp_send_json_error('Invalid data');
|
|
}
|
|
|
|
$result = update_post_meta($event_id, '_game_state', $new_state);
|
|
update_post_meta($event_id, '_scoreboard_last_update', time());
|
|
|
|
if ($result) {
|
|
wp_send_json_success('Game state updated successfully');
|
|
} else {
|
|
wp_send_json_error('Failed to update game state');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get team logo URL
|
|
*/
|
|
private function get_team_logo($team_id) {
|
|
$logo_id = get_post_thumbnail_id($team_id);
|
|
if ($logo_id) {
|
|
$logo_url = wp_get_attachment_image_url($logo_id, 'thumbnail');
|
|
return $logo_url ?: 'https://via.placeholder.com/60x60/cccccc/ffffff?text=T';
|
|
}
|
|
return 'https://via.placeholder.com/60x60/cccccc/ffffff?text=T';
|
|
}
|
|
}
|
|
|
|
// Initialize the shortcode
|
|
new LiveScoreboardShortcode();
|
|
|
|
/**
|
|
* Add scoreboard control meta box for events (admin only)
|
|
*/
|
|
add_action('add_meta_boxes', 'add_scoreboard_control_meta_box');
|
|
|
|
function add_scoreboard_control_meta_box() {
|
|
if (current_user_can('manage_scoreboard')) {
|
|
add_meta_box(
|
|
'scoreboard-controls',
|
|
'Live Scoreboard Controls',
|
|
'render_scoreboard_control_meta_box',
|
|
'sp_event',
|
|
'side',
|
|
'high'
|
|
);
|
|
}
|
|
}
|
|
|
|
function render_scoreboard_control_meta_box($post) {
|
|
$scoreboard_data = get_post_meta($post->ID, '_scoreboard_data', true);
|
|
$game_state = get_post_meta($post->ID, '_game_state', true) ?: 'WaitingForStart';
|
|
|
|
if (!is_array($scoreboard_data)) {
|
|
$scoreboard_data = array(
|
|
'timer_value' => '00:00'
|
|
);
|
|
}
|
|
|
|
wp_nonce_field('scoreboard_meta_box', 'scoreboard_meta_box_nonce');
|
|
?>
|
|
<div id="scoreboard-controls">
|
|
<p>
|
|
<label>Game State:</label>
|
|
<select name="game_state">
|
|
<option value="WaitingForStart" <?php selected($game_state, 'WaitingForStart'); ?>>Waiting for Start</option>
|
|
<option value="FirstHalf" <?php selected($game_state, 'FirstHalf'); ?>>First Half</option>
|
|
<option value="HalfTime" <?php selected($game_state, 'HalfTime'); ?>>Half Time</option>
|
|
<option value="SecondHalf" <?php selected($game_state, 'SecondHalf'); ?>>Second Half</option>
|
|
<option value="TimeOff" <?php selected($game_state, 'TimeOff'); ?>>Time Off</option>
|
|
<option value="Finished" <?php selected($game_state, 'Finished'); ?>>Finished</option>
|
|
</select>
|
|
</p>
|
|
<p>
|
|
<label>Timer:</label>
|
|
<input type="text" name="timer_value" value="<?php echo esc_attr($scoreboard_data['timer_value']); ?>" placeholder="00:00">
|
|
</p>
|
|
|
|
<div id="live-scoreboard-preview" data-event-id="<?php echo $post->ID; ?>">
|
|
<!-- Live preview will be loaded here -->
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
// Initialize live preview in admin
|
|
if (typeof scoreboardConfig !== 'undefined') {
|
|
const preview = new LiveScoreboard($('#live-scoreboard-preview')[0], {
|
|
eventId: <?php echo $post->ID; ?>,
|
|
ajaxUrl: '<?php echo admin_url('admin-ajax.php'); ?>',
|
|
nonce: '<?php echo wp_create_nonce('scoreboard_nonce'); ?>',
|
|
autoUpdate: true,
|
|
updateInterval: 5000
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
// Save scoreboard meta box data
|
|
add_action('save_post', 'save_scoreboard_control_meta_box', 10, 2);
|
|
|
|
function save_scoreboard_control_meta_box($post_id, $post) {
|
|
if ($post->post_type !== 'sp_event') {
|
|
return;
|
|
}
|
|
|
|
if (!isset($_POST['scoreboard_meta_box_nonce']) || !wp_verify_nonce($_POST['scoreboard_meta_box_nonce'], 'scoreboard_meta_box')) {
|
|
return;
|
|
}
|
|
|
|
if (!current_user_can('edit_post', $post_id)) {
|
|
return;
|
|
}
|
|
|
|
$scoreboard_data = array(
|
|
'timer_value' => sanitize_text_field($_POST['timer_value']),
|
|
'timestamp' => time()
|
|
);
|
|
|
|
$game_state = sanitize_text_field($_POST['game_state']);
|
|
|
|
update_post_meta($post_id, '_scoreboard_data', $scoreboard_data);
|
|
update_post_meta($post_id, '_game_state', $game_state);
|
|
update_post_meta($post_id, '_scoreboard_last_update', time());
|
|
}
|
|
function add_scoreboard_capability() {
|
|
$editor = get_role('editor');
|
|
$editor->add_cap('manage_scoreboard');
|
|
|
|
$admin = get_role('administrator');
|
|
$admin->add_cap('manage_scoreboard');
|
|
}
|
|
add_action('init', 'add_scoreboard_capability');
|
|
?>
|