37 Commits

Author SHA1 Message Date
b2d4c240ad Split ICS subscribe actions by platform
Some checks failed
Release Plugin Zip / release (push) Has been cancelled
2026-04-01 18:25:47 -05:00
6837907aa9 Bump plugin version to 0.1.7 2026-04-01 18:22:46 -05:00
bfc74fcab6 Refactor schedule exports into feed builders 2026-04-01 18:20:56 -05:00
894109ae6c bump version
Some checks failed
Release Plugin Zip / release (push) Has been cancelled
2026-03-31 21:53:37 -05:00
569d11ac1a Add SportsPress schedule exporter 2026-03-31 21:51:20 -05:00
e019bf2061 Add configurable printable venue labels 2026-03-29 09:32:41 -05:00
d2ff863ca5 Add printable calendars feature 2026-03-27 16:22:44 -05:00
785f617134 Fix Games admin filter links 2026-03-27 16:02:56 -05:00
910cc8905e bump version 2026-03-26 21:33:12 -05:00
723b90be52 Improve SportsPress event admin filter controls and team/result ordering
Some checks failed
Release Plugin Zip / release (push) Has been cancelled
2026-03-26 20:11:45 -05:00
e3d9c677c1 Consolidate team ordering and add explicit away-first settings 2026-03-26 20:11:45 -05:00
344a00cf82 bump version
Some checks failed
Release Plugin Zip / release (push) Has been cancelled
2026-03-24 19:27:23 -05:00
8f741f8f00 Add CSV feed 2026-03-24 19:26:25 -05:00
e4a596b479 ignore data folder 2026-03-24 19:19:59 -05:00
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
23 changed files with 8490 additions and 298 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

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ node_modules/
*.sql *.sql
*.tar.gz *.tar.gz
*.zip *.zip
data

503
assets/print-calendar.css Normal file
View File

