23 Commits

Author SHA1 Message Date
5f0622c052 version v0.1.3
All checks were successful
Release Plugin Zip / release (push) Successful in 4s
2026-02-17 15:33:24 -06:00
e1b3abbfb0 add officials to quick edit menu 2026-02-17 15:32:39 -06:00
2623fbf7f2 Merge branch 'github-main'
All checks were successful
Release Plugin Zip / release (push) Successful in 4s
2026-02-17 15:24:08 -06:00
0696d821b5 version 0.1.2 2026-02-17 15:23:48 -06:00
cb05d53522 add week filter 2026-02-17 15:22:48 -06:00
e0af498a3f add github actions 2026-02-17 15:22:12 -06:00
00f7b7979e add week filter
All checks were successful
Release Plugin Zip / release (push) Successful in 3s
2026-02-17 15:16:40 -06:00
9fbce23828 add github actions 2026-02-17 15:16:37 -06:00
d62b1a746b typo 2025-06-06 12:24:36 -05:00
f59273d788 rename readme as markdwon, update manifest 2025-06-06 12:22:33 -05:00
377cdbffb0 update readme and version 2025-06-06 12:19:40 -05:00
d3f4951fd8 Merge branch 'open-graph-tags' 2025-06-06 12:10:42 -05:00
d2ddf227fd Improve Open Graph metadata for sports events
- Added `asc_generate_sp_event_title()` to format event titles using team names.
- Added `asc_generate_short_date()` for consistent date/time representation.
- Refined title/description to reflect event status (postponed, cancelled, etc.) and venue.
- Handled special outcomes like Technical Forfeit or Forfeit in title.
2025-06-06 12:09:30 -05:00
b4b31f8123 add custom event parse request
For some reason it doesn't run through sportspress' query parser. i can't figure out why, but this is a way around it.
2024-05-20 16:40:08 -05:00
3ecb7ea937 Merge branch 'permalink'
# Conflicts:
#	tonys-sportspress-enhancements.php
2024-05-17 10:28:27 -05:00
78bf5207ab Merge branch 'open-graph-tags' 2024-05-17 10:27:17 -05:00
47433a939c first init 2024-05-17 10:26:42 -05:00
6a373208dd remove spaces from score 2024-05-17 07:19:09 -05:00
a9a6ab3207 remove hook for update post
this was leftover from when i was updating the featured image
2024-05-17 11:14:39 +00:00
8c0b1e3c19 check for image_path exists and set http status header 2024-05-16 16:07:20 -05:00
5361b0da90 Merge branch 'featured-image-generator' into open-graph-tags
# Conflicts:
#	tonys-sportspress-enhancements.php
2024-05-16 14:32:23 -05:00
6427906d15 change to dynamic image generation 2024-05-16 14:25:42 -05:00
534fd666be first attempt 2024-05-15 11:12:24 -05:00
9 changed files with 915 additions and 297 deletions

37
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Release Plugin Zip
on:
push:
tags:
- "v*"
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build plugin zip
id: build
run: |
set -euo pipefail
PLUGIN_SLUG="${GITHUB_REPOSITORY##*/}"
TAG="${GITHUB_REF_NAME}"
ZIP_NAME="${PLUGIN_SLUG}-${TAG}.zip"
mkdir -p dist
git archive --format=zip --prefix="${PLUGIN_SLUG}/" -o "dist/${ZIP_NAME}" HEAD
echo "zip_path=dist/${ZIP_NAME}" >> "$GITHUB_OUTPUT"
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
- name: Create or update release and upload zip
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.build.outputs.zip_path }}
fail_on_unmatched_files: true

View File

