wp-plugin-swiss-football-ma.../includes/class-swi-foot-admin.php
2026-03-27 13:59:28 +01:00

484 lines
24 KiB
PHP

<?php
class Swi_Foot_Admin
{
public function __construct()
{
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'settings_init'));
add_action('admin_enqueue_scripts', array($this, 'admin_scripts'));
add_action('add_meta_boxes', array($this, 'add_match_meta_boxes'));
// Finished-match deletion moved to REST endpoints (see includes/class-swi-foot-rest.php)
}
public function add_admin_menu()
{
add_options_page(
__('Swiss Football Matchdata Settings', 'swi_foot_matchdata'),
__('Swiss Football', 'swi_foot_matchdata'),
'manage_options',
'swiss-football-matchdata',
array($this, 'options_page')
);
}
public function settings_init()
{
register_setting('swi_foot_settings', 'swi_foot_api_base_url');
register_setting('swi_foot_settings', 'swi_foot_api_username');
register_setting('swi_foot_settings', 'swi_foot_api_password');
register_setting('swi_foot_settings', 'swi_foot_verein_id');
register_setting('swi_foot_settings', 'swi_foot_season_id');
register_setting('swi_foot_settings', 'swi_foot_match_cache_duration');
register_setting('swi_foot_settings', 'swi_foot_api_language');
add_settings_section(
'swi_foot_api_section',
__('API Configuration', 'swi_foot_matchdata'),
array($this, 'api_section_callback'),
'swi_foot_settings'
);
add_settings_field('swi_foot_api_base_url', __('API Base URL', 'swi_foot_matchdata'), array($this, 'base_url_render'), 'swi_foot_settings', 'swi_foot_api_section');
add_settings_field('swi_foot_api_username', __('API Username (Application Key)', 'swi_foot_matchdata'), array($this, 'username_render'), 'swi_foot_settings', 'swi_foot_api_section');
add_settings_field('swi_foot_api_password', __('API Password (Application Pass)', 'swi_foot_matchdata'), array($this, 'password_render'), 'swi_foot_settings', 'swi_foot_api_section');
add_settings_field('swi_foot_verein_id', __('Verein ID (Club ID)', 'swi_foot_matchdata'), array($this, 'verein_id_render'), 'swi_foot_settings', 'swi_foot_api_section');
add_settings_field('swi_foot_season_id', __('Season ID', 'swi_foot_matchdata'), array($this, 'season_id_render'), 'swi_foot_settings', 'swi_foot_api_section');
add_settings_field('swi_foot_api_language', __('API Response Language', 'swi_foot_matchdata'), array($this, 'language_render'), 'swi_foot_settings', 'swi_foot_api_section');
add_settings_section(
'swi_foot_cache_section',
__('Cache Settings', 'swi_foot_matchdata'),
array($this, 'cache_section_callback'),
'swi_foot_settings'
);
add_settings_field('swi_foot_match_cache_duration', __('Match Data Cache Duration (seconds)', 'swi_foot_matchdata'), array($this, 'cache_duration_render'), 'swi_foot_settings', 'swi_foot_cache_section');
}
public function api_section_callback()
{
echo '<p>' . __('Configure your Swiss Football API credentials here.', 'swi_foot_matchdata') . '</p>';
}
public function cache_section_callback()
{
echo '<p>' . __('Configure caching settings for match data.', 'swi_foot_matchdata') . '</p>';
}
public function base_url_render()
{
$base_url = get_option('swi_foot_api_base_url', 'https://stg-club-api-services.football.ch');
echo '<input type="url" name="swi_foot_api_base_url" value="' . esc_attr($base_url) . '" class="regular-text" />';
echo '<p class="description">' . __('The base URL for the Swiss Football API', 'swi_foot_matchdata') . '</p>';
}
public function username_render()
{
$username = get_option('swi_foot_api_username');
echo '<input type="text" name="swi_foot_api_username" value="' . esc_attr($username) . '" class="regular-text" />';
echo '<p class="description">' . __('Your API application key', 'swi_foot_matchdata') . '</p>';
}
public function password_render()
{
$password = get_option('swi_foot_api_password');
echo '<input type="password" name="swi_foot_api_password" value="' . esc_attr($password) . '" class="regular-text" />';
echo '<p class="description">' . __('Your API application password', 'swi_foot_matchdata') . '</p>';
}
public function verein_id_render()
{
$verein_id = get_option('swi_foot_verein_id');
echo '<input type="number" name="swi_foot_verein_id" value="' . esc_attr($verein_id) . '" class="regular-text" />';
echo '<p class="description">' . __('Enter your club\'s Verein ID (Club ID)', 'swi_foot_matchdata') . '</p>';
}
public function season_id_render()
{
$season_id = get_option('swi_foot_season_id', date('Y'));
echo '<input type="number" name="swi_foot_season_id" value="' . esc_attr($season_id) . '" class="regular-text" min="2020" max="2030" />';
echo '<p class="description">' . __('Current season ID (usually the year)', 'swi_foot_matchdata') . '</p>';
}
public function cache_duration_render()
{
$duration = get_option('swi_foot_match_cache_duration', 30);
echo '<input type="number" name="swi_foot_match_cache_duration" value="' . esc_attr($duration) . '" min="10" max="300" />';
echo '<p class="description">' . __('How long to cache match data in seconds (10-300)', 'swi_foot_matchdata') . '</p>';
}
public function language_render()
{
$language = get_option('swi_foot_api_language', '1');
echo '<select name="swi_foot_api_language">';
echo '<option value="1" ' . selected($language, '1', false) . '>' . __('German', 'swi_foot_matchdata') . '</option>';
echo '<option value="2" ' . selected($language, '2', false) . '>' . __('French', 'swi_foot_matchdata') . '</option>';
echo '<option value="3" ' . selected($language, '3', false) . '>' . __('Italian', 'swi_foot_matchdata') . '</option>';
echo '</select>';
echo '<p class="description">' . __('Select the language for API responses (German, French, or Italian)', 'swi_foot_matchdata') . '</p>';
}
public function admin_scripts($hook)
{
if ($hook === 'settings_page_swiss-football-matchdata') {
wp_enqueue_script('swi-foot-admin', SWI_FOOT_PLUGIN_URL . 'assets/admin.js', array('jquery'), SWI_FOOT_PLUGIN_VERSION, true);
wp_localize_script('swi-foot-admin', 'swi_foot_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('swi_foot_nonce'),
'rest_url' => esc_url_raw(rest_url('swi-foot/v1')),
'rest_nonce' => wp_create_nonce('wp_rest')
));
wp_enqueue_style('swi-foot-admin', SWI_FOOT_PLUGIN_URL . 'assets/admin.css', array(), SWI_FOOT_PLUGIN_VERSION);
}
// Add shortcode help to post/page editors
global $pagenow;
if (in_array($pagenow, array('post.php', 'post-new.php', 'edit.php'))) {
// Enqueue the registered editor bundle so WordPress picks the built asset when available.
// The script handle `swi-foot-editor-blocks` is registered in `register_blocks()`.
wp_enqueue_script('swi-foot-editor-blocks');
// Add admin footer debug output to help diagnose missing script tags.
add_action('admin_footer', array($this, 'print_editor_script_debug'));
// Post context editor panel: allow editor to set per-post season/team/match
wp_enqueue_script('swi-foot-post-context', SWI_FOOT_PLUGIN_URL . 'assets/post-context.js', array('wp-plugins','wp-edit-post','wp-element','wp-data','wp-components'), SWI_FOOT_PLUGIN_VERSION, true);
wp_localize_script('swi-foot-post-context', 'swi_foot_post_context', array(
'rest_url' => esc_url_raw(rest_url('swi-foot/v1')),
'rest_nonce' => wp_create_nonce('wp_rest'),
'default_season' => get_option('swi_foot_season_id', date('Y'))
));
wp_enqueue_style('swi-foot-admin', SWI_FOOT_PLUGIN_URL . 'assets/admin.css', array(), SWI_FOOT_PLUGIN_VERSION);
}
}
public function print_editor_script_debug()
{
// Only show on post editor pages
$pagenow = isset($GLOBALS['pagenow']) ? $GLOBALS['pagenow'] : '';
if (!in_array($pagenow, array('post.php', 'post-new.php', 'edit.php'))) return;
// Check registration/enqueue status and attempt to find the resolved src
$registered = false;
$enqueued = false;
$src = '';
global $wp_scripts;
if (isset($wp_scripts) && is_object($wp_scripts)) {
$registered = wp_script_is('swi-foot-editor-blocks', 'registered');
$enqueued = wp_script_is('swi-foot-editor-blocks', 'enqueued');
$handle = $wp_scripts->query('swi-foot-editor-blocks', 'registered');
if ($handle && isset($wp_scripts->registered['swi-foot-editor-blocks']->src)) {
$src = $wp_scripts->registered['swi-foot-editor-blocks']->src;
}
}
$msg = array(
'registered' => $registered ? 'yes' : 'no',
'enqueued' => $enqueued ? 'yes' : 'no',
'src' => $src
);
echo "<script>console.debug('swi-foot: editor script debug', ". json_encode($msg) .");</script>";
}
public function add_match_meta_boxes()
{
add_meta_box(
'swi-foot-shortcodes',
__('Swiss Football Shortcodes', 'swi_foot_matchdata'),
array($this, 'shortcodes_meta_box'),
array('post', 'page'),
'side',
'default'
);
}
public function shortcodes_meta_box($post)
{
?>
<div class="swi-foot-shortcodes-help">
<h4><?php _e('Available Shortcodes:', 'swi_foot_matchdata'); ?></h4>
<div class="shortcode-group">
<strong><?php _e('Full Match Display:', 'swi_foot_matchdata'); ?></strong>
<code data-shortcode="[swi_foot_match match_id=&quot;12345&quot;]">[swi_foot_match match_id="12345"]</code>
<code data-shortcode="[swi_foot_match team_id=&quot;67&quot; show_next=&quot;true&quot;]">[swi_foot_match team_id="67" show_next="true"]</code>
</div>
<div class="shortcode-group">
<strong><?php _e('Individual Match Elements:', 'swi_foot_matchdata'); ?></strong>
<code data-shortcode="[swi_foot_match_home_team match_id=&quot;12345&quot;]">[swi_foot_match_home_team match_id="12345"]</code>
<code data-shortcode="[swi_foot_match_away_team match_id=&quot;12345&quot;]">[swi_foot_match_away_team match_id="12345"]</code>
<code data-shortcode="[swi_foot_match_date match_id=&quot;12345&quot; format=&quot;d.m.Y&quot;]">[swi_foot_match_date match_id="12345" format="d.m.Y"]</code>
<code data-shortcode="[swi_foot_match_time match_id=&quot;12345&quot; format=&quot;H:i&quot;]">[swi_foot_match_time match_id="12345" format="H:i"]</code>
<code data-shortcode="[swi_foot_match_venue match_id=&quot;12345&quot;]">[swi_foot_match_venue match_id="12345"]</code>
<code data-shortcode="[swi_foot_match_score match_id=&quot;12345&quot; separator=&quot;:&quot;]">[swi_foot_match_score match_id="12345" separator=":"]</code>
<code data-shortcode="[swi_foot_match_status match_id=&quot;12345&quot;]">[swi_foot_match_status match_id="12345"]</code>
<code data-shortcode="[swi_foot_match_league match_id=&quot;12345&quot;]">[swi_foot_match_league match_id="12345"]</code>
<code data-shortcode="[swi_foot_match_round match_id=&quot;12345&quot;]">[swi_foot_match_round match_id="12345"]</code>
</div>
<div class="shortcode-group">
<strong><?php _e('Parameters:', 'swi_foot_matchdata'); ?></strong>
<ul>
<li><code>match_id</code> - Specific match ID</li>
<li><code>team_id</code> - Team ID (for next match)</li>
<li><code>show_next</code> - Show next match (true/false)</li>
<li><code>format</code> - Date/time format</li>
<li><code>separator</code> - Score separator</li>
</ul>
</div>
<p><em><?php _e('Click any shortcode above to copy it to clipboard', 'swi_foot_matchdata'); ?></em></p>
</div>
<script>
jQuery(document).ready(function($) {
$('.swi-foot-shortcodes-help code[data-shortcode]').css('cursor', 'pointer').on('click', function() {
var shortcode = $(this).data('shortcode');
if (navigator.clipboard) {
navigator.clipboard.writeText(shortcode).then(function() {
alert('<?php _e('Shortcode copied to clipboard!', 'swi_foot_matchdata'); ?>');
});
} else {
// Use native clipboard API
var textArea = document.createElement('textarea');
textArea.value = shortcode;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
alert('<?php _e('Shortcode copied to clipboard!', 'swi_foot_matchdata'); ?>');
}
});
});
</script>
<?php
}
public function options_page()
{
?>
<div class="wrap">
<h1><?php _e('Swiss Football Matchdata Settings', 'swi_foot_matchdata'); ?></h1>
<form action="options.php" method="post" id="swi-foot-settings-form">
<?php
settings_fields('swi_foot_settings');
do_settings_sections('swi_foot_settings');
?>
<div class="swi-foot-form-actions">
<button type="submit" name="swi_foot_action" value="save_and_test" class="button button-primary">
<?php _e('Save and Test', 'swi_foot_matchdata'); ?>
</button>
<button type="submit" name="swi_foot_action" value="save_only" class="button">
<?php _e('Save without Test', 'swi_foot_matchdata'); ?>
</button>
</div>
<span id="connection-status"></span>
</form>
<div class="swi-foot-admin-section">
<h2><?php _e('Team Management', 'swi_foot_matchdata'); ?></h2>
<p>
<button type="button" id="refresh-teams" class="button">
<?php _e('Refresh Teams List', 'swi_foot_matchdata'); ?>
</button>
<span id="refresh-status"></span>
</p>
<div id="teams-list">
<?php $this->display_teams(); ?>
</div>
</div>
<div class="swi-foot-admin-section">
<h2><?php _e('Cache Management', 'swi_foot_matchdata'); ?></h2>
<p>
<button type="button" id="clear-cache" class="button">
<?php _e('Clear Match Data Cache', 'swi_foot_matchdata'); ?>
</button>
<span id="cache-status"></span>
</p>
<?php $this->display_cache_info(); ?>
</div>
<div class="swi-foot-admin-section">
<h2><?php _e('Finished Matches Data', 'swi_foot_matchdata'); ?></h2>
<?php $this->display_finished_matches(); ?>
</div>
<div class="swi-foot-admin-section">
<h2><?php _e('Quick Shortcode Reference', 'swi_foot_matchdata'); ?></h2>
<div class="shortcode-quick-ref">
<div class="shortcode-examples">
<h4><?php _e('Common Examples:', 'swi_foot_matchdata'); ?></h4>
<p><strong><?php _e('Display full match info:', 'swi_foot_matchdata'); ?></strong><br>
<code>[swi_foot_match match_id="12345"]</code>
</p>
<p><strong><?php _e('Show next match for a team:', 'swi_foot_matchdata'); ?></strong><br>
<code>[swi_foot_match team_id="67" show_next="true"]</code>
</p>
<p><strong><?php _e('Individual elements in text:', 'swi_foot_matchdata'); ?></strong><br>
<?php _e('The match between', 'swi_foot_matchdata'); ?> <code>[swi_foot_match_home_team match_id="12345"]</code> <?php _e('and', 'swi_foot_matchdata'); ?> <code>[swi_foot_match_away_team match_id="12345"]</code> <?php _e('is on', 'swi_foot_matchdata'); ?> <code>[swi_foot_match_date match_id="12345"]</code></p>
</div>
</div>
</div>
</div>
<?php
}
private function display_teams()
{
$api = new Swi_Foot_API();
$teams = $api->get_teams();
if (is_wp_error($teams)) {
echo '<p class="notice notice-error">' .
sprintf(
/* translators: %s is the error message from the API */
__('Error loading teams: %s', 'swi_foot_matchdata'),
$teams->get_error_message()
) .
'</p>';
return;
}
if (empty($teams)) {
echo '<p class="notice notice-warning">' .
__('No teams found. Please check your API configuration.', 'swi_foot_matchdata') .
'</p>';
return;
}
echo '<h3>' . __('Available Teams:', 'swi_foot_matchdata') . '</h3>';
echo '<div class="swi-foot-teams-grid">';
foreach ($teams as $team) {
echo '<div class="team-card">';
echo '<strong>' . esc_html($team['teamName'] ?? 'Unknown Team') . '</strong><br>';
echo '<small>ID: ' . esc_html($team['teamId'] ?? 'N/A') . '</small><br>';
if (!empty($team['teamLeagueName'])) {
echo '<small>' . esc_html($team['teamLeagueName']) . '</small>';
}
echo '</div>';
}
echo '</div>';
}
private function display_cache_info()
{
$keys = get_transient('swi_foot_match_keys');
$cache_count = is_array($keys) ? count($keys) : 0;
$cache_duration = get_option('swi_foot_match_cache_duration', 30);
echo '<p>' . sprintf(
/* translators: %1$d is the number of cached match records, %2$d is the cache duration in seconds */
__('Currently caching %1$d match records with %2$d second cache duration.', 'swi_foot_matchdata'),
$cache_count,
$cache_duration
) . '</p>';
if ($cache_count > 0) {
$timestamps = array();
foreach ($keys as $k) {
$payload = get_transient('swi_foot_match_' . $k);
if (is_array($payload) && isset($payload['cached_at'])) {
$timestamps[] = (int) $payload['cached_at'];
}
}
if (!empty($timestamps)) {
$oldest_timestamp = min($timestamps);
$newest_timestamp = max($timestamps);
echo '<p><strong>' . __('Cache Range:', 'swi_foot_matchdata') . '</strong><br>';
echo __('Oldest:', 'swi_foot_matchdata') . ' ' . date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $oldest_timestamp) . '<br>';
echo __('Newest:', 'swi_foot_matchdata') . ' ' . date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $newest_timestamp) . '</p>';
}
}
}
private function display_finished_matches()
{
$finished = get_option('swi_foot_finished_matches', array());
if (empty($finished)) {
echo '<p>' . __('No finished match data stored.', 'swi_foot_matchdata') . '</p>';
return;
}
echo '<table class="widefat fixed striped">';
echo '<thead>
<tr>
<th>' . __('Match ID', 'swi_foot_matchdata') . '</th>
<th>' . __('Saved At', 'swi_foot_matchdata') . '</th>
<th>' . __('Players', 'swi_foot_matchdata') . '</th>
<th>' . __('Bench', 'swi_foot_matchdata') . '</th>
<th>' . __('Events', 'swi_foot_matchdata') . '</th>
<th></th>
</tr>
</thead><tbody>';
foreach ($finished as $mid => $item) {
$players = count($item['roster']['players'] ?? array());
$bench = count($item['roster']['bench'] ?? array());
$events = count($item['events'] ?? array());
$saved = !empty($item['saved_at']) ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $item['saved_at']) : '-';
echo '<tr>
<td>' . esc_html($mid) . '</td>
<td>' . esc_html($saved) . '</td>
<td>' . esc_html($players) . '</td>
<td>' . esc_html($bench) . '</td>
<td>' . esc_html($events) . '</td>
<td>
<button class="button swi-foot-clear-finished" data-id="' . esc_attr($mid) . '">' . __('Delete', 'swi_foot_matchdata') . '</button>
</td>
</tr>';
}
echo '</tbody></table>';
echo '<p><button id="swi-foot-clear-all-finished" class="button button-secondary">' . __('Clear All Finished Matches', 'swi_foot_matchdata') . '</button></p>';
// Include inline JS for actions
?>
<script>
jQuery(function($) {
$('.swi-foot-clear-finished').on('click', function() {
if (!confirm('<?php _e('Delete this finished match data?', 'swi_foot_matchdata'); ?>')) return;
var btn = $(this);
var matchId = btn.data('id');
fetch(swi_foot_ajax.rest_url.replace(/\/$/, '') + '/admin/finished/' + encodeURIComponent(matchId), {
method: 'DELETE',
credentials: 'same-origin',
headers: { 'X-WP-Nonce': swi_foot_ajax.rest_nonce }
}).then(function(resp) { return resp.json(); }).then(function(resp) {
if (resp && resp.success) {
btn.closest('tr').fadeOut();
} else {
alert(resp.error || 'Error');
}
}).catch(function() { alert('Network error'); });
});
$('#swi-foot-clear-all-finished').on('click', function() {
if (!confirm('<?php _e('Clear all finished match data?', 'swi_foot_matchdata'); ?>')) return;
fetch(swi_foot_ajax.rest_url.replace(/\/$/, '') + '/admin/finished', {
method: 'DELETE',
credentials: 'same-origin',
headers: { 'X-WP-Nonce': swi_foot_ajax.rest_nonce }
}).then(function(resp) { return resp.json(); }).then(function(resp) {
if (resp && resp.success) {
location.reload();
} else {
alert(resp.error || 'Error');
}
}).catch(function() { alert('Network error'); });
});
});
</script>
<?php
}
}
?>