@@ -0,0 +1,503 @@
@import url("https://fonts.googleapis.com/css2?family=Nunito+Sans:opsz,wdth,wght@6..12,75..125,600..900&family=Open+Sans:wght@400..800&display=swap");
:root {
--pc-font-body: "Open Sans", Arial, sans-serif;
--pc-font-display: "Nunito Sans", "Open Sans", Arial, sans-serif;
--pc-page-padding: 0.45in;
--pc-gap: 8px;
--pc-border: #d5dde6;
--pc-preview-bg: #d9dce3;
--pc-team-logo-size: clamp(40px, 5vw, 56px);
--pc-brand-logo-height: clamp(40px, 5vw, 56px);
--pc-brand-logo-max-width: 34%;
--pc-muted-day-bg: rgba(243, 245, 248, 0.72);
--pc-empty-day-bg: rgba(233, 237, 242, 0.72);
--pc-event-logo-height: clamp(28px, 100%, 74px);
--pc-qr-size: 50px;
--pc-qr-offset: calc(var(--pc-qr-size) + var(--pc-gap));
}
body {
margin: 0;
padding: 20px;
font-family: var(--pc-font-body);
color: #111;
background: #fff;
}
.print-shell {
margin: 0;
width: calc(100% / var(--sheet-scale));
background: #fff;
transform-origin: top left;
transform: scale(var(--sheet-scale));
}
.print-page {
padding: var(--pc-page-padding);
}
@media screen {
body.print-preview {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: flex-start;
padding: 24px;
background: var(--pc-preview-bg);
}
body.print-preview .print-shell {
box-shadow: 0 10px 30px rgba(17, 24, 39, 0.2);
}
body.print-preview .print-shell.letter {
width: 8.5in;
min-height: 11in;
}
body.print-preview .print-shell.ledger {
width: 11in;
min-height: 17in;
}
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin: 0 0 12px;
padding-bottom: 8px;
border-bottom: 2px solid var(--team-accent, #b61f0f);
}
.header-brand {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
}
.team-logo,
.league-logo {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
}
.team-logo {
width: var(--pc-team-logo-size);
height: var(--pc-team-logo-size);
}
.team-logo img,
.team-logo-img {
width: 100%;
height: 100%;
display: block;
object-fit: contain;
}
.league-logo {
height: var(--pc-brand-logo-height);
max-width: var(--pc-brand-logo-max-width);
}
.league-logo a,
.league-logo .custom-logo-link,
.league-logo .wp-block-site-logo__link {
height: 100%;
display: inline-flex;
align-items: center;
justify-content: center;
}
.league-logo img,
.league-logo-img,
.league-logo .custom-logo,
.league-logo .wp-block-site-logo__image {
width: auto;
height: 100%;
max-width: 100%;
display: block;
object-fit: contain;
}
.header-copy {
min-width: 0;
}
.title {
margin: 0;
font-family: var(--pc-font-display);
font-size: 28px;
font-weight: 800;
font-variation-settings: "wdth" 92, "wght" 800;
line-height: 1.1;
color: var(--team-ink, #111);
}
.meta {
margin: 0;
font-size: 13px;
color: var(--team-muted-ink, #333);
}
.legend {
display: flex;
flex-wrap: wrap;
gap: var(--pc-gap);
margin: 0;
}
.legend-item {
display: inline-flex;
align-items: center;
font-family: var(--pc-font-display);
font-variation-settings: "wdth" 70, "wght" 700;
background: #fff;
}
.sheet-grid {
display: grid;
grid-template-columns: repeat(var(--month-columns), minmax(0, 1fr));
gap: var(--pc-gap);
align-items: start;
}
.sheet-grid > .month:nth-child(3):last-child {
grid-column: 1 / -1;
width: calc((100% - var(--pc-gap)) / 2);
max-width: calc((100% - var(--pc-gap)) / 2);
justify-self: center;
}
.month {
margin: 0;
break-inside: avoid;
page-break-inside: avoid;
}
.month-title {
margin: 0;
padding: 2px;
font-family: var(--pc-font-display);
font-size: calc(20px * var(--month-font-scale));
font-weight: 800;
font-variation-settings: "wdth" 80, "wght" 800;
line-height: 1.1;
letter-spacing: 0.01em;
text-align: center;
text-transform: uppercase;
background: var(--team-primary);
color: var(--team-on-primary);
}
.dow,
.grid {
display: grid;
grid-template-columns: repeat(7, minmax(0, 1fr));
}
.dow span {
display: block;
padding: 2px 1px;
font-size: calc(10px * var(--month-font-scale));
font-weight: 700;
text-align: center;
text-transform: uppercase;
border-bottom: 1px solid var(--pc-border);
background: var(--team-link-color);
color: var(--team-on-link-color);
}
.day {
--corner-badge-size: calc(15px * var(--month-font-scale));
--corner-badge-offset: 0px;
position: relative;
overflow: hidden;
aspect-ratio: var(--day-aspect);
background: #fff;
}
.day.muted {
background: var(--pc-muted-day-bg);
color: #9aa5b1;
}
.day.no-events {
background: var(--pc-empty-day-bg);
}
.day-num {
position: absolute;
top: var(--corner-badge-offset);
left: var(--corner-badge-offset);
z-index: 4;
width: var(--corner-badge-size);
height: var(--corner-badge-size);
display: flex;
align-items: center;
justify-content: center;
font-family: var(--pc-font-display);
font-size: calc(12px * var(--month-font-scale));
font-weight: 800;
font-variation-settings: "wdth" 86, "wght" 800;
line-height: 1;
}
.day.has-events .day-num {
color: var(--day-num-color, #fff);
}
.events-stack {
height: 100%;
display: grid;
grid-template-rows: repeat(var(--event-count), minmax(0, 1fr));
}
.event {
--event-top-band: calc(var(--corner-badge-size, 11px) + var(--corner-badge-offset, 2px));
--event-bottom-band: 26px;
--event-logo-height: var(--pc-event-logo-height);
position: relative;
background: var(--event-bg, var(--team-primary, #1b76d1));
color: var(--event-fg, #fff);
}
.event.h {
--event-bg: var(--team-primary, #1b76d1);
}
.event.a {
--event-bg: var(--team-link-color, var(--team-secondary, #8b3f1f));
}
.event-center {
position: absolute;
top: var(--event-top-band);
left: 0;
right: 0;
bottom: var(--event-bottom-band);
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
width: 100%;
font-size: calc(10px * var(--month-font-scale));
font-weight: 700;
line-height: 1;
text-align: center;
}
.event-center img {
width: auto;
height: var(--event-logo-height);
max-width: 100%;
max-height: 100%;
display: block;
object-fit: contain;
object-position: center;
}
.event-name {
width: 100%;
max-width: 100%;
max-height: 100%;
overflow: hidden;
text-overflow: clip;
white-space: normal;
word-break: normal;
overflow-wrap: normal;
hyphens: none;
line-height: 1.05;
font-weight: 700;
opacity: 0.85;
}
.event.no-logo .event-name {
font-family: var(--pc-font-display);
font-weight: 700;
font-variation-settings: "wdth" 30, "wght" 700;
letter-spacing: -0.01em;
text-transform: uppercase;
}
.ha-flag {
position: absolute;
top: var(--corner-badge-offset, 2px);
right: var(--corner-badge-offset, 2px);
z-index: 3;
width: var(--corner-badge-size, 11px);
height: var(--corner-badge-size, 11px);
display: flex;
align-items: center;
justify-content: center;
font-family: var(--pc-font-display);
font-size: calc(10px * var(--month-font-scale));
font-weight: 900;
font-variation-settings: "wdth" 84, "wght" 900;
line-height: 1;
letter-spacing: -0.01em;
background: #111;
color: #fff;
}
.event.a .ha-flag {
background: #fff;
color: #111;
}
.event-meta {
position: absolute;
left: 0;
right: 0;
bottom: 2px;
z-index: 3;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
gap: 1px;
padding: 0 2px;
overflow: hidden;
}
.event-time {
order: 1;
max-width: 100%;
font-size: calc(12px * var(--month-font-scale));
font-weight: 800;
line-height: 1;
text-transform: uppercase;
text-align: center;
color: currentColor;
white-space: nowrap;
overflow: hidden;
text-overflow: clip;
opacity: 0.95;
}
.event-venue {
order: 2;
max-width: 100%;
font-size: calc(8px * var(--month-font-scale));
font-weight: 700;
line-height: 1;
text-align: center;
text-transform: uppercase;
white-space: nowrap;
overflow: hidden;
text-overflow: clip;
opacity: 0.88;
}
.empty {
padding: 16px;
border: 2px dashed #c8d2de;
background: #f8fafc;
}
.footer-meta {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--pc-gap);
margin-top: 10px;
padding-top: 8px;
border-top: 1px solid var(--pc-border);
}
.legend-bottom {
--month-width: calc((100% - ((var(--month-columns) - 1) * var(--pc-gap))) / var(--month-columns));
flex: 0 0 var(--month-width);
width: var(--month-width);
max-width: var(--month-width);
align-content: flex-start;
justify-content: center;
gap: 6px;
}
.legend-bottom .legend-item {
padding: 3px 6px;
font-size: 10px;
text-align: center;
}
.footer-qr {
position: relative;
flex: 0 0 auto;
display: flex;
align-items: flex-end;
justify-content: flex-end;
gap: var(--pc-gap);
min-height: var(--pc-qr-size);
padding-right: var(--pc-qr-offset);
}
.footer-qr-copy {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 2px;
text-align: right;
}
.footer-qr-label {
font-size: 9px;
font-weight: 700;
letter-spacing: 0.02em;
text-transform: uppercase;
color: var(--team-muted-ink, #333);
}
.footer-qr-link {
font-size: 10px;
font-weight: 700;
text-decoration: none;
color: var(--team-ink, #111);
}
.footer-qr-image {
position: absolute;
right: 0;
bottom: 0;
width: var(--pc-qr-size);
height: var(--pc-qr-size);
display: block;
border: 1px solid var(--pc-border);
background: #fff;
}
@media print {
body,
body.print-preview {
padding: 0;
display: block;
background: #fff;
}
.print-shell,
body.print-preview .print-shell,
body.print-preview .print-shell.letter,
body.print-preview .print-shell.ledger {
width: auto;
min-height: auto;
box-shadow: none;
}
.print-page {
padding: 0;
}
.header {
margin-bottom: 8px;
}
.title {
font-size: calc(26px * var(--month-font-scale));
}
}

View File

@@ -0,0 +1,27 @@
( function ( blocks, blockEditor, element, i18n ) {
var el = element.createElement;
var useBlockProps = blockEditor.useBlockProps;
var __ = i18n.__;
blocks.registerBlockType( 'tse/schedule-exporter', {
edit: function () {
var blockProps = useBlockProps( {
className: 'tse-schedule-exporter-block-placeholder',
} );
return el(
'div',
blockProps,
el( 'strong', null, __( 'Schedule Exporter', 'tonys-sportspress-enhancements' ) ),
el(
'p',
null,
__( 'This block renders the public schedule exporter on the frontend.', 'tonys-sportspress-enhancements' )
)
);
},
save: function () {
return null;
},
} );
} )( window.wp.blocks, window.wp.blockEditor, window.wp.element, window.wp.i18n );

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;
} }
function upload_image($file, $post_id, $team1_id, $team2_id) { // Get team colors and logos
$filename = 'event-' . $team1_id .'-'. $team2_id . '.png'; // Set custom file name $team1_colors = get_post_meta($team1_id, 'sp_colors', true);
$upload_dir = wp_upload_dir(); $team2_colors = get_post_meta($team2_id, 'sp_colors', true);
$file_path = $upload_dir['path'] . '/' . $filename;
// Find existing attachments with the same name and delete them $default_color = '#FFFFFF'; // Default color (black)
$existing_attachments = get_posts(array( $team1_color = !empty($team1_colors['primary']) ? $team1_colors['primary'] : $default_color;
'post_type' => 'attachment', $team2_color = !empty($team2_colors['primary']) ? $team2_colors['primary'] : $default_color;
'name' => sanitize_file_name($filename),
'posts_per_page' => -1, // Security check for hex color
'post_status' => 'any' $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';
foreach ($existing_attachments as $attachment) {
wp_delete_attachment($attachment->ID, true); $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
} }
$upload_file = wp_upload_bits($filename, null, file_get_contents($file)); $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);
if (!$upload_file['error']) { // Generate the image if no valid cache exists
$wp_filetype = wp_check_filetype($filename, null); $image_data = generate_bisected_image($team1_color, $team2_color, $team1_logo, $team2_logo);
$attachment = array( $image_path = save_image_to_cache($image_data, $cache_key);
'post_mime_type' => $wp_filetype['type'], set_transient($cache_key, $image_path, DAY_IN_SECONDS * 30); // Cache for 30 days
'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; 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.");
} }
return 0; // Clear all output buffering to prevent any extra output
while (ob_get_level()) {
ob_end_clean();
}
readfile($image_path);
} }
// Add menu item to generate images for existing events function save_image_to_cache($image_data, $cache_key) {
add_action('admin_menu', 'add_generate_images_menu'); $upload_dir = wp_get_upload_dir();
$file_path = $upload_dir['path'] . '/' . $cache_key . '.png';
function add_generate_images_menu() { // Assuming $image_data is raw image data
add_submenu_page( file_put_contents($file_path, $image_data);
'tools.php',
'Generate Event Images', // Page title return $file_path;
'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,418 @@
<?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;
}
/**
* Screen-option defaults for event list filters.
*
* @return array<string, bool>
*/
function tony_sportspress_event_filter_defaults() {
return array(
'month' => true,
'week' => true,
'team' => true,
'venue' => true,
'league' => true,
'season' => true,
'match_day' => true,
);
}
/**
* Build user meta key for an event filter screen option.
*
* @param string $key Filter key.
* @return string
*/
function tony_sportspress_event_filter_meta_key( $key ) {
return 'tony_sp_event_filter_' . $key;
}
/**
* Check whether a filter is enabled for the current user.
*
* @param string $key Filter key.
* @return bool
*/
function tony_sportspress_event_filter_enabled( $key ) {
$defaults = tony_sportspress_event_filter_defaults();
if ( ! array_key_exists( $key, $defaults ) ) {
return true;
}
$user_id = get_current_user_id();
if ( ! $user_id ) {
return (bool) $defaults[ $key ];
}
$stored = get_user_meta( $user_id, tony_sportspress_event_filter_meta_key( $key ), true );
if ( '' === $stored ) {
return (bool) $defaults[ $key ];
}
return '1' === (string) $stored;
}
/**
* Persist event filter Screen Options via AJAX.
*/
function tony_sportspress_save_event_filter_screen_options_ajax() {
if ( ! current_user_can( 'edit_posts' ) ) {
wp_send_json_error();
}
check_ajax_referer( 'tony_sp_event_filters', 'nonce' );
$defaults = tony_sportspress_event_filter_defaults();
$filters = isset( $_POST['filters'] ) && is_array( $_POST['filters'] ) ? $_POST['filters'] : array();
$user_id = get_current_user_id();
foreach ( $defaults as $key => $_enabled ) {
$value = isset( $filters[ $key ] ) ? sanitize_text_field( wp_unslash( $filters[ $key ] ) ) : '0';
update_user_meta( $user_id, tony_sportspress_event_filter_meta_key( $key ), '1' === $value ? '1' : '0' );
}
wp_send_json_success();
}
add_action( 'wp_ajax_tony_sp_event_filter_prefs_save', 'tony_sportspress_save_event_filter_screen_options_ajax' );
/**
* Add filter visibility toggles to Screen Options on event list admin page.
*
* @param string $settings Existing settings HTML.
* @param WP_Screen $screen Current screen.
* @return string
*/
function tony_sportspress_event_filter_screen_options_markup( $settings, $screen ) {
if ( ! $screen || 'edit-sp_event' !== $screen->id ) {
return $settings;
}
$labels = array(
'month' => __( 'Month/Year', 'tonys-sportspress-enhancements' ),
'week' => __( 'Week', 'tonys-sportspress-enhancements' ),
'team' => __( 'Team', 'tonys-sportspress-enhancements' ),
'venue' => __( 'Venue', 'tonys-sportspress-enhancements' ),
'league' => __( 'League', 'tonys-sportspress-enhancements' ),
'season' => __( 'Season', 'tonys-sportspress-enhancements' ),
'match_day' => __( 'Match Day', 'tonys-sportspress-enhancements' ),
);
$settings .= '<fieldset class="metabox-prefs">';
$settings .= '<legend>' . esc_html__( 'Event Filters', 'tonys-sportspress-enhancements' ) . '</legend>';
foreach ( $labels as $key => $label ) {
$meta_key = tony_sportspress_event_filter_meta_key( $key );
$checked = tony_sportspress_event_filter_enabled( $key ) ? ' checked="checked"' : '';
$settings .= '<label for="' . esc_attr( $meta_key ) . '">';
$settings .= '<input type="checkbox" id="' . esc_attr( $meta_key ) . '" name="' . esc_attr( $meta_key ) . '" value="1"' . $checked . ' />';
$settings .= esc_html( $label );
$settings .= '</label>';
}
$settings .= '</fieldset>';
return $settings;
}
add_filter( 'screen_settings', 'tony_sportspress_event_filter_screen_options_markup', 10, 2 );
/**
* 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;
}
if ( ! tony_sportspress_event_filter_enabled( 'week' ) ) {
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;
}
$hide = array(
'month' => ! tony_sportspress_event_filter_enabled( 'month' ),
'team' => ! tony_sportspress_event_filter_enabled( 'team' ),
'venue' => ! tony_sportspress_event_filter_enabled( 'venue' ),
'league' => ! tony_sportspress_event_filter_enabled( 'league' ),
'season' => ! tony_sportspress_event_filter_enabled( 'season' ),
'match_day' => ! tony_sportspress_event_filter_enabled( 'match_day' ),
'week' => ! tony_sportspress_event_filter_enabled( 'week' ),
);
?>
<style>
<?php if ( $hide['month'] ) : ?>
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="m"] { display: none !important; }
<?php endif; ?>
<?php if ( $hide['team'] ) : ?>
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="team"],
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="team"] + .chosen-container,
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="team"] + .select2 { display: none !important; }
<?php endif; ?>
<?php if ( $hide['venue'] ) : ?>
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_venue"],
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_venue"] + .chosen-container,
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_venue"] + .select2 { display: none !important; }
<?php endif; ?>
<?php if ( $hide['league'] ) : ?>
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_league"],
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_league"] + .chosen-container,
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_league"] + .select2 { display: none !important; }
<?php endif; ?>
<?php if ( $hide['season'] ) : ?>
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_season"],
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_season"] + .chosen-container,
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) select[name="sp_season"] + .select2 { display: none !important; }
<?php endif; ?>
<?php if ( $hide['match_day'] ) : ?>
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) input[name="match_day"] { display: none !important; }
<?php endif; ?>
<?php if ( $hide['week'] ) : ?>
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) #sp_week_filter,
.post-type-sp_event .tablenav.top .alignleft.actions:not(.bulkactions) #sp-week-filter-summary { display: none !important; }
<?php endif; ?>
@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 filterCheckboxes = Array.from(
document.querySelectorAll('#screen-options-wrap input[type="checkbox"][id^="tony_sp_event_filter_"]')
);
function saveFilterPrefs() {
if (!filterCheckboxes.length || typeof ajaxurl === 'undefined') {
return;
}
const body = new URLSearchParams();
body.append('action', 'tony_sp_event_filter_prefs_save');
body.append('nonce', <?php echo wp_json_encode( wp_create_nonce( 'tony_sp_event_filters' ) ); ?>);
filterCheckboxes.forEach(function(checkbox) {
const key = checkbox.id.replace('tony_sp_event_filter_', '');
body.append('filters[' + key + ']', checkbox.checked ? '1' : '0');
});
fetch(ajaxurl, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
body: body.toString(),
keepalive: true
}).catch(function() {});
}
filterCheckboxes.forEach(function(checkbox) {
checkbox.addEventListener('change', saveFilterPrefs);
});
const monthSelect = document.querySelector('select[name="m"]');
if (monthSelect) {
const allDates = monthSelect.querySelector('option[value="0"]');
if (allDates) {
allDates.textContent = <?php echo wp_json_encode( __( 'Month/Year', 'tonys-sportspress-enhancements' ) ); ?>;
}
}
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' );

1344
includes/sp-event-csv.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,922 @@
<?php
/**
* Standalone SportsPress schedule feeds.
*
* @package Tonys_Sportspress_Enhancements
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Register the standalone SportsPress CSV feed endpoint.
*
* @return void
*/
function tse_sp_event_export_register_feed() {
add_feed( 'sp-csv', 'tse_sp_event_export_render_feed' );
add_feed( 'sp-ics', 'tse_sp_event_export_render_ical_feed' );
}
add_action( 'init', 'tse_sp_event_export_register_feed', 20 );
/**
* Get available export formats.
*
* @return array
*/
function tse_sp_event_export_get_formats() {
return array(
'matchup' => array(
'label' => __( 'Matchup', 'tonys-sportspress-enhancements' ),
'description' => __( 'Date, time, away team, home team, and field columns.', 'tonys-sportspress-enhancements' ),
),
'team' => array(
'label' => __( 'Team', 'tonys-sportspress-enhancements' ),
'description' => __( 'Team-centric schedule rows with opponent and home/away columns.', 'tonys-sportspress-enhancements' ),
),
);
}
/**
* Get available export columns grouped by format.
*
* @return array
*/
function tse_sp_event_export_get_column_definitions() {
return array(
'matchup' => array(
'date' => __( 'Date', 'tonys-sportspress-enhancements' ),
'time' => __( 'Time', 'tonys-sportspress-enhancements' ),
'season' => __( 'Season', 'tonys-sportspress-enhancements' ),
'league' => __( 'League', 'tonys-sportspress-enhancements' ),
'away_team' => __( 'Away Team', 'tonys-sportspress-enhancements' ),
'home_team' => __( 'Home Team', 'tonys-sportspress-enhancements' ),
'field_name' => __( 'Field Name', 'tonys-sportspress-enhancements' ),
'officials' => __( 'Officials', 'tonys-sportspress-enhancements' ),
),
'team' => array(
'label' => __( 'Extra Label', 'tonys-sportspress-enhancements' ),
'date' => __( 'Date', 'tonys-sportspress-enhancements' ),
'time' => __( 'Time', 'tonys-sportspress-enhancements' ),
'season' => __( 'Season', 'tonys-sportspress-enhancements' ),
'league' => __( 'League', 'tonys-sportspress-enhancements' ),
'team_name' => __( 'Team', 'tonys-sportspress-enhancements' ),
'opponent_name' => __( 'Opponent', 'tonys-sportspress-enhancements' ),
'location_flag' => __( 'Home/Away', 'tonys-sportspress-enhancements' ),
'field_name' => __( 'Field Name', 'tonys-sportspress-enhancements' ),
'field_abbreviation' => __( 'Field Abbreviation', 'tonys-sportspress-enhancements' ),
'field_short_name' => __( 'Field Short Name', 'tonys-sportspress-enhancements' ),
'officials' => __( 'Officials', 'tonys-sportspress-enhancements' ),
'home_team' => __( 'Home Team', 'tonys-sportspress-enhancements' ),
'away_team' => __( 'Away Team', 'tonys-sportspress-enhancements' ),
),
);
}
/**
* Get default columns for an export format.
*
* @param string $format Export format.
* @return array
*/
function tse_sp_event_export_get_default_columns( $format ) {
$defaults = array(
'matchup' => array( 'date', 'time', 'season', 'league', 'away_team', 'home_team', 'field_name' ),
'team' => array( 'label', 'date', 'time', 'season', 'league', 'opponent_name', 'location_flag', 'field_name' ),
);
return isset( $defaults[ $format ] ) ? $defaults[ $format ] : $defaults['matchup'];
}
/**
* Sanitize an export format.
*
* @param string $format Raw format.
* @return string
*/
function tse_sp_event_export_sanitize_format( $format ) {
$format = sanitize_key( (string) $format );
$formats = tse_sp_event_export_get_formats();
return isset( $formats[ $format ] ) ? $format : 'matchup';
}
/**
* Sanitize requested columns for an export format.
*
* @param string $format Export format.
* @param string|array $columns Requested columns.
* @return array
*/
function tse_sp_event_export_sanitize_columns( $format, $columns ) {
$definitions = tse_sp_event_export_get_column_definitions();
$available = isset( $definitions[ $format ] ) ? $definitions[ $format ] : array();
if ( ! is_array( $columns ) ) {
$columns = explode( ',', (string) $columns );
}
$sanitized = array();
foreach ( $columns as $column ) {
$key = sanitize_key( (string) $column );
if ( '' === $key || ! isset( $available[ $key ] ) || in_array( $key, $sanitized, true ) ) {
continue;
}
$sanitized[] = $key;
}
if ( empty( $sanitized ) ) {
return tse_sp_event_export_get_default_columns( $format );
}
return $sanitized;
}
/**
* Normalize one or more numeric IDs from a request value.
*
* Accepts scalars, arrays, and comma-delimited strings.
*
* @param mixed $value Raw request value.
* @return int[]
*/
function tse_sp_event_export_parse_id_list( $value ) {
if ( is_array( $value ) ) {
$raw_values = $value;
} else {
$raw_values = explode( ',', (string) $value );
}
$ids = array();
foreach ( $raw_values as $raw_value ) {
$id = absint( trim( (string) $raw_value ) );
if ( $id > 0 ) {
$ids[] = $id;
}
}
$ids = array_values( array_unique( $ids ) );
return $ids;
}
/**
* Normalize export request arguments.
*
* @param array|null $source Optional input source.
* @return array
*/
function tse_sp_event_export_normalize_request_args( $source = null ) {
$source = is_array( $source ) ? $source : $_GET;
$format = isset( $source['format'] ) ? tse_sp_event_export_sanitize_format( wp_unslash( $source['format'] ) ) : 'matchup';
$team_ids = isset( $source['team_id'] ) ? tse_sp_event_export_parse_id_list( wp_unslash( $source['team_id'] ) ) : array();
$season_ids = isset( $source['season_id'] ) ? tse_sp_event_export_parse_id_list( wp_unslash( $source['season_id'] ) ) : array();
$league_ids = isset( $source['league_id'] ) ? tse_sp_event_export_parse_id_list( wp_unslash( $source['league_id'] ) ) : array();
$field_ids = isset( $source['field_id'] ) ? tse_sp_event_export_parse_id_list( wp_unslash( $source['field_id'] ) ) : array();
return array(
'team_id' => isset( $team_ids[0] ) ? $team_ids[0] : 0,
'team_ids' => $team_ids,
'season_id' => isset( $season_ids[0] ) ? $season_ids[0] : 0,
'season_ids'=> $season_ids,
'league_id' => isset( $league_ids[0] ) ? $league_ids[0] : 0,
'league_ids'=> $league_ids,
'field_id' => isset( $field_ids[0] ) ? $field_ids[0] : 0,
'field_ids' => $field_ids,
'format' => $format,
'columns' => isset( $source['columns'] ) ? tse_sp_event_export_sanitize_columns( $format, wp_unslash( $source['columns'] ) ) : tse_sp_event_export_get_default_columns( $format ),
);
}
/**
* Validate export filters for the requested format.
*
* @param array $filters Export filters.
* @return void
*/
function tse_sp_event_export_validate_filters( $filters ) {
$format = tse_sp_event_export_sanitize_format( isset( $filters['format'] ) ? $filters['format'] : 'matchup' );
$team_ids = isset( $filters['team_ids'] ) && is_array( $filters['team_ids'] ) ? $filters['team_ids'] : array();
if ( 'team' !== $format ) {
return;
}
if ( empty( $team_ids ) ) {
wp_die( esc_html__( 'Team format requires a team filter.', 'tonys-sportspress-enhancements' ), '', array( 'response' => 400 ) );
}
if ( count( $team_ids ) > 1 ) {
wp_die( esc_html__( 'Team format does not support multiple teams.', 'tonys-sportspress-enhancements' ), '', array( 'response' => 400 ) );
}
}
/**
* Query matching event posts for export.
*
* @param array $filters Export filters.
* @return WP_Post[]
*/
function tse_sp_event_export_query_posts( $filters ) {
$team_ids = isset( $filters['team_ids'] ) && is_array( $filters['team_ids'] ) ? array_values( array_filter( array_map( 'absint', $filters['team_ids'] ) ) ) : array();
$season_ids = isset( $filters['season_ids'] ) && is_array( $filters['season_ids'] ) ? array_values( array_filter( array_map( 'absint', $filters['season_ids'] ) ) ) : array();
$league_ids = isset( $filters['league_ids'] ) && is_array( $filters['league_ids'] ) ? array_values( array_filter( array_map( 'absint', $filters['league_ids'] ) ) ) : array();
$field_ids = isset( $filters['field_ids'] ) && is_array( $filters['field_ids'] ) ? array_values( array_filter( array_map( 'absint', $filters['field_ids'] ) ) ) : array();
$args = array(
'post_type' => 'sp_event',
'post_status' => array( 'publish', 'future' ),
'posts_per_page' => -1,
'orderby' => 'date',
'order' => 'ASC',
'no_found_rows' => true,
);
if ( ! empty( $team_ids ) ) {
$args['meta_query'] = array(
array(
'key' => 'sp_team',
'value' => array_map( 'strval', $team_ids ),
'compare' => 'IN',
),
);
}
$tax_query = array();
if ( ! empty( $season_ids ) ) {
$tax_query[] = array(
'taxonomy' => 'sp_season',
'field' => 'term_id',
'terms' => $season_ids,
);
}
if ( ! empty( $league_ids ) ) {
$tax_query[] = array(
'taxonomy' => 'sp_league',
'field' => 'term_id',
'terms' => $league_ids,
);
}
if ( ! empty( $field_ids ) ) {
$tax_query[] = array(
'taxonomy' => 'sp_venue',
'field' => 'term_id',
'terms' => $field_ids,
);
}
if ( ! empty( $tax_query ) ) {
if ( count( $tax_query ) > 1 ) {
$tax_query['relation'] = 'AND';
}
$args['tax_query'] = $tax_query;
}
$query = new WP_Query( $args );
return is_array( $query->posts ) ? $query->posts : array();
}
/**
* Query matching schedule events for export.
*
* @param array $filters Export filters.
* @return array
*/
function tse_sp_event_export_get_events( $filters ) {
$team_id = isset( $filters['team_id'] ) ? absint( $filters['team_id'] ) : 0;
$selected_ids = isset( $filters['team_ids'] ) && is_array( $filters['team_ids'] ) ? array_values( array_filter( array_map( 'absint', $filters['team_ids'] ) ) ) : array();
$query_posts = tse_sp_event_export_query_posts( $filters );
$events = array();
$team_name = $team_id > 0 ? get_the_title( $team_id ) : '';
foreach ( $query_posts as $event ) {
$event_id = $event instanceof WP_Post ? (int) $event->ID : 0;
if ( $event_id <= 0 ) {
continue;
}
$teams = array_values( array_unique( array_map( 'intval', get_post_meta( $event_id, 'sp_team', false ) ) ) );
if ( ! empty( $selected_ids ) && empty( array_intersect( $selected_ids, $teams ) ) ) {
continue;
}
$home_id = isset( $teams[0] ) ? (int) $teams[0] : 0;
$away_id = isset( $teams[1] ) ? (int) $teams[1] : 0;
$venue = tse_sp_event_export_get_primary_field( $event_id );
if ( $team_id > 0 ) {
$location_flag = $home_id === $team_id ? 'Home' : 'Away';
$opponent_id = $home_id === $team_id ? $away_id : $home_id;
} else {
$location_flag = '';
$opponent_id = 0;
}
$events[] = array(
'event_id' => $event_id,
'label' => '',
'date' => get_post_time( 'm/d/Y', false, $event_id, true ),
'time' => strtoupper( (string) ( function_exists( 'sp_get_time' ) ? sp_get_time( $event_id ) : get_post_time( get_option( 'time_format' ), false, $event_id, true ) ) ),
'team_name' => is_string( $team_name ) ? $team_name : '',
'opponent_name' => $opponent_id > 0 ? get_the_title( $opponent_id ) : '',
'location_flag' => $location_flag,
'home_team' => $home_id > 0 ? get_the_title( $home_id ) : '',
'away_team' => $away_id > 0 ? get_the_title( $away_id ) : '',
'field_name' => isset( $venue['name'] ) ? $venue['name'] : '',
'field_abbreviation' => isset( $venue['abbreviation'] ) ? $venue['abbreviation'] : '',
'field_short_name' => isset( $venue['short_name'] ) ? $venue['short_name'] : '',
'season' => tse_sp_event_export_get_event_term_names( $event_id, 'sp_season' ),
'league' => tse_sp_event_export_get_event_term_names( $event_id, 'sp_league' ),
'officials' => tse_sp_event_export_get_officials_value( $event_id ),
);
}
foreach ( $events as $index => $event ) {
$events[ $index ]['label'] = sprintf( 'G#%02d', $index + 1 );
}
wp_reset_postdata();
return $events;
}
/**
* Get event term names as a semicolon-delimited string.
*
* @param int $event_id Event ID.
* @param string $taxonomy Taxonomy name.
* @return string
*/
function tse_sp_event_export_get_event_term_names( $event_id, $taxonomy ) {
$terms = get_the_terms( $event_id, $taxonomy );
if ( ! is_array( $terms ) || empty( $terms ) ) {
return '';
}
$names = array_values( array_filter( array_map( 'strval', wp_list_pluck( $terms, 'name' ) ) ) );
return implode( '; ', array_unique( $names ) );
}
/**
* Get primary field metadata for an event.
*
* @param int $event_id Event ID.
* @return array
*/
function tse_sp_event_export_get_primary_field( $event_id ) {
$venues = get_the_terms( $event_id, 'sp_venue' );
if ( ! is_array( $venues ) || ! isset( $venues[0] ) || ! $venues[0] instanceof WP_Term ) {
return array(
'name' => '',
'abbreviation' => '',
'short_name' => '',
);
}
$venue = $venues[0];
return array(
'name' => isset( $venue->name ) ? (string) $venue->name : '',
'abbreviation' => trim( (string) get_term_meta( $venue->term_id, 'tse_abbreviation', true ) ),
'short_name' => trim( (string) get_term_meta( $venue->term_id, 'tse_short_name', true ) ),
);
}
/**
* Get event officials as a semicolon-delimited string.
*
* @param int $event_id Event ID.
* @return string
*/
function tse_sp_event_export_get_officials_value( $event_id ) {
$official_groups = get_post_meta( $event_id, 'sp_officials', true );
if ( ! is_array( $official_groups ) || empty( $official_groups ) ) {
return '';
}
$official_names = array();
foreach ( $official_groups as $official_ids ) {
if ( ! is_array( $official_ids ) ) {
continue;
}
foreach ( $official_ids as $official_id ) {
$official_id = absint( $official_id );
if ( $official_id <= 0 || 'sp_official' !== get_post_type( $official_id ) ) {
continue;
}
$name = get_the_title( $official_id );
if ( '' === $name ) {
continue;
}
$official_names[ $official_id ] = $name;
}
}
if ( empty( $official_names ) ) {
return '';
}
return implode( '; ', array_values( $official_names ) );
}
/**
* Escape iCalendar text content.
*
* @param string $value Raw value.
* @return string
*/
function tse_sp_event_export_escape_ical_text( $value ) {
$value = html_entity_decode( wp_strip_all_tags( (string) $value ), ENT_QUOTES, get_bloginfo( 'charset' ) );
$value = str_replace( array( '\\', "\r\n", "\r", "\n", ',', ';' ), array( '\\\\', '\n', '\n', '\n', '\,', '\;' ), $value );
return $value;
}
/**
* Fold an iCalendar content line.
*
* @param string $line Raw line.
* @return string
*/
function tse_sp_event_export_fold_ical_line( $line ) {
return wordwrap( (string) $line, 60, "\r\n\t", true );
}
/**
* Get the ICS summary for an event.
*
* For matchup format, this mirrors the SportsPress result/title behavior.
* For team format, this becomes a team-centric opponent summary.
*
* @param WP_Post $event Event post.
* @param array $filters Export filters.
* @return string
*/
function tse_sp_event_export_get_ical_summary( $event, $filters ) {
$format = tse_sp_event_export_sanitize_format( isset( $filters['format'] ) ? $filters['format'] : 'matchup' );
$team_ids = isset( $filters['team_ids'] ) && is_array( $filters['team_ids'] ) ? array_values( array_filter( array_map( 'absint', $filters['team_ids'] ) ) ) : array();
$team_id = isset( $team_ids[0] ) ? $team_ids[0] : 0;
if ( 'team' === $format && $team_id > 0 ) {
$teams = array_values( array_unique( array_map( 'intval', get_post_meta( $event->ID, 'sp_team', false ) ) ) );
$home_id = isset( $teams[0] ) ? (int) $teams[0] : 0;
$away_id = isset( $teams[1] ) ? (int) $teams[1] : 0;
if ( in_array( $team_id, $teams, true ) ) {
$is_home = $home_id === $team_id;
$opponent_id = $is_home ? $away_id : $home_id;
$opponent = $opponent_id > 0 ? get_the_title( $opponent_id ) : __( 'TBD', 'tonys-sportspress-enhancements' );
$summary = sprintf(
/* translators: 1: preposition, 2: opponent name. */
__( '%1$s %2$s', 'tonys-sportspress-enhancements' ),
$is_home ? 'vs' : 'at',
$opponent
);
return apply_filters( 'sportspress_ical_feed_summary', $summary, $event );
}
}
$main_result = get_option( 'sportspress_primary_result', null );
$results = array();
$teams = (array) get_post_meta( $event->ID, 'sp_team', false );
$teams = array_filter( array_unique( $teams ) );
if ( ! empty( $teams ) ) {
$event_results = get_post_meta( $event->ID, 'sp_results', true );
foreach ( $teams as $team_id ) {
$team_id = absint( $team_id );
if ( $team_id <= 0 ) {
continue;
}
$team = get_post( $team_id );
if ( ! $team instanceof WP_Post ) {
continue;
}
$team_results = is_array( $event_results ) && isset( $event_results[ $team_id ] ) ? $event_results[ $team_id ] : null;
if ( $main_result ) {
$team_result = is_array( $team_results ) && isset( $team_results[ $main_result ] ) ? $team_results[ $main_result ] : null;
} else {
if ( is_array( $team_results ) ) {
end( $team_results );
$team_result = prev( $team_results );
} else {
$team_result = null;
}
}
if ( null !== $team_result && '' !== (string) $team_result ) {
$results[] = get_the_title( $team_id ) . ' ' . $team_result;
}
}
}
$summary = ! empty( $results ) ? implode( ' ', $results ) : $event->post_title;
$summary = preg_replace_callback(
'/(&#[0-9]+;)/',
static function( $matches ) {
return mb_convert_encoding( $matches[1], 'UTF-8', 'HTML-ENTITIES' );
},
$summary
);
return apply_filters( 'sportspress_ical_feed_summary', $summary, $event );
}
/**
* Get the ICS location payload for an event.
*
* @param WP_Post $event Event post.
* @return array
*/
function tse_sp_event_export_get_ical_location_data( $event ) {
$location = '';
$geo = false;
$venues = get_the_terms( $event->ID, 'sp_venue' );
if ( ! is_array( $venues ) || empty( $venues ) ) {
return array(
'location' => $location,
'geo' => $geo,
);
}
$venue = reset( $venues );
$location = $venue->name;
$meta = get_option( 'taxonomy_' . $venue->term_id );
$address = is_array( $meta ) && isset( $meta['sp_address'] ) ? $meta['sp_address'] : false;
if ( false !== $address && '' !== (string) $address ) {
$location = $venue->name . ', ' . $address;
}
$latitude = is_array( $meta ) && isset( $meta['sp_latitude'] ) ? $meta['sp_latitude'] : false;
$longitude = is_array( $meta ) && isset( $meta['sp_longitude'] ) ? $meta['sp_longitude'] : false;
if ( false !== $latitude && false !== $longitude && '' !== (string) $latitude && '' !== (string) $longitude ) {
$geo = $latitude . ';' . $longitude;
}
return array(
'location' => (string) $location,
'geo' => $geo,
);
}
/**
* Build iCalendar output for the requested filters.
*
* @param array $filters Export filters.
* @return string
*/
function tse_sp_event_export_build_ical_output( $filters ) {
$query_posts = tse_sp_event_export_query_posts( $filters );
$locale = substr( get_locale(), 0, 2 );
$timezone = sanitize_option( 'timezone_string', get_option( 'timezone_string' ) );
$url = tse_sp_event_export_get_feed_url( $filters, 'ics' );
$calendar = tse_sp_event_export_get_feed_title( $filters );
$output =
"BEGIN:VCALENDAR\r\n" .
"VERSION:2.0\r\n" .
'PRODID:-//ThemeBoy//SportsPress//' . strtoupper( $locale ) . "\r\n" .
"CALSCALE:GREGORIAN\r\n" .
"METHOD:PUBLISH\r\n" .
'URL:' . tse_sp_event_export_fold_ical_line( $url ) . "\r\n" .
'X-FROM-URL:' . tse_sp_event_export_fold_ical_line( $url ) . "\r\n" .
'NAME:' . tse_sp_event_export_escape_ical_text( $calendar ) . "\r\n" .
'X-WR-CALNAME:' . tse_sp_event_export_escape_ical_text( $calendar ) . "\r\n" .
'DESCRIPTION:' . tse_sp_event_export_escape_ical_text( $calendar ) . "\r\n" .
'X-WR-CALDESC:' . tse_sp_event_export_escape_ical_text( $calendar ) . "\r\n" .
"REFRESH-INTERVAL;VALUE=DURATION:PT2M\r\n" .
"X-PUBLISHED-TTL:PT2M\r\n" .
'TZID:' . $timezone . "\r\n" .
'X-WR-TIMEZONE:' . $timezone . "\r\n";
foreach ( $query_posts as $event ) {
if ( ! $event instanceof WP_Post ) {
continue;
}
$date_format = 'Ymd\THis';
$description = tse_sp_event_export_escape_ical_text( $event->post_content );
$summary = tse_sp_event_export_get_ical_summary( $event, $filters );
$minutes = get_post_meta( $event->ID, 'sp_minutes', true );
$minutes = '' === $minutes ? get_option( 'sportspress_event_minutes', 90 ) : $minutes;
$end = new DateTime( $event->post_date );
$end->add( new DateInterval( 'PT' . absint( $minutes ) . 'M' ) );
$location = tse_sp_event_export_get_ical_location_data( $event );
$event_url = get_permalink( $event );
$output .= "BEGIN:VEVENT\r\n";
$output .= tse_sp_event_export_fold_ical_line( 'SUMMARY:' . tse_sp_event_export_escape_ical_text( $summary ) ) . "\r\n";
$output .= 'UID:' . $event->ID . "\r\n";
$output .= "STATUS:CONFIRMED\r\n";
$output .= "DTSTAMP:19700101T000000\r\n";
$output .= 'DTSTART:' . mysql2date( $date_format, $event->post_date ) . "\r\n";
$output .= 'DTEND:' . $end->format( $date_format ) . "\r\n";
$output .= 'LAST-MODIFIED:' . mysql2date( $date_format, $event->post_modified_gmt ) . "\r\n";
if ( '' !== $description ) {
$output .= tse_sp_event_export_fold_ical_line( 'DESCRIPTION:' . $description ) . "\r\n";
}
if ( '' !== $location['location'] ) {
$output .= tse_sp_event_export_fold_ical_line( 'LOCATION:' . tse_sp_event_export_escape_ical_text( $location['location'] ) ) . "\r\n";
}
if ( ! empty( $location['geo'] ) ) {
$output .= 'GEO:' . $location['geo'] . "\r\n";
}
if ( is_string( $event_url ) && '' !== $event_url ) {
$output .= tse_sp_event_export_fold_ical_line( 'URL:' . esc_url_raw( $event_url ) ) . "\r\n";
}
$output .= "END:VEVENT\r\n";
}
$output .= 'END:VCALENDAR';
return $output;
}
/**
* Stream ICS output.
*
* @param array $filters Export filters.
* @param array $args Optional render args.
* @return void
*/
function tse_sp_event_export_stream_ical( $filters, $args = array() ) {
tse_sp_event_export_validate_filters( $filters );
$disposition = isset( $args['disposition'] ) ? sanitize_key( $args['disposition'] ) : 'inline';
$disposition = in_array( $disposition, array( 'inline', 'attachment' ), true ) ? $disposition : 'inline';
$output = tse_sp_event_export_build_ical_output( $filters );
$filename = tse_sp_event_export_build_filename( $filters ) . '.ics';
$etag = md5( $output );
header( 'Content-type: text/calendar; charset=utf-8' );
header( 'Etag:' . '"' . $etag . '"' );
header( 'Content-Disposition: ' . $disposition . '; filename=' . $filename );
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
exit;
}
/**
* Build a CSV row value for a logical column.
*
* @param array $event Normalized event row.
* @param string $column Column key.
* @return string
*/
function tse_sp_event_export_get_row_value( $event, $column ) {
return isset( $event[ $column ] ) ? (string) $event[ $column ] : '';
}
/**
* Build a CSV filename for the export.
*
* @param array $filters Export filters.
* @return string
*/
function tse_sp_event_export_build_filename( $filters ) {
$parts = array( 'schedule' );
$parts = array_merge( $parts, tse_sp_event_export_get_post_slugs( isset( $filters['team_ids'] ) ? $filters['team_ids'] : array() ) );
$parts = array_merge( $parts, tse_sp_event_export_get_term_slugs_by_ids( isset( $filters['season_ids'] ) ? $filters['season_ids'] : array(), 'sp_season' ) );
$parts = array_merge( $parts, tse_sp_event_export_get_term_slugs_by_ids( isset( $filters['league_ids'] ) ? $filters['league_ids'] : array(), 'sp_league' ) );
$parts = array_merge( $parts, tse_sp_event_export_get_term_slugs_by_ids( isset( $filters['field_ids'] ) ? $filters['field_ids'] : array(), 'sp_venue' ) );
$parts[] = tse_sp_event_export_sanitize_format( isset( $filters['format'] ) ? $filters['format'] : 'matchup' );
$parts = array_values( array_filter( $parts ) );
return implode( '-', $parts );
}
/**
* Build a human-readable feed title from filters.
*
* @param array $filters Export filters.
* @return string
*/
function tse_sp_event_export_get_feed_title( $filters ) {
$site_title = trim( (string) get_bloginfo( 'name' ) );
$team_name = tse_sp_event_export_get_post_titles( isset( $filters['team_ids'] ) ? $filters['team_ids'] : array() );
$season_name = tse_sp_event_export_get_term_names_by_ids( isset( $filters['season_ids'] ) ? $filters['season_ids'] : array(), 'sp_season' );
$league_name = tse_sp_event_export_get_term_names_by_ids( isset( $filters['league_ids'] ) ? $filters['league_ids'] : array(), 'sp_league' );
$field_name = tse_sp_event_export_get_term_names_by_ids( isset( $filters['field_ids'] ) ? $filters['field_ids'] : array(), 'sp_venue' );
$title = '' !== $site_title ? $site_title . ' ' . __( 'Event Feed', 'tonys-sportspress-enhancements' ) : __( 'Event Feed', 'tonys-sportspress-enhancements' );
$details = array_filter( array( $team_name, $season_name, $league_name, $field_name ) );
if ( ! empty( $details ) ) {
$title .= ' (' . implode( ' • ', $details ) . ')';
}
return $title;
}
/**
* Get term names for a list of term IDs.
*
* @param array $ids Term IDs.
* @param string $taxonomy Taxonomy name.
* @return string
*/
function tse_sp_event_export_get_term_names_by_ids( $ids, $taxonomy ) {
$names = array();
foreach ( (array) $ids as $id ) {
$term = get_term( absint( $id ), $taxonomy );
if ( $term instanceof WP_Term ) {
$names[] = $term->name;
}
}
$names = array_values( array_unique( array_filter( $names ) ) );
return implode( '; ', $names );
}
/**
* Get post titles for a list of post IDs.
*
* @param array $ids Post IDs.
* @return string
*/
function tse_sp_event_export_get_post_titles( $ids ) {
$titles = array();
foreach ( (array) $ids as $id ) {
$post = get_post( absint( $id ) );
if ( $post instanceof WP_Post ) {
$titles[] = $post->post_title;
}
}
$titles = array_values( array_unique( array_filter( $titles ) ) );
return implode( '; ', $titles );
}
/**
* Get term slugs for a list of term IDs.
*
* @param array $ids Term IDs.
* @param string $taxonomy Taxonomy name.
* @return string[]
*/
function tse_sp_event_export_get_term_slugs_by_ids( $ids, $taxonomy ) {
$slugs = array();
foreach ( (array) $ids as $id ) {
$term = get_term( absint( $id ), $taxonomy );
if ( $term instanceof WP_Term && ! empty( $term->slug ) ) {
$slugs[] = sanitize_title( $term->slug );
}
}
return array_values( array_unique( array_filter( $slugs ) ) );
}
/**
* Get post slugs for a list of post IDs.
*
* @param array $ids Post IDs.
* @return string[]
*/
function tse_sp_event_export_get_post_slugs( $ids ) {
$slugs = array();
foreach ( (array) $ids as $id ) {
$post = get_post( absint( $id ) );
if ( $post instanceof WP_Post ) {
$slugs[] = sanitize_title( $post->post_name ? $post->post_name : $post->post_title );
}
}
return array_values( array_unique( array_filter( $slugs ) ) );
}
/**
* Stream CSV output for the requested export.
*
* @param array $filters Export filters.
* @param array $args Optional render args.
* @return void
*/
function tse_sp_event_export_stream_csv( $filters, $args = array() ) {
$filters['format'] = tse_sp_event_export_sanitize_format( isset( $filters['format'] ) ? $filters['format'] : 'matchup' );
$filters['columns'] = tse_sp_event_export_sanitize_columns( $filters['format'], isset( $filters['columns'] ) ? $filters['columns'] : array() );
tse_sp_event_export_validate_filters( $filters );
$definitions = tse_sp_event_export_get_column_definitions();
$headers = array();
foreach ( $filters['columns'] as $column ) {
$headers[] = $definitions[ $filters['format'] ][ $column ];
}
$events = tse_sp_event_export_get_events( $filters );
$disposition = isset( $args['disposition'] ) ? sanitize_key( $args['disposition'] ) : 'inline';
$disposition = in_array( $disposition, array( 'inline', 'attachment' ), true ) ? $disposition : 'inline';
$filename = tse_sp_event_export_build_filename( $filters ) . '.csv';
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: ' . $disposition . '; filename=' . $filename );
$output = fopen( 'php://output', 'w' );
if ( false === $output ) {
wp_die( esc_html__( 'Unable to generate the CSV export.', 'tonys-sportspress-enhancements' ), '', array( 'response' => 500 ) );
}
fwrite( $output, "\xEF\xBB\xBF" );
fputcsv( $output, $headers );
foreach ( $events as $event ) {
$row = array();
foreach ( $filters['columns'] as $column ) {
$row[] = tse_sp_event_export_get_row_value( $event, $column );
}
fputcsv( $output, $row );
}
fclose( $output );
exit;
}
/**
* Render the standalone CSV feed.
*
* @return void
*/
function tse_sp_event_export_render_feed() {
if ( ! class_exists( 'SP_Calendar' ) && ! post_type_exists( 'sp_event' ) ) {
wp_die( esc_html__( 'SportsPress is required for this feed.', 'tonys-sportspress-enhancements' ), '', array( 'response' => 500 ) );
}
tse_sp_event_export_stream_csv( tse_sp_event_export_normalize_request_args() );
}
/**
* Render the standalone ICS feed.
*
* @return void
*/
function tse_sp_event_export_render_ical_feed() {
if ( ! class_exists( 'SP_Calendar' ) && ! post_type_exists( 'sp_event' ) ) {
wp_die( esc_html__( 'SportsPress is required for this feed.', 'tonys-sportspress-enhancements' ), '', array( 'response' => 500 ) );
}
tse_sp_event_export_stream_ical( tse_sp_event_export_normalize_request_args() );
}
/**
* Build a shareable feed URL.
*
* @param array $args Export args.
* @param string $feed_type Feed type.
* @return string
*/
function tse_sp_event_export_get_feed_url( $args = array(), $feed_type = 'csv' ) {
$filters = tse_sp_event_export_normalize_request_args( $args );
$feed = 'ics' === sanitize_key( $feed_type ) ? 'sp-ics' : 'sp-csv';
$query = array(
'feed' => $feed,
'format' => $filters['format'],
'team_id' => ! empty( $filters['team_ids'] ) ? implode( ',', $filters['team_ids'] ) : '',
'season_id' => ! empty( $filters['season_ids'] ) ? implode( ',', $filters['season_ids'] ) : '',
'league_id' => ! empty( $filters['league_ids'] ) ? implode( ',', $filters['league_ids'] ) : '',
'field_id' => ! empty( $filters['field_ids'] ) ? implode( ',', $filters['field_ids'] ) : '',
'columns' => implode( ',', $filters['columns'] ),
);
return add_query_arg( array_filter( $query, 'strlen' ), home_url( '/' ) );
}

View File

@@ -0,0 +1,101 @@
<?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 front-end single event query to allow scheduled events to resolve.
function custom_event_parse_request( $query ) {
if ( ! $query instanceof WP_Query ) {
return;
}
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( 'sp_event' !== $query->get( 'post_type' ) ) {
return;
}
$post_id = absint( $query->get( 'p' ) );
if ( $post_id <= 0 ) {
return;
}
$query->set( 'post_type', 'sp_event' );
$query->set( 'p', $post_id );
$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' );

View File

@@ -0,0 +1,275 @@
<?php
/**
* Unified team-ordering behavior for SportsPress event admin and frontend lists.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Check whether SportsPress reverse teams option is enabled.
*
* @return bool
*/
function tony_sportspress_reverse_teams_enabled() {
return get_option( 'sportspress_event_reverse_teams', 'no' ) === 'yes';
}
/**
* Add Away | Home option to Event List title format setting.
*
* @param array $options Event list settings options.
* @return array
*/
function tony_sportspress_event_list_add_away_home_option( $options ) {
if ( ! is_array( $options ) ) {
return $options;
}
foreach ( $options as &$option ) {
if ( ! is_array( $option ) || ! isset( $option['id'] ) || 'sportspress_event_list_title_format' !== $option['id'] ) {
continue;
}
if ( ! isset( $option['options'] ) || ! is_array( $option['options'] ) ) {
continue;
}
if ( ! array_key_exists( 'awayhome', $option['options'] ) ) {
$option['options']['awayhome'] = sprintf( '%s | %s', esc_attr__( 'Away', 'sportspress' ), esc_attr__( 'Home', 'sportspress' ) );
}
}
unset( $option );
return $options;
}
add_filter( 'sportspress_event_list_options', 'tony_sportspress_event_list_add_away_home_option' );
/**
* Clarify wording of SportsPress Teams reverse-order option.
*
* @param array $options Team options.
* @return array
*/
function tony_sportspress_relabel_reverse_teams_option( $options ) {
if ( ! is_array( $options ) ) {
return $options;
}
foreach ( $options as &$option ) {
if ( ! is_array( $option ) || ! isset( $option['id'] ) || 'sportspress_event_reverse_teams' !== $option['id'] ) {
continue;
}
$option['desc'] = esc_attr__( 'Show away team first', 'tonys-sportspress-enhancements' );
}
unset( $option );
return $options;
}
add_filter( 'sportspress_event_logo_options', 'tony_sportspress_relabel_reverse_teams_option' );
/**
* Add Event Results team order option.
*
* @param array $options Event Results options.
* @return array
*/
function tony_sportspress_add_event_results_order_option( $options ) {
if ( ! is_array( $options ) ) {
return $options;
}
$options[] = array(
'title' => esc_attr__( 'Row order', 'tonys-sportspress-enhancements' ),
'id' => 'tony_sportspress_event_results_row_order',
'default' => 'home_away',
'type' => 'radio',
'options' => array(
'home_away' => sprintf( '%s | %s', esc_attr__( 'Home', 'sportspress' ), esc_attr__( 'Away', 'sportspress' ) ),
'away_home' => sprintf( '%s | %s', esc_attr__( 'Away', 'sportspress' ), esc_attr__( 'Home', 'sportspress' ) ),
),
);
return $options;
}
add_filter( 'sportspress_result_options', 'tony_sportspress_add_event_results_order_option' );
/**
* Override SportsPress event templates with plugin versions.
*
* @param string $template Located template path.
* @param string $template_name Template filename.
* @param string $template_path Template base path.
* @return string
*/
function tony_sportspress_locate_event_list_template( $template, $template_name, $template_path ) {
$supported = array(
'event-list.php',
'event-results.php',
);
if ( ! in_array( $template_name, $supported, true ) ) {
return $template;
}
$override = dirname( __DIR__ ) . '/templates/' . $template_name;
if ( file_exists( $override ) ) {
return $override;
}
return $template;
}
add_filter( 'sportspress_locate_template', 'tony_sportspress_locate_event_list_template', 10, 3 );
/**
* Add admin styles for explicit Home/Away labels on event edit screens.
*/
function tony_sportspress_event_team_order_admin_styles() {
$screen = get_current_screen();
if ( ! $screen || 'sp_event' !== $screen->post_type ) {
return;
}
?>
<style>
#sp_teamdiv .sp-instance {
padding-top: 8px;
border-top: 1px solid #dcdcde;
}
#sp_teamdiv .sp-instance:first-child {
padding-top: 0;
border-top: 0;
}
#sp_teamdiv .tony-sp-home-away-label {
margin: 0 0 8px;
font-size: 12px;
letter-spacing: 0.02em;
text-transform: uppercase;
color: #50575e;
}
</style>
<?php
}
add_action( 'admin_head-post.php', 'tony_sportspress_event_team_order_admin_styles' );
add_action( 'admin_head-post-new.php', 'tony_sportspress_event_team_order_admin_styles' );
/**
* Add explicit Home/Away labels and reverse visual order in event edit teams metabox.
*/
function tony_sportspress_event_team_order_admin_script() {
$screen = get_current_screen();
if ( ! $screen || 'sp_event' !== $screen->post_type ) {
return;
}
$slot_labels = array(
__( 'Home Team', 'tonys-sportspress-enhancements' ),
__( 'Away Team', 'tonys-sportspress-enhancements' ),
);
$show_away_first = tony_sportspress_reverse_teams_enabled();
?>
<script>
(function($) {
function applyHomeAwayLabels() {
var labels = <?php echo wp_json_encode( $slot_labels ); ?>;
var showAwayFirst = <?php echo $show_away_first ? 'true' : 'false'; ?>;
var $instances = $('#sp_teamdiv .sp-instance');
if (!$instances.length) {
return;
}
var $container = $instances.first().parent();
$instances.css('order', '');
if (showAwayFirst && $instances.length > 1) {
$container.css({
display: 'flex',
flexDirection: 'column'
});
$instances.each(function(index) {
$(this).css('order', index + 1);
});
$instances.eq(0).css('order', 2);
$instances.eq(1).css('order', 1);
}
$instances.each(function(index) {
var label = labels[index] || ('Team ' + (index + 1));
var $instance = $(this);
if (!$instance.children('.tony-sp-home-away-label').length) {
$instance.prepend('<p class="tony-sp-home-away-label"><strong>' + label + '</strong></p>');
} else {
$instance.children('.tony-sp-home-away-label').find('strong').text(label);
}
$instance.find('select[name="sp_team[]"]').first().attr('aria-label', label);
});
}
$(applyHomeAwayLabels);
$(document).on('sp-init-chosen sp-init', applyHomeAwayLabels);
})(jQuery);
</script>
<?php
}
add_action( 'admin_footer-post.php', 'tony_sportspress_event_team_order_admin_script' );
add_action( 'admin_footer-post-new.php', 'tony_sportspress_event_team_order_admin_script' );
/**
* Normalize event-list results array to match event team order.
*
* @param array $main_results Team results array.
* @param int $event_id Event ID.
* @return array
*/
function tony_sportspress_event_list_score_order( $main_results, $event_id ) {
if ( ! is_array( $main_results ) || empty( $main_results ) ) {
return $main_results;
}
$teams = (array) get_post_meta( $event_id, 'sp_team', false );
$teams = array_values( array_filter( array_map( 'absint', $teams ) ) );
if ( empty( $teams ) ) {
return $main_results;
}
$ordered = array();
foreach ( $teams as $index => $team_id ) {
if ( array_key_exists( $team_id, $main_results ) ) {
$ordered[ $team_id ] = $main_results[ $team_id ];
continue;
}
// SportsPress main_results() can be positional (0,1,...) in team order.
if ( array_key_exists( $index, $main_results ) ) {
$ordered[ $team_id ] = $main_results[ $index ];
}
}
if ( empty( $ordered ) ) {
return $main_results;
}
foreach ( $main_results as $team_id => $result ) {
if ( array_key_exists( $team_id, $ordered ) ) {
continue;
}
// Skip positional keys that have already been remapped to team IDs.
if ( is_int( $team_id ) || ctype_digit( (string) $team_id ) ) {
$position = (int) $team_id;
if ( array_key_exists( $position, $teams ) ) {
continue;
}
}
$ordered[ $team_id ] = $result;
}
return $ordered;
}
add_filter( 'sportspress_event_list_main_results', 'tony_sportspress_event_list_score_order', 999, 2 );

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

285
includes/sp-url-builder.php Normal file
View File

@@ -0,0 +1,285 @@
<?php
/**
* Tony's Settings CSV URL builder tab.
*
* @package Tonys_Sportspress_Enhancements
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Register the URL builder tab label.
*
* @param array $tabs Existing tab map.
* @return array
*/
function tse_sp_url_builder_register_tab( $tabs ) {
$tabs['url-builder'] = __( 'Feed Builder', 'tonys-sportspress-enhancements' );
return $tabs;
}
add_filter( 'tse_tonys_settings_tabs', 'tse_sp_url_builder_register_tab' );
/**
* Render the URL builder tab.
*
* @return void
*/
function tse_sp_url_builder_render_tab() {
$leagues = function_exists( 'tse_sp_schedule_exporter_get_leagues' ) ? tse_sp_schedule_exporter_get_leagues() : array();
$seasons = function_exists( 'tse_sp_schedule_exporter_get_seasons' ) ? tse_sp_schedule_exporter_get_seasons() : array();
$teams = function_exists( 'tse_sp_schedule_exporter_get_teams' ) ? tse_sp_schedule_exporter_get_teams() : array();
$fields = function_exists( 'tse_sp_schedule_exporter_get_fields' ) ? tse_sp_schedule_exporter_get_fields() : array();
$formats = function_exists( 'tse_sp_event_export_get_formats' ) ? tse_sp_event_export_get_formats() : array();
$columns = function_exists( 'tse_sp_event_export_get_column_definitions' ) ? tse_sp_event_export_get_column_definitions() : array();
echo '<h2>' . esc_html__( 'Feed Builder', 'tonys-sportspress-enhancements' ) . '</h2>';
echo '<p>' . esc_html__( 'Build a shareable CSV feed URL with format, filters, and custom columns. This does not save settings.', 'tonys-sportspress-enhancements' ) . '</p>';
echo '<div class="tse-url-builder" style="max-width:1100px;padding:20px 24px;border:1px solid #dcdcde;background:#fff;">';
echo '<table class="form-table" role="presentation"><tbody>';
echo '<tr><th scope="row"><label for="tse-url-builder-feed-type">' . esc_html__( 'Feed Type', 'tonys-sportspress-enhancements' ) . '</label></th><td>';
echo '<select id="tse-url-builder-feed-type">';
echo '<option value="csv">' . esc_html__( 'CSV', 'tonys-sportspress-enhancements' ) . '</option>';
echo '<option value="ics">' . esc_html__( 'iCal / ICS', 'tonys-sportspress-enhancements' ) . '</option>';
echo '</select>';
echo '</td></tr>';
echo '<tr><th scope="row"><label for="tse-url-builder-format">' . esc_html__( 'Format', 'tonys-sportspress-enhancements' ) . '</label></th><td>';
echo '<select id="tse-url-builder-format">';
foreach ( $formats as $format_key => $format ) {
echo '<option value="' . esc_attr( $format_key ) . '">' . esc_html( $format['label'] ) . '</option>';
}
echo '</select>';
echo '</td></tr>';
echo '<tr><th scope="row">' . esc_html__( 'League', 'tonys-sportspress-enhancements' ) . '</th><td>';
echo '<div id="tse-url-builder-league" style="display:flex;flex-wrap:wrap;gap:10px 14px;max-width:720px;">';
foreach ( $leagues as $league ) {
$input_id = 'tse-url-builder-league-' . absint( $league->term_id );
echo '<label for="' . esc_attr( $input_id ) . '" style="display:inline-flex;align-items:center;gap:6px;">';
echo '<input id="' . esc_attr( $input_id ) . '" type="checkbox" data-tse-builder-filter="league_id" value="' . esc_attr( (string) $league->term_id ) . '" />';
echo esc_html( $league->name );
echo '</label>';
}
echo '</div>';
echo '<p class="description">' . esc_html__( 'Select one or more leagues. Leave empty to include all leagues.', 'tonys-sportspress-enhancements' ) . '</p>';
echo '</td></tr>';
echo '<tr><th scope="row">' . esc_html__( 'Season', 'tonys-sportspress-enhancements' ) . '</th><td>';
echo '<div id="tse-url-builder-season" style="display:flex;flex-wrap:wrap;gap:10px 14px;max-width:720px;">';
foreach ( $seasons as $season ) {
$input_id = 'tse-url-builder-season-' . absint( $season->term_id );
echo '<label for="' . esc_attr( $input_id ) . '" style="display:inline-flex;align-items:center;gap:6px;">';
echo '<input id="' . esc_attr( $input_id ) . '" type="checkbox" data-tse-builder-filter="season_id" value="' . esc_attr( (string) $season->term_id ) . '" />';
echo esc_html( $season->name );
echo '</label>';
}
echo '</div>';
echo '<p class="description">' . esc_html__( 'Select one or more seasons. Leave empty to include all seasons.', 'tonys-sportspress-enhancements' ) . '</p>';
echo '</td></tr>';
echo '<tr><th scope="row">' . esc_html__( 'Team', 'tonys-sportspress-enhancements' ) . '</th><td>';
echo '<div id="tse-url-builder-team" style="display:flex;flex-wrap:wrap;gap:10px 14px;max-width:720px;">';
foreach ( $teams as $team ) {
$input_id = 'tse-url-builder-team-' . absint( $team->ID );
echo '<label for="' . esc_attr( $input_id ) . '" style="display:inline-flex;align-items:center;gap:6px;">';
echo '<input id="' . esc_attr( $input_id ) . '" type="checkbox" data-tse-builder-filter="team_id" value="' . esc_attr( (string) $team->ID ) . '" />';
echo esc_html( $team->post_title );
echo '</label>';
}
echo '</div>';
echo '<p class="description">' . esc_html__( 'Select one or more teams. Team format requires exactly one team.', 'tonys-sportspress-enhancements' ) . '</p>';
echo '</td></tr>';
echo '<tr><th scope="row">' . esc_html__( 'Field', 'tonys-sportspress-enhancements' ) . '</th><td>';
echo '<div id="tse-url-builder-field" style="display:flex;flex-wrap:wrap;gap:10px 14px;max-width:720px;">';
foreach ( $fields as $field ) {
$input_id = 'tse-url-builder-field-' . absint( $field->term_id );
echo '<label for="' . esc_attr( $input_id ) . '" style="display:inline-flex;align-items:center;gap:6px;">';
echo '<input id="' . esc_attr( $input_id ) . '" type="checkbox" data-tse-builder-filter="field_id" value="' . esc_attr( (string) $field->term_id ) . '" />';
echo esc_html( $field->name );
echo '</label>';
}
echo '</div>';
echo '<p class="description">' . esc_html__( 'Select one or more fields. Leave empty to include all fields.', 'tonys-sportspress-enhancements' ) . '</p>';
echo '</td></tr>';
echo '</tbody></table>';
echo '<div style="display:grid;gap:16px;margin-top:20px;">';
foreach ( $columns as $format_key => $format_columns ) {
$default_columns = function_exists( 'tse_sp_event_export_get_default_columns' ) ? tse_sp_event_export_get_default_columns( $format_key ) : array();
$label = isset( $formats[ $format_key ]['label'] ) ? $formats[ $format_key ]['label'] : ucfirst( $format_key );
echo '<fieldset data-tse-builder-columns="' . esc_attr( $format_key ) . '" style="margin:0;padding:16px;border:1px solid #d7d7db;">';
echo '<legend><strong>' . esc_html( sprintf( __( '%s Columns', 'tonys-sportspress-enhancements' ), $label ) ) . '</strong></legend>';
echo '<div style="display:flex;flex-wrap:wrap;gap:12px 18px;">';
foreach ( $format_columns as $column_key => $column_label ) {
$input_id = 'tse-url-builder-' . sanitize_html_class( $format_key . '-' . $column_key );
echo '<label for="' . esc_attr( $input_id ) . '" style="display:inline-flex;align-items:center;gap:6px;">';
echo '<input id="' . esc_attr( $input_id ) . '" type="checkbox" data-tse-builder-column="' . esc_attr( $format_key ) . '" value="' . esc_attr( $column_key ) . '" ' . checked( in_array( $column_key, $default_columns, true ), true, false ) . ' />';
echo esc_html( $column_label );
echo '</label>';
}
echo '</div>';
echo '</fieldset>';
}
echo '</div>';
echo '<h3 style="margin-top:24px;">' . esc_html__( 'Generated URL', 'tonys-sportspress-enhancements' ) . '</h3>';
echo '<div style="display:flex;align-items:center;gap:8px;max-width:100%;">';
echo '<input type="text" id="tse-url-builder-output" class="large-text code" readonly="readonly" />';
echo '<button type="button" id="tse-url-builder-copy" class="button" aria-label="' . esc_attr__( 'Copy URL', 'tonys-sportspress-enhancements' ) . '" title="' . esc_attr__( 'Copy URL', 'tonys-sportspress-enhancements' ) . '" style="display:inline-flex;align-items:center;justify-content:center;min-width:40px;padding:0 10px;">';
echo '<span aria-hidden="true" style="font-size:16px;line-height:1;">⧉</span>';
echo '</button>';
echo '</div>';
echo '<p><a id="tse-url-builder-open" class="button button-primary" href="' . esc_url( home_url( '/' ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Open Feed URL', 'tonys-sportspress-enhancements' ) . '</a></p>';
echo '<p class="description">' . esc_html__( 'The builder always generates the standalone CSV feed endpoint using the selected filters and columns.', 'tonys-sportspress-enhancements' ) . '</p>';
echo '</div>';
tse_sp_url_builder_render_script();
}
add_action( 'tse_tonys_settings_render_tab_url-builder', 'tse_sp_url_builder_render_tab' );
/**
* Render builder script.
*
* @return void
*/
function tse_sp_url_builder_render_script() {
$base_url = home_url( '/' );
?>
<script>
(function(){
var root = document.querySelector('.tse-url-builder');
if (!root) {
return;
}
var baseUrl = <?php echo wp_json_encode( $base_url ); ?>;
var feedType = root.querySelector('#tse-url-builder-feed-type');
var format = root.querySelector('#tse-url-builder-format');
var output = root.querySelector('#tse-url-builder-output');
var copyButton = root.querySelector('#tse-url-builder-copy');
var openLink = root.querySelector('#tse-url-builder-open');
function getSelectedValues(filterName) {
return Array.prototype.slice.call(root.querySelectorAll('[data-tse-builder-filter="' + filterName + '"]:checked')).map(function(input){
return input.value;
});
}
function syncColumnGroups() {
var selectedFormat = format.value || 'matchup';
var isCsv = (feedType.value || 'csv') === 'csv';
root.querySelectorAll('[data-tse-builder-columns]').forEach(function(group){
group.style.display = (isCsv && group.getAttribute('data-tse-builder-columns') === selectedFormat) ? 'block' : 'none';
});
}
function buildUrl() {
var selectedFeedType = feedType.value || 'csv';
var selectedFormat = format.value || 'matchup';
var url = new URL(baseUrl, window.location.origin);
var leagues = getSelectedValues('league_id');
var seasons = getSelectedValues('season_id');
var teams = getSelectedValues('team_id');
var fields = getSelectedValues('field_id');
var selectedColumns = Array.prototype.slice.call(root.querySelectorAll('[data-tse-builder-column="' + selectedFormat + '"]:checked')).map(function(input){
return input.value;
}).filter(Boolean);
url.searchParams.set('feed', selectedFeedType === 'ics' ? 'sp-ics' : 'sp-csv');
url.searchParams.set('format', selectedFormat);
if (leagues.length) {
url.searchParams.set('league_id', leagues.join(','));
} else {
url.searchParams.delete('league_id');
}
if (seasons.length) {
url.searchParams.set('season_id', seasons.join(','));
} else {
url.searchParams.delete('season_id');
}
if (teams.length) {
url.searchParams.set('team_id', teams.join(','));
} else {
url.searchParams.delete('team_id');
}
if (fields.length) {
url.searchParams.set('field_id', fields.join(','));
} else {
url.searchParams.delete('field_id');
}
if (selectedFeedType === 'csv' && selectedColumns.length) {
url.searchParams.set('columns', selectedColumns.join(','));
} else {
url.searchParams.delete('columns');
}
output.value = url.toString();
openLink.href = url.toString();
openLink.textContent = selectedFeedType === 'ics' ? 'Open ICS Feed' : 'Open Feed URL';
}
syncColumnGroups();
buildUrl();
[feedType, format].forEach(function(input){
input.addEventListener('change', function(){
syncColumnGroups();
buildUrl();
});
});
root.querySelectorAll('[data-tse-builder-filter]').forEach(function(input){
input.addEventListener('change', buildUrl);
});
root.querySelectorAll('[data-tse-builder-column]').forEach(function(input){
input.addEventListener('change', buildUrl);
});
if (copyButton) {
copyButton.addEventListener('click', function(){
var value = output.value || '';
if (!value) {
return;
}
var defaultTitle = copyButton.getAttribute('data-default-title') || copyButton.title || 'Copy URL';
copyButton.setAttribute('data-default-title', defaultTitle);
function markCopied() {
copyButton.title = 'Copied';
window.setTimeout(function(){
copyButton.title = defaultTitle;
}, 1200);
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(value).then(function(){
markCopied();
}).catch(function(){
output.focus();
output.select();
document.execCommand('copy');
markCopied();
});
return;
}
output.focus();
output.select();
document.execCommand('copy');
markCopied();
});
}
})();
</script>
<?php
}

116
includes/sp-venue-meta.php Normal file
View File

@@ -0,0 +1,116 @@
<?php
/**
* Venue term metadata support.
*
* Adds short name and abbreviation fields to SportsPress venues.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Register venue term meta.
*/
function tony_sportspress_register_venue_term_meta() {
register_term_meta(
'sp_venue',
'tse_short_name',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => 'sanitize_text_field',
'show_in_rest' => true,
'auth_callback' => static function() {
return current_user_can( 'manage_categories' );
},
)
);
register_term_meta(
'sp_venue',
'tse_abbreviation',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => 'sanitize_text_field',
'show_in_rest' => true,
'auth_callback' => static function() {
return current_user_can( 'manage_categories' );
},
)
);
}
add_action( 'init', 'tony_sportspress_register_venue_term_meta' );
/**
* Render add-form fields for venue metadata.
*/
function tony_sportspress_add_venue_meta_fields() {
?>
<div class="form-field term-short-name-wrap">
<label for="tse_short_name"><?php esc_html_e( 'Short Name', 'tonys-sportspress-enhancements' ); ?></label>
<input name="tse_short_name" id="tse_short_name" type="text" value="" maxlength="100" />
<p><?php esc_html_e( 'Optional shorter label for this field or venue.', 'tonys-sportspress-enhancements' ); ?></p>
</div>
<div class="form-field term-abbreviation-wrap">
<label for="tse_abbreviation"><?php esc_html_e( 'Abbreviation', 'tonys-sportspress-enhancements' ); ?></label>
<input name="tse_abbreviation" id="tse_abbreviation" type="text" value="" maxlength="20" />
<p><?php esc_html_e( 'Optional abbreviation such as CC East or Field 1.', 'tonys-sportspress-enhancements' ); ?></p>
</div>
<?php
}
add_action( 'sp_venue_add_form_fields', 'tony_sportspress_add_venue_meta_fields' );
/**
* Render edit-form fields for venue metadata.
*
* @param WP_Term $term Venue term.
*/
function tony_sportspress_edit_venue_meta_fields( $term ) {
$short_name = get_term_meta( $term->term_id, 'tse_short_name', true );
$abbreviation = get_term_meta( $term->term_id, 'tse_abbreviation', true );
?>
<tr class="form-field term-short-name-wrap">
<th scope="row">
<label for="tse_short_name"><?php esc_html_e( 'Short Name', 'tonys-sportspress-enhancements' ); ?></label>
</th>
<td>
<input name="tse_short_name" id="tse_short_name" type="text" value="<?php echo esc_attr( $short_name ); ?>" maxlength="100" />
<p class="description"><?php esc_html_e( 'Optional shorter label for this field or venue.', 'tonys-sportspress-enhancements' ); ?></p>
</td>
</tr>
<tr class="form-field term-abbreviation-wrap">
<th scope="row">
<label for="tse_abbreviation"><?php esc_html_e( 'Abbreviation', 'tonys-sportspress-enhancements' ); ?></label>
</th>
<td>
<input name="tse_abbreviation" id="tse_abbreviation" type="text" value="<?php echo esc_attr( $abbreviation ); ?>" maxlength="20" />
<p class="description"><?php esc_html_e( 'Optional abbreviation such as CC East or Field 1.', 'tonys-sportspress-enhancements' ); ?></p>
</td>
</tr>
<?php
}
add_action( 'sp_venue_edit_form_fields', 'tony_sportspress_edit_venue_meta_fields' );
/**
* Save venue metadata fields.
*
* @param int $term_id Venue term ID.
*/
function tony_sportspress_save_venue_meta_fields( $term_id ) {
if ( ! current_user_can( 'manage_categories' ) ) {
return;
}
$short_name = isset( $_POST['tse_short_name'] ) ? sanitize_text_field( wp_unslash( $_POST['tse_short_name'] ) ) : '';
$short_name = is_string( $short_name ) ? trim( $short_name ) : '';
$abbreviation = isset( $_POST['tse_abbreviation'] ) ? sanitize_text_field( wp_unslash( $_POST['tse_abbreviation'] ) ) : '';
$abbreviation = is_string( $abbreviation ) ? trim( $abbreviation ) : '';
update_term_meta( $term_id, 'tse_short_name', $short_name );
update_term_meta( $term_id, 'tse_abbreviation', $abbreviation );
}
add_action( 'created_sp_venue', 'tony_sportspress_save_venue_meta_fields' );
add_action( 'edited_sp_venue', 'tony_sportspress_save_venue_meta_fields' );

View File

@@ -1,7 +1,7 @@
{ {
"name": "tonys-sportspress-enhancements", "name": "tonys-sportspress-enhancements",
"version": "0.1.0", "version": "0.1.5",
"main": "Gruntfile.js", "main": "Gruntfile.js",
"author": "YOUR NAME HERE", "author": "YOUR NAME HERE",
"scripts" : { "scripts" : {

39
readme.md Normal file
View File

@@ -0,0 +1,39 @@
# 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.
- **Printable team schedules** with season-aware venue colors and a print-friendly calendar layout.
## Features
- Custom rewrite rules and permalinks for SportsPress events.
- Open Graph integration for better social sharing.
- Dynamic, cached event images based on team data.
- Printable schedule pages linked from team profiles.
- 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 ?>`

576
templates/event-list.php Normal file
View File

@@ -0,0 +1,576 @@
<?php
/**
* Event List
*
* @author ThemeBoy
* @package SportsPress/Templates
* @version 2.7.23
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
$defaults = array(
'id' => null,
'title' => false,
'status' => 'default',
'format' => 'default',
'date' => 'default',
'date_from' => 'default',
'date_to' => 'default',
'date_past' => 'default',
'date_future' => 'default',
'date_relative' => 'default',
'day' => 'default',
'league' => null,
'season' => null,
'venue' => null,
'team' => null,
'teams_past' => null,
'date_before' => null,
'player' => null,
'number' => -1,
'show_team_logo' => get_option( 'sportspress_event_list_show_logos', 'no' ) == 'yes' ? true : false,
'link_events' => get_option( 'sportspress_link_events', 'yes' ) == 'yes' ? true : false,
'link_teams' => get_option( 'sportspress_link_teams', 'no' ) == 'yes' ? true : false,
'link_venues' => get_option( 'sportspress_link_venues', 'yes' ) == 'yes' ? true : false,
'responsive' => get_option( 'sportspress_enable_responsive_tables', 'no' ) == 'yes' ? true : false,
'sortable' => get_option( 'sportspress_enable_sortable_tables', 'yes' ) == 'yes' ? true : false,
'scrollable' => get_option( 'sportspress_enable_scrollable_tables', 'yes' ) == 'yes' ? true : false,
'paginated' => get_option( 'sportspress_event_list_paginated', 'yes' ) == 'yes' ? true : false,
'rows' => get_option( 'sportspress_event_list_rows', 10 ),
'order' => 'default',
'columns' => null,
'show_all_events_link' => false,
'show_title' => get_option( 'sportspress_event_list_show_title', 'yes' ) == 'yes' ? true : false,
'title_format' => get_option( 'sportspress_event_list_title_format', 'title' ),
'time_format' => get_option( 'sportspress_event_list_time_format', 'combined' ),
);
extract( $defaults, EXTR_SKIP );
$calendar = new SP_Calendar( $id );
if ( $status != 'default' ) {
$calendar->status = $status;
}
if ( $format != 'default' ) {
$calendar->event_format = $format;
}
if ( $date != 'default' ) {
$calendar->date = $date;
}
if ( $date_from != 'default' ) {
$calendar->from = $date_from;
}
if ( $date_to != 'default' ) {
$calendar->to = $date_to;
}
if ( $date_past != 'default' ) {
$calendar->past = $date_past;
}
if ( $date_future != 'default' ) {
$calendar->future = $date_future;
}
if ( $date_relative != 'default' ) {
$calendar->relative = $date_relative;
}
if ( $league ) {
$calendar->league = $league;
}
if ( $season ) {
$calendar->season = $season;
}
if ( $venue ) {
$calendar->venue = $venue;
}
if ( $team ) {
$calendar->team = $team;
}
if ( $teams_past ) {
$calendar->teams_past = $teams_past;
}
if ( $date_before ) {
$calendar->date_before = $date_before;
}
if ( $player ) {
$calendar->player = $player;
}
if ( $order != 'default' ) {
$calendar->order = $order;
}
if ( $day != 'default' ) {
$calendar->day = $day;
}
$data = $calendar->data();
$usecolumns = $calendar->columns;
if ( isset( $columns ) ) :
if ( is_array( $columns ) ) {
$usecolumns = $columns;
} else {
$usecolumns = explode( ',', $columns );
}
endif;
$title_format_raw = $title_format;
$is_awayhome_format = 'awayhome' === $title_format_raw;
if ( $is_awayhome_format ) {
$title_format = 'homeaway';
}
$home_label = $is_awayhome_format ? esc_attr__( 'Away', 'sportspress' ) : esc_attr__( 'Home', 'sportspress' );
$away_label = $is_awayhome_format ? esc_attr__( 'Home', 'sportspress' ) : esc_attr__( 'Away', 'sportspress' );
if ( $show_title && false === $title && $id ) :
$caption = $calendar->caption;
if ( $caption ) {
$title = $caption;
} else {
$title = get_the_title( $id );
}
endif;
$labels = array();
// Create a unique identifier based on the current time in microseconds
$identifier = uniqid( 'eventlist_' );
?>
<div class="sp-template sp-template-event-list">
<?php if ( $title ) { ?>
<h4 class="sp-table-caption"><?php echo wp_kses_post( $title ); ?></h4>
<?php } ?>
<div class="sp-table-wrapper">
<table class="sp-event-list sp-event-list-format-<?php echo esc_attr( $title_format ); ?> sp-data-table
<?php
if ( $paginated ) {
?>
sp-paginated-table
<?php
} if ( $sortable ) {
?>
sp-sortable-table
<?php
} if ( $responsive ) {
echo ' sp-responsive-table ' . esc_attr( $identifier ); } if ( $scrollable ) {
?>
sp-scrollable-table <?php } ?>" data-sp-rows="<?php echo esc_attr( $rows ); ?>">
<thead>
<tr>
<?php
echo '<th class="data-date">' . esc_attr__( 'Date', 'sportspress' ) . '</th>';
switch ( $title_format ) {
case 'homeaway':
if ( sp_column_active( $usecolumns, 'event' ) ) {
echo '<th class="data-home">' . esc_html( $home_label ) . '</th>';
}
if ( 'combined' == $time_format && sp_column_active( $usecolumns, 'time' ) ) {
echo '<th class="data-time">' . esc_attr__( 'Time/Results', 'sportspress' ) . '</th>';
$labels[] = esc_attr__( 'Time/Results', 'sportspress' );
} elseif ( in_array( $time_format, array( 'separate', 'results' ) ) && sp_column_active( $usecolumns, 'results' ) ) {
echo '<th class="data-results">' . esc_attr__( 'Results', 'sportspress' ) . '</th>';
}
if ( sp_column_active( $usecolumns, 'event' ) ) {
echo '<th class="data-away">' . esc_html( $away_label ) . '</th>';
}
if ( in_array( $time_format, array( 'separate', 'time' ) ) && sp_column_active( $usecolumns, 'time' ) ) {
echo '<th class="data-time">' . esc_attr__( 'Time', 'sportspress' ) . '</th>';
}
break;
default:
if ( sp_column_active( $usecolumns, 'event' ) ) {
if ( $title_format == 'teams' ) {
echo '<th class="data-teams">' . esc_attr__( 'Teams', 'sportspress' ) . '</th>';
} else {
echo '<th class="data-event">' . esc_attr__( 'Event', 'sportspress' ) . '</th>';
}
}
switch ( $time_format ) {
case 'separate':
if ( sp_column_active( $usecolumns, 'time' ) ) {
echo '<th class="data-time">' . esc_attr__( 'Time', 'sportspress' ) . '</th>';
}
if ( sp_column_active( $usecolumns, 'results' ) ) {
echo '<th class="data-results">' . esc_attr__( 'Results', 'sportspress' ) . '</th>';
}
break;
case 'time':
if ( sp_column_active( $usecolumns, 'time' ) ) {
echo '<th class="data-time">' . esc_attr__( 'Time', 'sportspress' ) . '</th>';
}
break;
case 'results':
if ( sp_column_active( $usecolumns, 'results' ) ) {
echo '<th class="data-results">' . esc_attr__( 'Results', 'sportspress' ) . '</th>';
}
break;
default:
if ( sp_column_active( $usecolumns, 'time' ) ) {
echo '<th class="data-time">' . esc_attr__( 'Time/Results', 'sportspress' ) . '</th>';
}
}
}
if ( sp_column_active( $usecolumns, 'league' ) ) {
echo '<th class="data-league">' . esc_attr__( 'League', 'sportspress' ) . '</th>';
}
if ( sp_column_active( $usecolumns, 'season' ) ) {
echo '<th class="data-season">' . esc_attr__( 'Season', 'sportspress' ) . '</th>';
}
if ( sp_column_active( $usecolumns, 'venue' ) ) {
echo '<th class="data-venue">' . esc_attr__( 'Venue', 'sportspress' ) . '</th>';
} else {
echo '<th style="display:none;" class="data-venue">' . esc_attr__( 'Venue', 'sportspress' ) . '</th>';
}
if ( sp_column_active( $usecolumns, 'article' ) ) {
echo '<th class="data-article">' . esc_attr__( 'Article', 'sportspress' ) . '</th>';
}
if ( sp_column_active( $usecolumns, 'day' ) ) {
echo '<th class="data-day">' . esc_attr__( 'Match Day', 'sportspress' ) . '</th>';
}
do_action( 'sportspress_event_list_head_row', $usecolumns );
?>
</tr>
</thead>
<tbody>
<?php
$i = 0;
if ( is_numeric( $number ) && $number > 0 ) {
$limit = $number;
}
foreach ( $data as $event ) :
if ( isset( $limit ) && $i >= $limit ) {
continue;
}
$teams = get_post_meta( $event->ID, 'sp_team' );
$video = get_post_meta( $event->ID, 'sp_video', true );
$status = get_post_meta( $event->ID, 'sp_status', true );
$main_results = apply_filters( 'sportspress_event_list_main_results', sp_get_main_results( $event ), $event->ID );
$reverse_results = $is_awayhome_format;
$reverse_teams = false;
if ( 'homeaway' === $title_format ) {
$reverse_teams = $is_awayhome_format;
} elseif ( 'teams' === $title_format ) {
$reverse_teams = get_option( 'sportspress_event_reverse_teams', 'no' ) === 'yes' ? true : false;
}
if ( $reverse_results ) {
$main_results = array_reverse( $main_results, true );
}
$teams_output = '';
$team_class = '';
$teams_array = array();
$team_logos = array();
if ( $teams ) :
foreach ( $teams as $t => $team ) :
$name = sp_team_short_name( $team );
if ( $name ) :
$name = '<meta itemprop="name" content="' . $name . '">' . $name;
if ( $show_team_logo ) :
if ( has_post_thumbnail( $team ) ) :
$logo = '<span class="team-logo">' . sp_get_logo( $team, 'mini', array( 'itemprop' => 'url' ) ) . '</span>';
$team_logos[] = $logo;
$team_class .= ' has-logo';
if ( $t ) :
$name = $logo . ' ' . $name;
else :
$name .= ' ' . $logo;
endif;
endif;
endif;
if ( $link_teams ) :
$team_output = '<a href="' . get_post_permalink( $team ) . '" itemprop="url">' . $name . '</a>';
else :
$team_output = $name;
endif;
$team_result = sp_array_value( $main_results, $team, null );
if ( $team_result != null ) :
if ( $usecolumns != null && ! in_array( 'time', $usecolumns ) ) :
$team_output .= ' (' . $team_result . ')';
endif;
endif;
$teams_array[] = $team_output;
$teams_output .= $team_output . '<br>';
endif;
endforeach;
else :
$teams_output .= '&mdash;';
endif;
echo '<tr class="sp-row sp-post' . ( $i % 2 == 0 ? ' alternate' : '' ) . ' sp-row-no-' . esc_attr( $i ) . '" itemscope itemtype="http://schema.org/SportsEvent">';
$date_html = '<date>' . get_post_time( 'Y-m-d H:i:s', false, $event ) . '</date>' . apply_filters( 'sportspress_event_date', get_post_time( get_option( 'date_format' ), false, $event, true ), $event->ID );
if ( $link_events ) {
$date_html = '<a href="' . get_post_permalink( $event->ID, false, true ) . '" itemprop="url">' . $date_html . '</a>';
}
echo '<td class="data-date" itemprop="startDate" content="' . esc_attr( mysql2date( 'Y-m-d\TH:i:sP', $event->post_date ) ) . '" data-label="' . esc_attr__( 'Date', 'sportspress' ) . '">' . wp_kses( $date_html, array( 'a' => array( 'href' => array(), 'itemprop' => array() ), 'date' => array() ) ) . '</td>';
// Check if the reverse_teams option is selected and alter the teams order
if ( $reverse_teams ) {
$teams_array = array_reverse( $teams_array, true );
}
switch ( $title_format ) {
case 'homeaway':
if ( sp_column_active( $usecolumns, 'event' ) ) {
$team = array_shift( $teams_array );
echo '<td class="data-home' . esc_attr( $team_class ) . '" itemprop="competitor" itemscope itemtype="http://schema.org/SportsTeam" data-label="' . esc_attr( $home_label ) . '">' . wp_kses_post( $team ) . '</td>';
}
if ( 'combined' == $time_format && sp_column_active( $usecolumns, 'time' ) ) {
echo '<td class="data-time ' . esc_attr( $status ) . '" data-label="' . esc_attr__( 'Time/Results', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
if ( ! empty( $main_results ) ) :
echo wp_kses_post( implode( ' - ', $main_results ) );
else :
echo '<date>&nbsp;' . wp_kses_post( get_post_time( 'H:i:s', false, $event ) ) . '</date>' . wp_kses_post( apply_filters( 'sportspress_event_time', sp_get_time( $event ), $event->ID ) );
endif;
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
} elseif ( in_array( $time_format, array( 'separate', 'results' ) ) && sp_column_active( $usecolumns, 'results' ) ) {
echo '<td class="data-results" data-label="' . esc_attr__( 'Results', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
if ( ! empty( $main_results ) ) :
echo wp_kses_post( implode( ' - ', $main_results ) );
else :
echo '-';
endif;
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
}
if ( sp_column_active( $usecolumns, 'event' ) ) {
$team = array_shift( $teams_array );
echo '<td class="data-away' . esc_attr( $team_class ) . '" itemprop="competitor" itemscope itemtype="http://schema.org/SportsTeam" data-label="' . esc_attr( $away_label ) . '">' . wp_kses_post( $team ) . '</td>';
}
if ( in_array( $time_format, array( 'separate', 'time' ) ) && sp_column_active( $usecolumns, 'time' ) ) {
echo '<td class="data-time ' . esc_attr( $status ) . '" data-label="' . esc_attr__( 'Time', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
echo '<date>&nbsp;' . wp_kses_post( get_post_time( 'H:i:s', false, $event ) ) . '</date>' . wp_kses_post( apply_filters( 'sportspress_event_time', sp_get_time( $event ), $event->ID ) );
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
}
break;
default:
if ( sp_column_active( $usecolumns, 'event' ) ) {
if ( $title_format == 'teams' ) {
echo '<td class="data-event data-teams" data-label="' . esc_attr__( 'Teams', 'sportspress' ) . '">' . wp_kses_post( $teams_output ) . '</td>';
} else {
$title_html = implode( ' ', $team_logos ) . ' ' . $event->post_title;
if ( $link_events ) {
$title_html = '<a href="' . get_post_permalink( $event->ID, false, true ) . '" itemprop="url name">' . $title_html . '</a>';
}
echo '<td class="data-event" data-label="' . esc_attr__( 'Event', 'sportspress' ) . '">' . wp_kses_post( $title_html ) . '</td>';
}
}
switch ( $time_format ) {
case 'separate':
if ( sp_column_active( $usecolumns, 'time' ) ) {
echo '<td class="data-time ' . esc_attr( $status ) . '" data-label="' . esc_attr__( 'Time', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
echo '<date>&nbsp;' . wp_kses_post( get_post_time( 'H:i:s', false, $event ) ) . '</date>' . wp_kses_post( apply_filters( 'sportspress_event_time', sp_get_time( $event ), $event->ID ) );
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
}
if ( sp_column_active( $usecolumns, 'results' ) ) {
echo '<td class="data-results" data-label="' . esc_attr__( 'Results', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
if ( ! empty( $main_results ) ) :
echo wp_kses_post( implode( ' - ', $main_results ) );
else :
echo '-';
endif;
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
}
break;
case 'time':
if ( sp_column_active( $usecolumns, 'time' ) ) {
echo '<td class="data-time ' . esc_attr( $status ) . '" data-label="' . esc_attr__( 'Time', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
echo '<date>&nbsp;' . wp_kses_post( get_post_time( 'H:i:s', false, $event ) ) . '</date>' . wp_kses_post( apply_filters( 'sportspress_event_time', sp_get_time( $event ), $event->ID ) );
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
}
break;
case 'results':
if ( sp_column_active( $usecolumns, 'results' ) ) {
echo '<td class="data-results" data-label="' . esc_attr__( 'Results', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
if ( ! empty( $main_results ) ) :
echo wp_kses_post( implode( ' - ', $main_results ) );
else :
echo '-';
endif;
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
}
break;
default:
if ( sp_column_active( $usecolumns, 'time' ) ) {
echo '<td class="data-time ' . esc_attr( $status ) . '" data-label="' . esc_attr__( 'Time/Results', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
if ( ! empty( $main_results ) ) :
echo wp_kses_post( implode( ' - ', $main_results ) );
else :
echo '<date>&nbsp;' . wp_kses_post( get_post_time( 'H:i:s', false, $event ) ) . '</date>' . wp_kses_post( apply_filters( 'sportspress_event_time', sp_get_time( $event ), $event->ID ) );
endif;
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
}
}
}
if ( sp_column_active( $usecolumns, 'league' ) ) :
echo '<td class="data-league" data-label="' . esc_attr__( 'League', 'sportspress' ) . '">';
$leagues = get_the_terms( $event->ID, 'sp_league' );
if ( $leagues ) :
echo wp_kses_post( implode( ', ', wp_list_pluck( $leagues, 'name' ) ) );
endif;
echo '</td>';
endif;
if ( sp_column_active( $usecolumns, 'season' ) ) :
echo '<td class="data-season" data-label="' . esc_attr__( 'Season', 'sportspress' ) . '">';
$seasons = get_the_terms( $event->ID, 'sp_season' );
if ( $seasons ) :
echo wp_kses_post( implode( ', ', wp_list_pluck( $seasons, 'name' ) ) );
endif;
echo '</td>';
endif;
if ( sp_column_active( $usecolumns, 'venue' ) ) :
echo '<td class="data-venue" data-label="' . esc_attr__( 'Venue', 'sportspress' ) . '" itemprop="location" itemscope itemtype="http://schema.org/Place">';
echo '<div itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">';
if ( $link_venues ) :
the_terms( $event->ID, 'sp_venue' );
else :
$venues = get_the_terms( $event->ID, 'sp_venue' );
if ( $venues ) :
echo wp_kses_post( implode( ', ', wp_list_pluck( $venues, 'name' ) ) );
endif;
endif;
echo '</div>';
echo '</td>';
else :
echo '<td style="display:none;" class="data-venue" data-label="' . esc_attr__( 'Venue', 'sportspress' ) . '" itemprop="location" itemscope itemtype="http://schema.org/Place">';
echo '<div itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">';
esc_attr_e( 'N/A', 'sportspress' );
echo '</div>';
echo '</td>';
endif;
if ( sp_column_active( $usecolumns, 'article' ) ) :
echo '<td class="data-article" data-label="' . esc_attr__( 'Article', 'sportspress' ) . '">';
if ( $link_events ) {
echo '<a href="' . esc_url( get_post_permalink( $event->ID, false, true ) ) . '" itemprop="url">';
}
if ( $video ) :
echo '<div class="dashicons dashicons-video-alt"></div>';
elseif ( has_post_thumbnail( $event->ID ) ) :
echo '<div class="dashicons dashicons-camera"></div>';
endif;
if ( $event->post_content !== null ) :
if ( $event->post_status == 'publish' ) :
esc_attr_e( 'Recap', 'sportspress' );
else :
esc_attr_e( 'Preview', 'sportspress' );
endif;
endif;
if ( $link_events ) {
echo '</a>';
}
echo '</td>';
endif;
if ( sp_column_active( $usecolumns, 'day' ) ) :
echo '<td class="data-day" data-label="' . esc_attr__( 'Match Day', 'sportspress' ) . '">';
$day = get_post_meta( $event->ID, 'sp_day', true );
if ( '' == $day ) {
echo '-';
} else {
echo wp_kses_post( $day );
}
echo '</td>';
endif;
do_action( 'sportspress_event_list_row', $event, $usecolumns );
echo '</tr>';
$i++;
endforeach;
?>
</tbody>
</table>
</div>
<?php
// If responsive tables are enabled then load the inline css code
if ( $responsive ) {
// sportspress_responsive_tables_css( $identifier );
}
if ( $id && $show_all_events_link ) {
echo '<div class="sp-calendar-link sp-view-all-link"><a href="' . esc_url( get_permalink( $id ) ) . '">' . esc_attr__( 'View all events', 'sportspress' ) . '</a></div>';
}
?>
</div>

166
templates/event-results.php Normal file
View File

@@ -0,0 +1,166 @@
<?php
/**
* Event Results
*
* @author ThemeBoy
* @package SportsPress/Templates
* @version 2.7.1
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if ( get_option( 'sportspress_event_show_results', 'yes' ) === 'no' ) {
return;
}
if ( ! isset( $id ) ) {
$id = get_the_ID();
}
$event = new SP_Event( $id );
$status = $event->status();
if ( 'results' != $status ) {
return;
}
if ( ! isset( $caption ) ) {
$caption = esc_attr__( 'Results', 'sportspress' );
}
// 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;
}
$scrollable = get_option( 'sportspress_enable_scrollable_tables', 'yes' ) == 'yes' ? true : false;
$link_teams = get_option( 'sportspress_link_teams', 'no' ) == 'yes' ? true : false;
$show_outcomes = array_key_exists( 'outcome', $labels );
// Initialize
$output = '';
$table_rows = '';
$i = 0;
// Event Results row order is controlled in SportsPress > Settings > Events > Event Results.
$row_order = get_option( 'tony_sportspress_event_results_row_order', '' );
if ( '' === $row_order ) {
// Backward compatibility with the legacy checkbox option.
$legacy_away_first = get_option( 'tony_sportspress_event_results_away_first', 'no' ) === 'yes';
$row_order = $legacy_away_first ? 'away_home' : 'home_away';
}
$teams = array_values( array_filter( array_map( 'absint', (array) get_post_meta( $id, 'sp_team', false ) ) ) );
if ( 'away_home' === $row_order ) {
$teams = array_reverse( $teams );
}
if ( ! empty( $teams ) ) {
$ordered = array();
foreach ( $teams as $index => $team_id ) {
if ( array_key_exists( $team_id, $data ) ) {
$ordered[ $team_id ] = $data[ $team_id ];
continue;
}
// SportsPress can store positional rows in team order (0,1,...) depending on context.
if ( array_key_exists( $index, $data ) ) {
$ordered[ $team_id ] = $data[ $index ];
}
}
foreach ( $data as $team_id => $result ) {
if ( ! array_key_exists( $team_id, $ordered ) ) {
$ordered[ $team_id ] = $result;
}
}
if ( ! empty( $ordered ) ) {
$data = $ordered;
}
}
foreach ( $data as $team_id => $result ) :
if ( $show_outcomes ) :
$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;
endif;
unset( $result['outcome'] );
$table_rows .= '<tr class="' . ( $i % 2 == 0 ? 'odd' : 'even' ) . '">';
$team_name = sp_team_short_name( $team_id );
if ( $link_teams && sp_post_exists( $team_id ) ) :
$team_name = '<a href="' . get_post_permalink( $team_id ) . '">' . $team_name . '</a>';
endif;
$table_rows .= '<td class="data-name">' . $team_name . '</td>';
foreach ( $labels as $key => $label ) :
if ( in_array( $key, array( 'name', 'outcome' ) ) ) {
continue;
}
if ( array_key_exists( $key, $result ) && $result[ $key ] != '' ) :
$value = $result[ $key ];
else :
$value = apply_filters( 'sportspress_event_empty_result_string', '&mdash;' );
endif;
$table_rows .= '<td class="data-' . $key . '">' . $value . '</td>';
endforeach;
if ( $show_outcomes ) :
$table_rows .= '<td class="data-outcome">' . implode( ', ', $outcomes ) . '</td>';
endif;
$table_rows .= '</tr>';
$i++;
endforeach;
if ( empty( $table_rows ) ) :
return false;
else :
$output .= '<h4 class="sp-table-caption">' . $caption . '</h4>';
$output .= '<div class="sp-table-wrapper">' .
'<table class="sp-event-results sp-data-table' . ( $scrollable ? ' sp-scrollable-table' : '' ) . '"><thead>' .
'<th class="data-name">' . esc_attr__( 'Team', 'sportspress' ) . '</th>';
foreach ( $labels as $key => $label ) :
$output .= '<th class="data-' . $key . '">' . $label . '</th>';
endforeach;
$output .= '</tr>' . '</thead>' . '<tbody>';
$output .= $table_rows;
$output .= '</tbody>' . '</table>' . '</div>';
endif;
?>
<div class="sp-template sp-template-event-results">
<?php echo wp_kses_post( $output ); ?>
</div>

View File

@@ -0,0 +1,81 @@
<?php
/**
* Tests for custom event permalink query behavior.
*
* @package Tonys_Sportspress_Enhancements
*/
/**
* Event permalink query tests.
*/
class Test_SP_Event_Permalink extends WP_UnitTestCase {
/**
* Preserve global main query references for each test.
*
* @var WP_Query
*/
private $original_wp_query;
/**
* Preserve global main query references for each test.
*
* @var WP_Query
*/
private $original_wp_the_query;
/**
* Set up test case state.
*/
public function set_up(): void {
parent::set_up();
$this->original_wp_query = $GLOBALS['wp_query'];
$this->original_wp_the_query = $GLOBALS['wp_the_query'];
set_current_screen( 'front' );
}
/**
* Restore global query references.
*/
public function tear_down(): void {
$GLOBALS['wp_query'] = $this->original_wp_query;
$GLOBALS['wp_the_query'] = $this->original_wp_the_query;
set_current_screen( 'front' );
parent::tear_down();
}
/**
* The admin event list query should not be altered by permalink handling.
*/
public function test_admin_event_queries_are_not_modified() {
set_current_screen( 'edit-sp_event' );
$query = new WP_Query();
$query->set( 'post_type', 'sp_event' );
$query->set( 'p', 123 );
$query->set( 'post_status', 'future' );
custom_event_parse_request( $query );
$this->assertSame( 'future', $query->get( 'post_status' ) );
$this->assertSame( 123, $query->get( 'p' ) );
}
/**
* Front-end single event requests should include future posts.
*/
public function test_frontend_single_event_queries_include_future_posts() {
$query = new WP_Query();
$query->set( 'post_type', 'sp_event' );
$query->set( 'p', 456 );
$GLOBALS['wp_query'] = $query;
$GLOBALS['wp_the_query'] = $query;
custom_event_parse_request( $query );
$this->assertSame( 'sp_event', $query->get( 'post_type' ) );
$this->assertSame( 456, $query->get( 'p' ) );
$this->assertSame( array( 'publish', 'future' ), $query->get( 'post_status' ) );
}
}

View File

@@ -1,17 +1,43 @@
<?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.7
* *
* @package Tonys_Sportspress_Enhancements * @package Tonys_Sportspress_Enhancements
*/ */
if ( ! defined( 'TONY_SPORTSPRESS_ENHANCEMENTS_VERSION' ) ) {
define( 'TONY_SPORTSPRESS_ENHANCEMENTS_VERSION', '0.1.7' );
}
if ( ! defined( 'TONY_SPORTSPRESS_ENHANCEMENTS_FILE' ) ) {
define( 'TONY_SPORTSPRESS_ENHANCEMENTS_FILE', __FILE__ );
}
if ( ! defined( 'TONY_SPORTSPRESS_ENHANCEMENTS_DIR' ) ) {
define( 'TONY_SPORTSPRESS_ENHANCEMENTS_DIR', plugin_dir_path( __FILE__ ) );
}
if ( ! defined( 'TONY_SPORTSPRESS_ENHANCEMENTS_URL' ) ) {
define( 'TONY_SPORTSPRESS_ENHANCEMENTS_URL', plugin_dir_url( __FILE__ ) );
}
// 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-export.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-event-csv.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';
require_once plugin_dir_path(__FILE__) . 'includes/sp-event-team-ordering.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-printable-calendars.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-url-builder.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-schedule-exporter.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-venue-meta.php';