@@ -6,50 +6,6 @@ Version: 1.0
Author: Your Name Author: Your Name
*/ */
add_action('save_post_sp_event', 'generate_event_featured_image', 10, 3);
function generate_event_featured_image($post_id, $post) {
// Verify post type
if ($post->post_type !== 'sp_event') return;
// Get associated teams from post meta
$team_ids = get_post_meta($post_id, 'sp_team', false); // false to get an array of values
// Ensure we have exactly two teams
if (count($team_ids) < 2) return;
$team1_id = $team_ids[0];
$team2_id = $team_ids[1];
// Get team colors and logos
$team1_colors = get_post_meta($team1_id, 'sp_colors', true);
$team2_colors = get_post_meta($team2_id, 'sp_colors', true);
$default_color = '#FFFFFF'; // Default color (black)
$team1_color = !empty($team1_colors['primary']) ? $team1_colors['primary'] : $default_color;
$team2_color = !empty($team2_colors['primary']) ? $team2_colors['primary'] : $default_color;
$team1_logo_url = get_the_post_thumbnail_url($team1_id, 'full');
$team2_logo_url = get_the_post_thumbnail_url($team2_id, 'full');
// Check if both team colors are default and both logos are empty
if (($team1_color === $default_color && empty($team1_logo_url)) && ($team2_color === $default_color && empty($team2_logo_url))) {
return; // Do nothing if both teams have no valid color or logo
}
$team1_logo_thumbnail_id = get_post_thumbnail_id($team1_id, 'full');
$team2_logo_thumbnail_id = get_post_thumbnail_id($team2_id, 'full');
$team1_logo = get_attached_file($team1_logo_thumbnail_id);
$team2_logo = get_attached_file($team2_logo_thumbnail_id);
// Generate image
$image = generate_bisected_image($team1_color, $team2_color, $team1_logo, $team2_logo);
// Upload and set as featured image
$attachment_id = upload_image($image, $post_id, $team1_id, $team2_id);
set_post_thumbnail($post_id, $attachment_id);
}
function generate_bisected_image($color1, $color2, $logo1_path, $logo2_path) { function generate_bisected_image($color1, $color2, $logo1_path, $logo2_path) {
$width = 1200; $width = 1200;
$height = 628; $height = 628;
@@ -104,8 +60,8 @@ function generate_bisected_image($color1, $color2, $logo1_path, $logo2_path) {
} }
// Center logo 1 // Center logo 1
$logo1_x = ($width / 4) - ($new_logo1_width / 2); $logo1_x = (int) ($width / 4) - ($new_logo1_width / 2);
$logo1_y = ($height / 2) - ($new_logo1_height / 2); $logo1_y = (int) ($height / 2) - ($new_logo1_height / 2);
imagecopyresampled($image, $logo1, $logo1_x, $logo1_y, 0, 0, $new_logo1_width, $new_logo1_height, $logo1_width, $logo1_height); imagecopyresampled($image, $logo1, $logo1_x, $logo1_y, 0, 0, $new_logo1_width, $new_logo1_height, $logo1_width, $logo1_height);
imagedestroy($logo1); imagedestroy($logo1);
} }
@@ -134,149 +90,118 @@ function generate_bisected_image($color1, $color2, $logo1_path, $logo2_path) {
} }
// Center logo 2 // Center logo 2
$logo2_x = (3 * $width / 4) - ($new_logo2_width / 2); $logo2_x = (int) (3 * $width / 4) - ($new_logo2_width / 2);
$logo2_y = ($height / 2) - ($new_logo2_height / 2); $logo2_y = (int) ($height / 2) - ($new_logo2_height / 2);
imagecopyresampled($image, $logo2, $logo2_x, $logo2_y, 0, 0, $new_logo2_width, $new_logo2_height, $logo2_width, $logo2_height); imagecopyresampled($image, $logo2, $logo2_x, $logo2_y, 0, 0, $new_logo2_width, $new_logo2_height, $logo2_width, $logo2_height);
imagedestroy($logo2); imagedestroy($logo2);
} }
// Start output buffering to capture the image data
ob_start();
imagepng($image); // Output the image as PNG
$image_data = ob_get_clean(); // Get the image data from the buffer
// Save to temp location // Clean up memory
$temp_file = tempnam(sys_get_temp_dir(), 'event_image');
rename($temp_file, $temp_file .= '.png');
$temp_file .= '.png';
imagepng($image, $temp_file);
imagedestroy($image); imagedestroy($image);
return $temp_file; return $image_data;
} }
function debug_to_console($data) { function add_image_generator_endpoint() {
$output = $data; add_rewrite_endpoint('head-to-head', EP_ROOT, true);
if (is_array($output)) }
$output = implode(',', $output); add_action('init', 'add_image_generator_endpoint');
echo "<script>console.log('Debug Objects: " . $output . "' );</script>"; function handle_image_request() {
if (!isset($_GET['post'])) return;
$post_id = $_GET['post'];
$post = get_post($post_id);
// Verify post type
if (!$post && $post->post_type !== 'sp_event') return;
// Get associated teams from post meta
$team_ids = get_post_meta($post_id, 'sp_team', false); // false to get an array of values
// Ensure we have exactly two teams
if (count($team_ids) < 2) return;
$team1_id = $team_ids[0];
$team2_id = $team_ids[1];
$team1 = get_post($team1_id);
$team2 = get_post($team2_id);
$team1_postmodified = strtotime($team1->post_modified);
$team2_postmodified = strtotime($team2->post_modified);
$cache_key = "team_image_{$team1_id}_{$team1_postmodified}-{$team2_id}_{$team2_postmodified}";
$cached_image_path = get_transient($cache_key);
if ($cached_image_path && file_exists($cached_image_path)) {
serve_image($cached_image_path);
exit;
}
// Get team colors and logos
$team1_colors = get_post_meta($team1_id, 'sp_colors', true);
$team2_colors = get_post_meta($team2_id, 'sp_colors', true);
$default_color = '#FFFFFF'; // Default color (black)
$team1_color = !empty($team1_colors['primary']) ? $team1_colors['primary'] : $default_color;
$team2_color = !empty($team2_colors['primary']) ? $team2_colors['primary'] : $default_color;
// Security check for hex color
$team1_color = preg_match('/^#[a-fA-F0-9]{6}$/', $team1_color) ? $team1_color : '#FFFFFF';
$team2_color = preg_match('/^#[a-fA-F0-9]{6}$/', $team2_color) ? $team2_color : '#FFFFFF';
$team1_logo_url = get_the_post_thumbnail_url($team1_id, 'full');
$team2_logo_url = get_the_post_thumbnail_url($team2_id, 'full');
// Check if both team colors are default and both logos are empty
if (($team1_color === $default_color && empty($team1_logo_url)) && ($team2_color === $default_color && empty($team2_logo_url))) {
return; // Do nothing if both teams have no valid color or logo
}
$team1_logo_thumbnail_id = get_post_thumbnail_id($team1_id, 'full');
$team2_logo_thumbnail_id = get_post_thumbnail_id($team2_id, 'full');
$team1_logo = get_attached_file($team1_logo_thumbnail_id);
$team2_logo = get_attached_file($team2_logo_thumbnail_id);
// Generate the image if no valid cache exists
$image_data = generate_bisected_image($team1_color, $team2_color, $team1_logo, $team2_logo);
$image_path = save_image_to_cache($image_data, $cache_key);
set_transient($cache_key, $image_path, DAY_IN_SECONDS * 30); // Cache for 30 days
serve_image($image_path);
exit;
}
add_action('template_redirect', 'handle_image_request');
function serve_image($image_path) {
header('Content-Type: image/png');
if (file_exists($image_path)) {
status_header( 200 );
} else {
status_header( 404 );
die("Image not found.");
}
// Clear all output buffering to prevent any extra output
while (ob_get_level()) {
ob_end_clean();
}
readfile($image_path);
} }
function upload_image($file, $post_id, $team1_id, $team2_id) { function save_image_to_cache($image_data, $cache_key) {
$filename = 'event-' . $team1_id .'-'. $team2_id . '.png'; // Set custom file name $upload_dir = wp_get_upload_dir();
$upload_dir = wp_upload_dir(); $file_path = $upload_dir['path'] . '/' . $cache_key . '.png';
$file_path = $upload_dir['path'] . '/' . $filename;
// Find existing attachments with the same name and delete them // Assuming $image_data is raw image data
$existing_attachments = get_posts(array( file_put_contents($file_path, $image_data);
'post_type' => 'attachment',
'name' => sanitize_file_name($filename),
'posts_per_page' => -1,
'post_status' => 'any'
));
foreach ($existing_attachments as $attachment) {
wp_delete_attachment($attachment->ID, true);
}
$upload_file = wp_upload_bits($filename, null, file_get_contents($file)); return $file_path;
if (!$upload_file['error']) {
$wp_filetype = wp_check_filetype($filename, null);
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_title' => sanitize_file_name($filename),
'post_content' => '',
'post_status' => 'private' // Set the status to private
);
$attachment_id = wp_insert_attachment($attachment, $upload_file['file'], $post_id);
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attach_data = wp_generate_attachment_metadata($attachment_id, $upload_file['file']);
wp_update_attachment_metadata($attachment_id, $attach_data);
return $attachment_id;
}
return 0;
}
// Add menu item to generate images for existing events
add_action('admin_menu', 'add_generate_images_menu');
function add_generate_images_menu() {
add_submenu_page(
'tools.php',
'Generate Event Images', // Page title
'Generate Event Images', // Menu title
'manage_options', // Capability
'generate-event-images', // Menu slug
'generate_images_menu_page' // Function to display page content
);
}
function generate_images_menu_page() {
// Check user capabilities
if (!current_user_can('manage_options')) {
return;
}
// Handle form submission
if (isset($_POST['generate_images'])) {
$season = isset($_POST['sp_season']) ? sanitize_text_field($_POST['sp_season']) : '';
generate_images_for_existing_events($season);
echo '<div class="updated"><p>Images generated for selected events.</p></div>';
}
// Get available seasons
$seasons = get_terms(array(
'taxonomy' => 'sp_season',
'hide_empty' => false,
));
?>
<div class="wrap">
<h1>Generate Event Images</h1>
<form method="post" action="">
<p>Select a season to generate images for:</p>
<select name="sp_season">
<option value="">All Seasons</option>
<?php
foreach ($seasons as $season) {
echo '<option value="' . esc_attr($season->slug) . '">' . esc_html($season->name) . '</option>';
}
?>
</select>
<p><input type="submit" name="generate_images" class="button-primary" value="Generate Images"></p>
</form>
</div>
<?php
}
function generate_images_for_existing_events($season = '') {
$args = array(
'post_type' => 'sp_event',
'posts_per_page' => -1,
);
// Add season filter if selected
if (!empty($season)) {
$args['tax_query'] = array(
array(
'taxonomy' => 'sp_season',
'field' => 'slug',
'terms' => $season,
),
);
}
$events = new WP_Query($args);
if ($events->have_posts()) {
while ($events->have_posts()) {
$events->the_post();
$post_id = get_the_ID();
// Ensure no duplicate processing
if (has_post_thumbnail($post_id)) continue;
// Generate the featured image using the existing function
generate_event_featured_image($post_id, get_post($post_id), false);
}
wp_reset_postdata();
}
} }

View File

@@ -0,0 +1,187 @@
<?php
/*
Plugin Name: Custom Open Graph Tags with SportsPress Integration
Description: Adds custom Open Graph tags to posts based on their type, specifically handling sp_event post types with methods from the SportsPress SP_Event class.
Version: 1.0
Author: Your Name
*/
add_action('wp_head', 'custom_open_graph_tags_with_sportspress_integration');
function asc_generate_sp_event_title( $post ) {
// See https://github.com/ThemeBoy/SportsPress/blob/770fa8c6654d7d6648791e877709c2428677635b/includes/admin/post-types/class-sp-admin-cpt-event.php#L99C40-L99C55
if ( is_numeric( $post ) ) {
$post = get_post( $post );
}
if ( ! $post || $post->post_type !== 'sp_event' ) {
return get_the_title();
}
$teams = get_post_meta( $post->ID, 'sp_team', false );
$teams = array_filter( $teams );
$team_names = array();
foreach ( $teams as $team ) {
while ( is_array( $team ) ) {
$team = array_shift( array_filter( $team ) );
}
if ( $team > 0 ) {
$team_names[] = sp_team_short_name( $team );
}
}
$team_names = array_unique( $team_names );
if ( get_option( 'sportspress_event_reverse_teams', 'no' ) === 'yes' ) {
$team_names = array_reverse( $team_names );
}
$delimiter = ' ' . get_option( 'sportspress_event_teams_delimiter', 'vs' ) . ' ';
return implode( $delimiter, $team_names );
}
function asc_generate_short_date( $post, $withTime = true ) {
$formatted_date = get_the_date('D n/j/y', $post);
if (!$withTime){
return $formatted_date;
}
if ( get_the_date('i', $post) == "00") {
$formatted_time = get_the_date('gA', $post);
} else {
$formatted_time = get_the_date('g:iA', $post);
}
return $formatted_date . " " . $formatted_time ;
}
function custom_open_graph_tags_with_sportspress_integration() {
if (is_single()) {
global $post;
if ($post->post_type === 'sp_event') {
// Instantiate SP_Event object
$event = new SP_Event($post->ID);
// Fetch details using SP_Event methods
$publish_date = get_the_date('F j, Y', $post);
$venue_terms = get_the_terms($post->ID, 'sp_venue');
$venue_name = $venue_terms ? $venue_terms[0]->name : 'Venue TBD';
$results = $event->results(); // Using SP_Event method
$title = asc_generate_sp_event_title($post);
$sp_status = get_post_meta( $post->ID, 'sp_status', true );
$status = $event->status(); // Using SP_Event method
$publish_date_and_time = get_the_date('F j, Y g:i A', $post);
$description = "{$publish_date_and_time} at {$venue_name}.";
if ( 'postponed' == $sp_status || 'cancelled' == $sp_status || 'tbd' == $sp_status) {
$description = strtoupper($sp_status) . "" . $description;
$title = strtoupper($sp_status) . "" . $title . "" . asc_generate_short_date($post) . "" . $venue_name;
}
if ( 'future' == $status ) {
$description = $description;
$title = $title . "" . asc_generate_short_date($post) . "" . $venue_name;
}
if ( 'results' == $status ) { // checks if there is a final score
// Get event result data
$data = $event->results();
// The first row should be column labels
$labels = $data[0];
// Remove the first row to leave us with the actual data
unset( $data[0] );
$data = array_filter( $data );
if ( empty( $data ) ) {
return false;
}
// Initialize
$i = 0;
$result_string = '';
$title_string = '';
// Reverse teams order if the option "Events > Teams > Order > Reverse order" is enabled.
$reverse_teams = get_option( 'sportspress_event_reverse_teams', 'no' ) === 'yes' ? true : false;
if ( $reverse_teams ) {
$data = array_reverse( $data, true );
}
$teams_result_array = [];
foreach ( $data as $team_id => $result ) :
$outcomes = array();
$result_outcome = sp_array_value( $result, 'outcome' );
if ( ! is_array( $result_outcome ) ) :
$outcomes = array( '&mdash;' );
else :
foreach ( $result_outcome as $outcome ) :
$the_outcome = get_page_by_path( $outcome, OBJECT, 'sp_outcome' );
if ( is_object( $the_outcome ) ) :
$outcomes[] = $the_outcome->post_title;
endif;
endforeach;
endif;
unset( $result['outcome'] );
$team_name = sp_team_short_name( $team_id );
$team_abbreviation = sp_team_abbreviation( $team_id );
$outcome_abbreviation = get_post_meta( $the_outcome->ID, 'sp_abbreviation', true );
if ( ! $outcome_abbreviation ) {
$outcome_abbreviation = sp_substr( $the_outcome->post_title, 0, 1 );
}
array_push($teams_result_array, [
"result" => $result,
"outcome" => $the_outcome->post_title,
"outcome_abbreviation" => $outcome_abbreviation,
"team_name" => $team_name,
"team_abbreviation" => $team_abbreviation
]
);
$i++;
endforeach;
$publish_date = asc_generate_short_date($post, false);
$special_result_suffix_abbreviation = '';
$special_result_suffix= '';
foreach ( $teams_result_array as $team ) {
$outcome_abbreviation = strtoupper( $team['outcome_abbreviation'] ); // Normalize case
if ( $outcome_abbreviation === 'TF-W' ) {
$special_result_suffix_abbreviation = 'TF-W';
$special_result_suffix = 'Technical Forfeit Win';
break;
} elseif ( $outcome_abbreviation === 'TF-L' ) {
$special_result_suffix_abbreviation = 'TF';
$special_result_suffix = 'Technical Forfeit';
break;
} elseif ( $outcome_abbreviation === 'F-W' || $outcome_abbreviation === 'F-L' ) {
$special_result_suffix_abbreviation = 'Forfeit';
$special_result_suffix = 'Forfeit';
break;
}
}
$title = "{$teams_result_array[0]['team_name']} {$teams_result_array[0]['result']['r']}-{$teams_result_array[1]['result']['r']} {$teams_result_array[1]['team_name']}{$publish_date}" . ($special_result_suffix ? "({$special_result_suffix_abbreviation})" : "");
$description .= " " . "{$teams_result_array[0]['team_name']} ({$teams_result_array[0]['outcome']}), {$teams_result_array[1]['team_name']} ({$teams_result_array[1]['outcome']})." ;
}
$description .= " " . $post->post_content;
$image = get_site_url() . "/head-to-head?post={$post->ID}";
echo '<meta property="og:type" content="article" />' . "\n";
echo '<meta property="og:image" content="'. $image . '" />' . "\n";
echo '<meta property="og:title" content="' . $title . '" />' . "\n";
echo '<meta property="og:description" content="' . $description . '" />' . "\n";
echo '<meta property="og:url" content="' . get_permalink() . '" />' . "\n";
}
}
}
?>

View File

@@ -0,0 +1,220 @@
<?php
/**
* Admin week filter for SportsPress events.
*
* Adds a week selector in wp-admin for `sp_event` and filters events by
* Monday-start week (Monday 00:00:00 through Sunday 23:59:59).
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Parse an ISO week input (e.g. 2026-W07) from the request.
*
* @return array{year:int,week:int}|null
*/
function tony_sportspress_parse_admin_week_filter() {
if ( empty( $_GET['sp_week_filter'] ) ) {
return null;
}
$raw = sanitize_text_field( wp_unslash( $_GET['sp_week_filter'] ) );
if ( ! preg_match( '/^(\d{4})-W(0[1-9]|[1-4][0-9]|5[0-3])$/', $raw, $matches ) ) {
return null;
}
$year = (int) $matches[1];
$week = (int) $matches[2];
return array(
'year' => $year,
'week' => $week,
);
}
/**
* Render week filter control in event admin list.
*
* @param string $post_type Current post type.
*/
function tony_sportspress_render_admin_week_filter( $post_type ) {
if ( 'sp_event' !== $post_type ) {
return;
}
$value = '';
if ( ! empty( $_GET['sp_week_filter'] ) ) {
$value = sanitize_text_field( wp_unslash( $_GET['sp_week_filter'] ) );
}
$summary_text = __( 'Select a week', 'tonys-sportspress-enhancements' );
$parsed = tony_sportspress_parse_admin_week_filter();
if ( is_array( $parsed ) ) {
$timezone = wp_timezone();
$monday = ( new DateTimeImmutable( 'now', $timezone ) )->setISODate( $parsed['year'], $parsed['week'], 1 )->setTime( 0, 0, 0 );
$sunday = $monday->modify( '+6 days' )->setTime( 23, 59, 59 );
/* translators: 1: Monday label/date, 2: Sunday label/date. */
$summary_text = sprintf(
__( '%1$s to %2$s', 'tonys-sportspress-enhancements' ),
wp_date( 'D M j, Y', $monday->getTimestamp(), $timezone ),
wp_date( 'D M j, Y', $sunday->getTimestamp(), $timezone )
);
}
?>
<label for="sp_week_filter" class="screen-reader-text"><?php esc_html_e( 'Filter by week', 'tonys-sportspress-enhancements' ); ?></label>
<input
type="week"
id="sp_week_filter"
name="sp_week_filter"
class="sp-week-filter-field"
value="<?php echo esc_attr( $value ); ?>"
title="<?php esc_attr_e( 'Week (Monday start)', 'tonys-sportspress-enhancements' ); ?>"
/>
<span id="sp-week-filter-summary" class="sp-week-filter-summary"><?php echo esc_html( $summary_text ); ?></span>
<?php
}
add_action( 'restrict_manage_posts', 'tony_sportspress_render_admin_week_filter' );
/**
* Add responsive admin styles so filters stay visible on narrow widths.
*/
function tony_sportspress_admin_week_filter_styles() {
$screen = get_current_screen();
if ( ! $screen || 'edit-sp_event' !== $screen->id ) {
return;
}
?>
<style>
@media (max-width: 1200px) {
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) {
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) > * {
float: none;
margin-right: 0;
}
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) .sp-week-filter-field {
min-width: 145px;
}
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) .sp-week-filter-summary {
display: block;
width: 100%;
margin-top: 2px;
color: #50575e;
font-size: 12px;
line-height: 1.4;
}
}
</style>
<?php
}
add_action( 'admin_head-edit.php', 'tony_sportspress_admin_week_filter_styles' );
/**
* Update week summary text when week input changes.
*/
function tony_sportspress_admin_week_filter_script() {
$screen = get_current_screen();
if ( ! $screen || 'edit-sp_event' !== $screen->id ) {
return;
}
?>
<script>
(function() {
const input = document.getElementById('sp_week_filter');
const summary = document.getElementById('sp-week-filter-summary');
if (!input || !summary) {
return;
}
function updateSummary() {
const raw = (input.value || '').trim();
const match = raw.match(/^(\d{4})-W(\d{2})$/);
if (!match) {
summary.textContent = 'Select a week';
return;
}
const year = parseInt(match[1], 10);
const week = parseInt(match[2], 10);
const jan4 = new Date(Date.UTC(year, 0, 4));
const jan4Day = jan4.getUTCDay() || 7;
const mondayWeek1 = new Date(jan4);
mondayWeek1.setUTCDate(jan4.getUTCDate() - jan4Day + 1);
const monday = new Date(mondayWeek1);
monday.setUTCDate(mondayWeek1.getUTCDate() + (week - 1) * 7);
const sunday = new Date(monday);
sunday.setUTCDate(monday.getUTCDate() + 6);
const fmt = new Intl.DateTimeFormat(undefined, {
weekday: 'short',
month: 'short',
day: 'numeric',
year: 'numeric',
timeZone: 'UTC'
});
summary.textContent = fmt.format(monday) + ' to ' + fmt.format(sunday);
}
input.addEventListener('change', updateSummary);
updateSummary();
})();
</script>
<?php
}
add_action( 'admin_footer-edit.php', 'tony_sportspress_admin_week_filter_script' );
/**
* Apply Monday-start week date query to event admin list.
*
* @param WP_Query $query Main query.
*/
function tony_sportspress_apply_admin_week_filter( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) {
return;
}
$post_type = $query->get( 'post_type' );
if ( 'sp_event' !== $post_type ) {
return;
}
$parsed = tony_sportspress_parse_admin_week_filter();
if ( null === $parsed ) {
return;
}
$timezone = wp_timezone();
$monday = ( new DateTimeImmutable( 'now', $timezone ) )->setISODate( $parsed['year'], $parsed['week'], 1 )->setTime( 0, 0, 0 );
$sunday = $monday->modify( '+6 days' )->setTime( 23, 59, 59 );
$date_query = $query->get( 'date_query' );
if ( ! is_array( $date_query ) ) {
$date_query = array();
}
$date_query[] = array(
'after' => array(
'year' => (int) $monday->format( 'Y' ),
'month' => (int) $monday->format( 'n' ),
'day' => (int) $monday->format( 'j' ),
),
'before' => array(
'year' => (int) $sunday->format( 'Y' ),
'month' => (int) $sunday->format( 'n' ),
'day' => (int) $sunday->format( 'j' ),
),
'inclusive' => true,
);
$query->set( 'date_query', $date_query );
}
add_action( 'pre_get_posts', 'tony_sportspress_apply_admin_week_filter' );

View File

@@ -0,0 +1,89 @@
<?php
/*
Plugin Name: Custom Event Permalinks
Description: Adds a custom permalink structure for the sp_event post type.
Version: 1.0
Author: Your Name
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
// Register custom rewrite rules
function custom_event_rewrite_rules() {
add_rewrite_rule(
'(?:event|game)/.*?[/]?([0-9]+)[/]?$',
'index.php?post_type=sp_event&p=$matches[1]',
'top'
);
}
add_action('init', 'custom_event_rewrite_rules');
// Customize the permalink structure
function custom_event_permalink($permalink, $post) {
if ($post->post_type !== 'sp_event') {
return $permalink;
}
$event = new SP_Event($post->ID);
$teams = get_post_meta($post->ID,'sp_team', false);
$format = get_post_meta($post->ID,'sp_format', true);
sort($teams);
$seasons = get_the_terms($post->ID, 'sp_season', true );
if ($seasons) {
$seasons_slug = implode(
"-",
array_map(function($season){return $season->slug;},$seasons),
);
} else {
$seasons_slug = "no-season";
};
// Get the teams associated with the event
$team_1 = get_post($teams[0]);
$team_2 = get_post($teams[1]);
switch ($format) {
case 'league':
$format_string = 'game';
break;
case 'tournament':
$format_string = 'game';
break;
case 'friendly':
$format_string = 'event';
break;
default:
$format_string = 'event';
break;
}
if ($team_1 && $team_2) {
$permalink = home_url($format_string ."/". $seasons_slug . '/' . $team_1->post_name . '-' . $team_2->post_name . '/' . $post->ID);
}
return $permalink;
}
add_filter('post_type_link', 'custom_event_permalink', 10, 2);
// Flush rewrite rules on activation and deactivation
function custom_event_rewrite_flush() {
custom_event_rewrite_rules();
flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'custom_event_rewrite_flush');
register_deactivation_hook(__FILE__, 'flush_rewrite_rules');
// Modify the query to handle custom permalinks and include future posts
function custom_event_parse_request($query) {
$post_type = sp_array_value( $query->query, 'post_type', null );
if (isset($query->query_vars['post_type']) && $query->query_vars['post_type'] === 'sp_event') {
if (isset($query->query_vars['p'])) {
$query->set('post_type', 'sp_event');
$query->set('p', $query->query_vars['p']);
$query->set('post_status', array('publish', 'future'));
}
}
}
add_action('pre_get_posts', 'custom_event_parse_request');

View File

@@ -0,0 +1,234 @@
<?php
/**
* Quick Edit officials support for SportsPress events.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Print hidden officials data on each event row for quick edit prefill.
*
* @param string $column Column key.
* @param int $post_id Post ID.
*/
function tony_sportspress_event_quick_edit_officials_row_data( $column, $post_id ) {
if ( 'sp_team' !== $column ) {
return;
}
$officials = get_post_meta( $post_id, 'sp_officials', true );
if ( ! is_array( $officials ) ) {
$officials = array();
}
$serialized = wp_json_encode( $officials );
if ( ! is_string( $serialized ) ) {
$serialized = '{}';
}
echo '<span class="hidden tony-event-officials-data" data-officials="' . esc_attr( $serialized ) . '"></span>';
}
add_action( 'manage_sp_event_posts_custom_column', 'tony_sportspress_event_quick_edit_officials_row_data', 20, 2 );
/**
* Render quick edit UI for officials.
*
* @param string $column_name Column key.
* @param string $post_type Post type key.
*/
function tony_sportspress_event_quick_edit_officials_field( $column_name, $post_type ) {
if ( 'sp_event' !== $post_type || 'sp_team' !== $column_name ) {
return;
}
static $printed = false;
if ( $printed ) {
return;
}
$printed = true;
wp_nonce_field( 'tony_sp_event_officials_quick_edit', 'tony_sp_event_officials_quick_edit_nonce' );
$duties = get_terms(
array(
'taxonomy' => 'sp_duty',
'hide_empty' => false,
'orderby' => 'meta_value_num',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'sp_order',
'compare' => 'NOT EXISTS',
),
array(
'key' => 'sp_order',
'compare' => 'EXISTS',
),
),
)
);
if ( ! is_array( $duties ) || empty( $duties ) ) {
return;
}
$officials = get_posts(
array(
'post_type' => 'sp_official',
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
)
);
if ( ! is_array( $officials ) || empty( $officials ) ) {
return;
}
?>
<fieldset class="inline-edit-col-right tony-sp-event-officials-wrap">
<div class="inline-edit-col">
<span class="title inline-edit-categories-label"><?php esc_html_e( 'Officials', 'tonys-sportspress-enhancements' ); ?></span>
<?php foreach ( $duties as $duty ) : ?>
<div class="tony-sp-duty-group">
<span class="title inline-edit-categories-label"><?php echo esc_html( $duty->name ); ?></span>
<input type="hidden" name="tony_sp_officials[<?php echo esc_attr( $duty->term_id ); ?>][]" value="0">
<ul class="cat-checklist">
<?php foreach ( $officials as $official ) : ?>
<li>
<label class="selectit">
<input
value="<?php echo esc_attr( $official->ID ); ?>"
type="checkbox"
name="tony_sp_officials[<?php echo esc_attr( $duty->term_id ); ?>][]"
class="tony-sp-official-checkbox"
data-duty-id="<?php echo esc_attr( $duty->term_id ); ?>"
>
<?php echo esc_html( $official->post_title ); ?>
</label>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endforeach; ?>
</div>
</fieldset>
<?php
}
add_action( 'quick_edit_custom_box', 'tony_sportspress_event_quick_edit_officials_field', 10, 2 );
/**
* Save quick edit officials data.
*
* @param int $post_id Post ID.
*/
function tony_sportspress_event_quick_edit_officials_save( $post_id ) {
if ( empty( $_POST ) ) {
return;
}
if ( wp_is_post_revision( $post_id ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
return;
}
if ( 'sp_event' !== get_post_type( $post_id ) ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
$nonce = isset( $_POST['tony_sp_event_officials_quick_edit_nonce'] )
? sanitize_text_field( wp_unslash( $_POST['tony_sp_event_officials_quick_edit_nonce'] ) )
: '';
if ( '' === $nonce || ! wp_verify_nonce( $nonce, 'tony_sp_event_officials_quick_edit' ) ) {
return;
}
$raw_officials = isset( $_POST['tony_sp_officials'] ) ? wp_unslash( $_POST['tony_sp_officials'] ) : array();
if ( ! is_array( $raw_officials ) ) {
$raw_officials = array();
}
$clean_officials = array();
foreach ( $raw_officials as $duty_id => $official_ids ) {
$duty_id = absint( $duty_id );
if ( $duty_id <= 0 || ! is_array( $official_ids ) ) {
continue;
}
$official_ids = array_map( 'absint', $official_ids );
$official_ids = array_filter( $official_ids );
$official_ids = array_values( array_unique( $official_ids ) );
if ( ! empty( $official_ids ) ) {
$clean_officials[ $duty_id ] = $official_ids;
}
}
update_post_meta( $post_id, 'sp_officials', $clean_officials );
}
add_action( 'save_post', 'tony_sportspress_event_quick_edit_officials_save' );
/**
* Prefill quick edit checkboxes with existing officials.
*/
function tony_sportspress_event_quick_edit_officials_script() {
$screen = get_current_screen();
if ( ! $screen || 'edit-sp_event' !== $screen->id ) {
return;
}
?>
<script>
(function($) {
if (typeof inlineEditPost === 'undefined' || !inlineEditPost.edit) {
return;
}
var wpInlineEdit = inlineEditPost.edit;
inlineEditPost.edit = function(id) {
wpInlineEdit.apply(this, arguments);
var postId = 0;
if (typeof id === 'object') {
postId = parseInt(this.getId(id), 10);
}
if (!postId) {
return;
}
var editRow = $('#edit-' + postId);
var postRow = $('#post-' + postId);
var payload = postRow.find('.tony-event-officials-data').attr('data-officials');
var selected = {};
try {
selected = payload ? JSON.parse(payload) : {};
} catch (e) {
selected = {};
}
editRow.find('.tony-sp-official-checkbox').prop('checked', false);
Object.keys(selected).forEach(function(dutyId) {
var ids = selected[dutyId];
if (!Array.isArray(ids)) {
return;
}
ids.forEach(function(officialId) {
editRow
.find('.tony-sp-official-checkbox[data-duty-id="' + dutyId + '"][value="' + String(officialId) + '"]')
.prop('checked', true);
});
});
};
})(jQuery);
</script>
<?php
}
add_action( 'admin_footer-edit.php', 'tony_sportspress_event_quick_edit_officials_script' );

37
readme.md Normal file
View File

@@ -0,0 +1,37 @@
# Tony's SportsPress Enhancements
Suite of enhancements for the SportsPress plugin, including custom event permalinks, Open Graph tags, and automatic featured image generation for events.
## Description
Tony's SportsPress Enhancements is a collection of add-ons for the [SportsPress](https://wordpress.org/plugins/sportspress/) plugin. It provides:
- **Custom event permalinks** for `sp_event` post types, including season and team slugs.
- **Open Graph meta tags** for events, with dynamic titles, descriptions, and images.
- **Automatic featured image generation** for events, combining team colors and logos into a shareable image.
## Features
- Custom rewrite rules and permalinks for SportsPress events.
- Open Graph integration for better social sharing.
- Dynamic, cached event images based on team data.
- Compatible with WordPress 4.5+ and PHP 5.6+.
## Installation
1. Upload the plugin files to the `/wp-content/plugins/tonys-sportspress-enhancements/` directory, or install via the WordPress plugin repository.
2. Activate the plugin through the 'Plugins' menu in WordPress.
3. Make sure the [SportsPress](https://wordpress.org/plugins/sportspress/) plugin is installed and activated.
## Frequently Asked Questions
**Q: Does this plugin require SportsPress?**
A: Yes, it extends the functionality of the SportsPress plugin.
**Q: How are event images generated?**
A: When an event is viewed or shared, a featured image is generated using the primary colors and logos of the participating teams.
---
*This plugin is not affiliated with or endorsed by ThemeBoy or the official SportsPress plugin.*

View File

@@ -1,115 +0,0 @@
=== Tonys Sportspress Enhancements ===
Contributors: (this should be a list of wordpress.org userid's)
Donate link: https://example.com/
Tags: comments, spam
Requires at least: 4.5
Tested up to: 6.5.3
Requires PHP: 5.6
Stable tag: 0.1.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Here is a short description of the plugin. This should be no more than 150 characters. No markup here.
== Description ==
This is the long description. No limit, and you can use Markdown (as well as in the following sections).
For backwards compatibility, if this section is missing, the full length of the short description will be used, and
Markdown parsed.
A few notes about the sections above:
* "Contributors" is a comma separated list of wp.org/wp-plugins.org usernames
* "Tags" is a comma separated list of tags that apply to the plugin
* "Requires at least" is the lowest version that the plugin will work on
* "Tested up to" is the highest version that you've *successfully used to test the plugin*. Note that it might work on
higher versions... this is just the highest one you've verified.
* Stable tag should indicate the Subversion "tag" of the latest stable version, or "trunk," if you use `/trunk/` for
stable.
Note that the `readme.txt` of the stable tag is the one that is considered the defining one for the plugin, so
if the `/trunk/readme.txt` file says that the stable tag is `4.3`, then it is `/tags/4.3/readme.txt` that'll be used
for displaying information about the plugin. In this situation, the only thing considered from the trunk `readme.txt`
is the stable tag pointer. Thus, if you develop in trunk, you can update the trunk `readme.txt` to reflect changes in
your in-development version, without having that information incorrectly disclosed about the current stable version
that lacks those changes -- as long as the trunk's `readme.txt` points to the correct stable tag.
If no stable tag is provided, it is assumed that trunk is stable, but you should specify "trunk" if that's where
you put the stable version, in order to eliminate any doubt.
== Installation ==
This section describes how to install the plugin and get it working.
e.g.
1. Upload `plugin-name.php` to the `/wp-content/plugins/` directory
1. Activate the plugin through the 'Plugins' menu in WordPress
1. Place `<?php do_action('plugin_name_hook'); ?>` in your templates
== Frequently Asked Questions ==
= A question that someone might have =
An answer to that question.
= What about foo bar? =
Answer to foo bar dilemma.
== Screenshots ==
1. This screen shot description corresponds to screenshot-1.(png|jpg|jpeg|gif). Note that the screenshot is taken from
the /assets directory or the directory that contains the stable readme.txt (tags or trunk). Screenshots in the /assets
directory take precedence. For example, `/assets/screenshot-1.png` would win over `/tags/4.3/screenshot-1.png`
(or jpg, jpeg, gif).
2. This is the second screen shot
== Changelog ==
= 1.0 =
* A change since the previous version.
* Another change.
= 0.5 =
* List versions from most recent at top to oldest at bottom.
== Upgrade Notice ==
= 1.0 =
Upgrade notices describe the reason a user should upgrade. No more than 300 characters.
= 0.5 =
This version fixes a security related bug. Upgrade immediately.
== Arbitrary section ==
You may provide arbitrary sections, in the same format as the ones above. This may be of use for extremely complicated
plugins where more information needs to be conveyed that doesn't fit into the categories of "description" or
"installation." Arbitrary sections will be shown below the built-in sections outlined above.
== A brief Markdown Example ==
Ordered list:
1. Some feature
1. Another feature
1. Something else about the plugin
Unordered list:
* something
* something else
* third thing
Here's a link to [WordPress](https://wordpress.org/ "Your favorite software") and one to [Markdown's Syntax Documentation][markdown syntax].
Titles are optional, naturally.
[markdown syntax]: https://daringfireball.net/projects/markdown/syntax
"Markdown is what the parser uses to process much of the readme file"
Markdown uses email style notation for blockquotes and I've been told:
> Asterisks for *emphasis*. Double it up for **strong**.
`<?php code(); // goes in backticks ?>`

View File

@@ -1,17 +1,21 @@
<?php <?php
/** /**
* Plugin Name: Tonys SportsPress Enhancements * Plugin Name: Tonys SportsPress Enhancements
* Plugin URI: PLUGIN SITE HERE * Plugin URI: https://github.com/anthonyscorrea/tonys-sportspress-enhancements
* Description: Suite of SportsPress Enhancements * Description: Suite of SportsPress Enhancements
* Author: YOUR NAME HERE * Author: Tony Correa
* Author URI: YOUR SITE HERE * Author URI: https://github.com/anthonyscorrea/
* Text Domain: tonys-sportspress-enhancements * Text Domain: tonys-sportspress-enhancements
* Domain Path: /languages * Domain Path: /languages
* Version: 0.1.0 * Version: 0.1.3
* *
* @package Tonys_Sportspress_Enhancements * @package Tonys_Sportspress_Enhancements
*/ */
// Include other files here // Include other files here
require_once plugin_dir_path(__FILE__) . 'includes/open-graph-tags.php';
require_once plugin_dir_path(__FILE__) . 'includes/featured-image-generator.php'; require_once plugin_dir_path(__FILE__) . 'includes/featured-image-generator.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-event-permalink.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-event-admin-week-filter.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-event-quick-edit-officials.php';