5 Commits

Author SHA1 Message Date
69faa9a17e Use TONY_SPORTSPRESS_ENHANCEMENTS_DIR constant in requires; localize JS strings
- Replace repeated plugin_dir_path(__FILE__) calls with the already-defined
  TONY_SPORTSPRESS_ENHANCEMENTS_DIR constant
- Localize 'Open ICS Feed' and 'Open Feed URL' JS strings in sp-url-builder.php
  via wp_json_encode() so they pass through the translation system

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:41:07 -05:00
7fc040d87a Prefix generic function names, normalize style in featured-image-generator
- Rename generate_bisected_image, handle_image_request, serve_image,
  save_image_to_cache, add_image_generator_endpoint to tony_sportspress_*
  to avoid global namespace collisions
- Guard $team1/$team2 against null before accessing ->post_modified
- Unify the two logo-placement loops into a single foreach
- Normalize indentation to tabs throughout
- Fix missing space before => in sp-event-export normalize_request_args

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:39:09 -05:00
7cde030f0e Refactor open-graph-tags.php for correctness and standards compliance
- Early-return instead of deeply nested if blocks
- Fix $the_outcome used outside the foreach loop (undefined variable risk)
- Remove unused variables ($results, $publish_date, $i, $result_string,
  $title_string, $labels)
- Remove no-op $description = $description assignment
- Normalize indentation to tabs throughout
- Use strict comparison (===) consistently
- Guard $venue_terms with is_wp_error() check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:37:54 -05:00
6eb51a89b2 Replace old plugin headers with proper file docblocks, add ABSPATH guards
- open-graph-tags.php: replace Plugin Name header with package docblock
- featured-image-generator.php: same cleanup
- sp-event-permalink.php: same cleanup, normalize indentation to tabs,
  collapse redundant switch cases (league/tournament both map to 'game')

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:33:15 -05:00
7f0d0457e1 Fix security and logic bugs in image generator and OG tags
- Fix null-dereference: check !$post with || before accessing post_type
- Sanitize $_GET['post'] with absint() before use
- Escape OG tag attribute values with esc_attr()/esc_url()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:19:57 -05:00
19 changed files with 695 additions and 5299 deletions

View File

@@ -1,93 +0,0 @@
Copyright © 2010 by Dharma Type.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

View File

@@ -27,7 +27,10 @@ body {
.print-shell {
margin: 0;
width: calc(100% / var(--sheet-scale));
background: #fff;
transform-origin: top left;
transform: scale(var(--sheet-scale));
}
.print-page {
@@ -45,10 +48,7 @@ body {
}
body.print-preview .print-shell {
flex: 0 0 auto;
box-shadow: 0 10px 30px rgba(17, 24, 39, 0.2);
transform-origin: top left;
transform: scale(var(--sheet-scale));
}
body.print-preview .print-shell.letter {
@@ -60,104 +60,6 @@ body {
width: 11in;
min-height: 17in;
}
body.print-preview.month-pages {
display: block;
overflow-x: auto;
}
body.print-preview.month-pages .print-shell,
body.print-preview.month-pages .print-shell.letter,
body.print-preview.month-pages .print-shell.ledger {
width: auto;
min-height: auto;
background: transparent;
box-shadow: none;
transform: none;
}
body.print-preview.month-pages .print-page {
padding: 0;
}
body.print-preview.month-pages .month-page {
box-sizing: border-box;
width: 8.5in;
min-height: 11in;
margin-right: auto;
margin-left: auto;
padding: var(--pc-page-padding);
background: #fff;
box-shadow: 0 10px 30px rgba(17, 24, 39, 0.2);
}
body.print-preview.month-pages.ledger .month-page {
width: 11in;
min-height: 17in;
}
body.print-preview.month-pages .month-page + .month-page {
margin-top: 24px;
}
body.print-preview.month-pages .month-page .header {
margin-bottom: 24px;
}
body.print-preview.month-pages .month-page .footer-meta {
margin-top: 24px;
}
body.print-preview.month-pages .sheet-grid {
display: block;
}
body.print-preview.month-pages .month {
width: 100%;
}
body.month-pages .month-title {
font-size: calc(26px * var(--month-font-scale));
padding: 4px;
}
body.month-pages .dow span {
font-size: calc(12px * var(--month-font-scale));
padding: 4px 2px;
}
body.month-pages .day-num {
font-size: calc(14px * var(--month-font-scale));
}
body.month-pages .event-name {
font-size: calc(13px * var(--month-font-scale));
line-height: 0.98;
}
body.month-pages .matchup-name {
font-size: calc(13px * var(--month-font-scale));
line-height: 0.92;
}
body.month-pages .event-time {
font-size: calc(14px * var(--month-font-scale));
}
body.month-pages .event.matchup .event-time {
font-size: calc(11px * var(--month-font-scale));
}
body.month-pages .event.matchup .event-venue {
font-size: calc(10px * var(--month-font-scale));
}
}
@media screen and (max-width: 900px) {
body.print-preview {
justify-content: flex-start;
overflow-x: auto;
}
}
.header {
@@ -333,7 +235,7 @@ body.month-pages .event.matchup .event-venue {
position: absolute;
top: var(--corner-badge-offset);
left: var(--corner-badge-offset);
z-index: 20;
z-index: 4;
width: var(--corner-badge-size);
height: var(--corner-badge-size);
display: flex;
@@ -350,46 +252,12 @@ body.month-pages .event.matchup .event-venue {
color: var(--day-num-color, #fff);
}
.day.has-matchups {
background: #fff;
border: 1px solid var(--pc-border);
}
.day.has-matchups .day-num {
position: absolute;
z-index: 20;
background: #fff;
color: var(--team-ink, #111) !important;
text-shadow: none !important;
box-shadow: 0 0 0 1px var(--pc-border);
}
body.month-pages .day .day-num {
top: 1px;
left: 1px;
width: var(--corner-badge-size);
height: var(--corner-badge-size);
border-radius: 0;
background: #fff;
color: var(--team-ink, #111);
text-shadow: none;
}
.events-stack {
position: relative;
z-index: 1;
height: 100%;
display: grid;
grid-template-rows: repeat(var(--event-count), minmax(0, 1fr));
}
.day.has-matchups .events-stack {
box-sizing: border-box;
grid-template-rows: repeat(var(--event-count), minmax(0, 1fr));
gap: 2px;
padding: 2px;
}
.event {
--event-top-band: calc(var(--corner-badge-size, 11px) + var(--corner-badge-offset, 2px));
--event-bottom-band: 26px;
@@ -407,17 +275,6 @@ body.month-pages .day .day-num {
--event-bg: var(--team-link-color, var(--team-secondary, #8b3f1f));
}
.event.matchup {
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(0, 1fr) auto;
gap: 1px;
min-height: 0;
padding: 2px 3px;
border-radius: 2px;
overflow: hidden;
}
.event-center {
position: absolute;
top: var(--event-top-band);
@@ -436,17 +293,6 @@ body.month-pages .day .day-num {
text-align: center;
}
.event.matchup .event-center {
position: relative;
top: auto;
left: auto;
right: auto;
bottom: auto;
min-height: 0;
align-items: center;
justify-content: center;
}
.event-center img {
width: auto;
height: var(--event-logo-height);
@@ -469,8 +315,6 @@ body.month-pages .day .day-num {
hyphens: none;
line-height: 1.05;
font-weight: 700;
font-stretch: condensed;
font-variation-settings: "wdth" 42, "wght" 700;
opacity: 0.85;
}
@@ -478,38 +322,10 @@ body.month-pages .day .day-num {
font-family: var(--pc-font-display);
font-weight: 700;
font-variation-settings: "wdth" 30, "wght" 700;
letter-spacing: -0.01em;
text-transform: uppercase;
}
.matchup-name {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0;
padding: 0;
font-size: calc(8.5px * var(--month-font-scale));
line-height: 0.98;
font-variation-settings: "wdth" 36, "wght" 800;
opacity: 1;
}
.matchup-vs {
font-size: 0.66em;
font-weight: 900;
opacity: 0.78;
}
.matchup-team {
display: block;
width: 100%;
overflow: hidden;
text-align: center;
text-overflow: clip;
white-space: nowrap;
font-stretch: condensed;
}
.ha-flag {
position: absolute;
top: var(--corner-badge-offset, 2px);
@@ -525,7 +341,7 @@ body.month-pages .day .day-num {
font-weight: 900;
font-variation-settings: "wdth" 84, "wght" 900;
line-height: 1;
letter-spacing: 0;
letter-spacing: -0.01em;
background: #111;
color: #fff;
}
@@ -550,26 +366,11 @@ body.month-pages .day .day-num {
overflow: hidden;
}
.event.matchup .event-meta {
position: relative;
left: auto;
right: auto;
bottom: auto;
display: grid;
grid-template-columns: minmax(0, auto) minmax(0, 1fr);
align-items: center;
justify-content: center;
column-gap: 3px;
padding: 0;
}
.event-time {
order: 1;
max-width: 100%;
font-size: calc(12px * var(--month-font-scale));
font-weight: 800;
font-stretch: condensed;
font-variation-settings: "wdth" 56, "wght" 800;
line-height: 1;
text-transform: uppercase;
text-align: center;
@@ -580,19 +381,11 @@ body.month-pages .day .day-num {
opacity: 0.95;
}
.event.matchup .event-time {
min-width: 0;
font-size: calc(7.5px * var(--month-font-scale));
font-weight: 900;
}
.event-venue {
order: 2;
max-width: 100%;
font-size: calc(8px * var(--month-font-scale));
font-weight: 700;
font-stretch: condensed;
font-variation-settings: "wdth" 50, "wght" 700;
line-height: 1;
text-align: center;
text-transform: uppercase;
@@ -602,12 +395,6 @@ body.month-pages .day .day-num {
opacity: 0.88;
}
.event.matchup .event-venue {
min-width: 0;
font-size: calc(7px * var(--month-font-scale));
text-align: left;
}
.empty {
padding: 16px;
border: 2px dashed #c8d2de;
@@ -685,100 +472,6 @@ body.month-pages .day .day-num {
background: #fff;
}
body.black-white .header,
body.black-white .month-title,
body.black-white .dow span,
body.black-white .day,
body.black-white .event,
body.black-white .empty,
body.black-white .footer-meta,
body.black-white .footer-qr-image {
border-color: #2f3337 !important;
}
body.black-white .month-title,
body.black-white .ha-flag {
background: #1f2328 !important;
color: #fff !important;
text-shadow: none !important;
}
body.black-white .dow span,
body.black-white .day,
body.black-white .day.muted,
body.black-white .day.no-events,
body.black-white .day.has-matchups,
body.black-white .empty,
body.black-white .legend-item,
body.black-white .event.a .ha-flag {
background: #f7f7f7 !important;
color: #111 !important;
text-shadow: none !important;
}
body.black-white .dow span {
background: #e4e7eb !important;
}
body.black-white .day,
body.black-white .day.has-matchups,
body.black-white .legend-item {
background: #fff !important;
}
body.black-white .event,
body.black-white .event.h,
body.black-white .event.a,
body.black-white .event.matchup {
background: #e4e7eb !important;
color: #111 !important;
text-shadow: none !important;
}
body.black-white .day.muted,
body.black-white .day.no-events {
background: #f2f3f5 !important;
color: #4b5563 !important;
}
body.black-white .month-title,
body.black-white .dow span,
body.black-white .day,
body.black-white .event {
border: 1px solid #2f3337;
}
body.black-white .day-num,
body.black-white .day.has-events .day-num,
body.black-white .day.has-matchups .day-num,
body.black-white.month-pages .day .day-num {
background: #fff !important;
color: #111 !important;
box-shadow: 0 0 0 1px #2f3337 !important;
text-shadow: none !important;
}
body.black-white .ha-flag,
body.black-white .event.a .ha-flag {
box-shadow: 0 0 0 1px #2f3337;
}
body.black-white .event-name,
body.black-white .event-time,
body.black-white .event-venue,
body.black-white .matchup-vs,
body.black-white .footer-qr-label,
body.black-white .footer-qr-link,
body.black-white .meta {
color: #111 !important;
opacity: 1 !important;
}
body.black-white .meta,
body.black-white .footer-qr-label {
color: #374151 !important;
}
@media print {
body,
body.print-preview {
@@ -794,7 +487,6 @@ body.black-white .footer-qr-label {
width: auto;
min-height: auto;
box-shadow: none;
transform: none;
}
.print-page {
@@ -808,27 +500,4 @@ body.black-white .footer-qr-label {
.title {
font-size: calc(26px * var(--month-font-scale));
}
body.month-pages .sheet-grid {
display: block;
}
body.month-pages .month-page {
break-after: page;
page-break-after: always;
}
body.month-pages .month-page:last-child {
break-after: auto;
page-break-after: auto;
}
body.month-pages .month {
width: 100%;
}
body.month-pages .grid {
break-inside: avoid;
page-break-inside: avoid;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,151 +1,68 @@
<?php
/**
* Open Graph tags for SportsPress events.
* Open Graph tags with SportsPress integration.
*
* Adds custom Open Graph meta tags to sp_event single pages.
*
* @package Tonys_Sportspress_Enhancements
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action( 'wp_head', 'custom_open_graph_tags_with_sportspress_integration' );
/**
* Build the dynamic matchup image URL for an event.
* Return the head-to-head matchup image URL for an event.
*
* @param int|WP_Post $post Event post or ID.
* @param string $variant Image variant.
* @param int|WP_Post $post Post ID or object.
* @return string
*/
function asc_sp_event_matchup_image_url( $post, $variant = 'wide' ) {
$post = asc_sp_event_get_post( $post );
function asc_sp_event_matchup_image_url( $post ) {
if ( is_numeric( $post ) ) {
$post = get_post( $post );
}
if ( ! $post ) {
if ( ! $post || 'sp_event' !== $post->post_type ) {
return '';
}
$args = array(
'post' => $post->ID,
);
if ( 'wide' !== $variant ) {
$args['variant'] = $variant;
}
if ( function_exists( 'asc_sp_event_image_url_version' ) ) {
$args['v'] = asc_sp_event_image_url_version();
}
return add_query_arg( $args, home_url( '/head-to-head' ) );
return get_site_url() . '/head-to-head?post=' . $post->ID;
}
/**
* Build Open Graph image descriptors for an event.
* Generate a display title for an sp_event using team short names.
*
* @param WP_Post $post Event post.
* @return array<int,array<string,string>>
* @param int|WP_Post $post Post ID or object.
* @return string
*/
function asc_sp_event_open_graph_images( WP_Post $post ) {
return array(
array(
'url' => asc_sp_event_matchup_image_url( $post, 'wide' ),
'width' => '1200',
'height' => '628',
),
);
}
/**
* Normalize an event post argument.
*
* @param int|WP_Post|null $post Post object or ID.
* @return WP_Post|null
*/
function asc_sp_event_get_post( $post = null ) {
if ( null === $post ) {
$post = get_post();
} elseif ( is_numeric( $post ) ) {
$post = get_post( absint( $post ) );
function asc_generate_sp_event_title( $post ) {
if ( is_numeric( $post ) ) {
$post = get_post( $post );
}
if ( ! $post instanceof WP_Post || 'sp_event' !== $post->post_type ) {
return null;
if ( ! $post || $post->post_type !== 'sp_event' ) {
return get_the_title();
}
return $post;
}
/**
* Get event team IDs in SportsPress display order.
*
* @param int|WP_Post $post Event post or ID.
* @return int[]
*/
function asc_sp_event_team_ids( $post ) {
$post = asc_sp_event_get_post( $post );
if ( ! $post ) {
return array();
}
$teams = get_post_meta( $post->ID, 'sp_team', false );
$team_ids = array();
$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 ) );
}
$team = absint( $team );
if ( $team > 0 ) {
$team_ids[] = $team;
$team_names[] = sp_team_short_name( $team );
}
}
$team_ids = array_values( array_unique( $team_ids ) );
$team_names = array_unique( $team_names );
if ( 'yes' === get_option( 'sportspress_event_reverse_teams', 'no' ) ) {
$team_ids = array_reverse( $team_ids );
}
return $team_ids;
}
/**
* Get a safe team short name with fallbacks for test and partial SportsPress environments.
*
* @param int $team_id Team post ID.
* @return string
*/
function asc_sp_team_short_name( $team_id ) {
$name = '';
if ( function_exists( 'sp_team_short_name' ) ) {
$name = (string) sp_team_short_name( $team_id );
}
if ( '' === trim( $name ) ) {
$name = get_the_title( $team_id );
}
return '' !== trim( $name ) ? $name : __( 'Team TBD', 'tonys-sportspress-enhancements' );
}
/**
* Generate a matchup title from event teams.
*
* @param int|WP_Post $post Event post or ID.
* @return string
*/
function asc_generate_sp_event_title( $post ) {
$post = asc_sp_event_get_post( $post );
if ( ! $post ) {
return get_the_title();
}
$team_names = array_map( 'asc_sp_team_short_name', asc_sp_event_team_ids( $post ) );
$team_names = array_values( array_filter( array_unique( $team_names ) ) );
if ( empty( $team_names ) ) {
return get_the_title( $post );
if ( get_option( 'sportspress_event_reverse_teams', 'no' ) === 'yes' ) {
$team_names = array_reverse( $team_names );
}
$delimiter = ' ' . get_option( 'sportspress_event_teams_delimiter', 'vs' ) . ' ';
@@ -154,342 +71,149 @@ function asc_generate_sp_event_title( $post ) {
}
/**
* Generate compact event date text.
* Format a short date string for an event.
*
* @param int|WP_Post $post Event post or ID.
* @param bool $withTime Include time.
* @param int|WP_Post $post Post ID or object.
* @param bool $withTime Whether to include the time.
* @return string
*/
function asc_generate_short_date( $post, $withTime = true ) {
$post = asc_sp_event_get_post( $post );
if ( ! $post ) {
return '';
}
$formatted_date = get_the_date( 'D n/j/y', $post );
if ( ! $withTime ) {
return $formatted_date;
}
$formatted_time = '00' === get_the_date( 'i', $post ) ? get_the_date( 'gA', $post ) : get_the_date( 'g:iA', $post );
if ( get_the_date( 'i', $post ) === '00' ) {
$formatted_time = get_the_date( 'gA', $post );
} else {
$formatted_time = get_the_date( 'g:iA', $post );
}
return trim( $formatted_date . ' ' . $formatted_time );
return $formatted_date . ' ' . $formatted_time;
}
/**
* Get venue name for an event.
* Output Open Graph meta tags for sp_event single pages.
*
* @param WP_Post $post Event post.
* @return string
*/
function asc_sp_event_venue_name( WP_Post $post ) {
$venue_terms = get_the_terms( $post->ID, 'sp_venue' );
if ( is_wp_error( $venue_terms ) || empty( $venue_terms ) ) {
return __( 'Venue TBD', 'tonys-sportspress-enhancements' );
}
return $venue_terms[0]->name;
}
/**
* Normalize event body content for meta descriptions.
*
* @param WP_Post $post Event post.
* @return string
*/
function asc_sp_event_body_excerpt( WP_Post $post ) {
$content = strip_shortcodes( $post->post_content );
$content = wp_strip_all_tags( $content, true );
$content = html_entity_decode( $content, ENT_QUOTES, get_bloginfo( 'charset' ) );
$content = preg_replace( '/\s+/', ' ', $content );
$content = trim( (string) $content );
if ( '' === $content ) {
return '';
}
return wp_trim_words( $content, 35, '' );
}
/**
* Safely instantiate a SportsPress event object.
*
* @param WP_Post $post Event post.
* @return object|null
*/
function asc_sp_event_object( WP_Post $post ) {
if ( ! class_exists( 'SP_Event' ) ) {
return null;
}
try {
return new SP_Event( $post->ID );
} catch ( Throwable $e ) {
return null;
}
}
/**
* Get the SportsPress event status with fallbacks.
*
* @param WP_Post $post Event post.
* @param object|null $event SportsPress event object.
* @return string
*/
function asc_sp_event_status( WP_Post $post, $event = null ) {
if ( $event && is_callable( array( $event, 'status' ) ) ) {
try {
$status = (string) $event->status();
if ( '' !== $status ) {
return $status;
}
} catch ( Throwable $e ) {
return '';
}
}
return 'future' === $post->post_status ? 'future' : '';
}
/**
* Get SportsPress result rows safely.
*
* @param object|null $event SportsPress event object.
* @return array
*/
function asc_sp_event_results( $event = null ) {
if ( ! $event || ! is_callable( array( $event, 'results' ) ) ) {
return array();
}
try {
$results = $event->results();
return is_array( $results ) ? $results : array();
} catch ( Throwable $e ) {
return array();
}
}
/**
* Convert a result row into outcome labels.
*
* @param array $result Result row.
* @return array
*/
function asc_sp_event_result_outcomes( array $result ) {
$result_outcome = isset( $result['outcome'] ) ? $result['outcome'] : null;
if ( ! is_array( $result_outcome ) ) {
return array();
}
$outcomes = array();
foreach ( $result_outcome as $outcome ) {
$the_outcome = get_page_by_path( $outcome, OBJECT, 'sp_outcome' );
if ( $the_outcome instanceof WP_Post ) {
$outcome_abbreviation = get_post_meta( $the_outcome->ID, 'sp_abbreviation', true );
if ( ! $outcome_abbreviation ) {
$outcome_abbreviation = function_exists( 'sp_substr' ) ? sp_substr( $the_outcome->post_title, 0, 1 ) : substr( $the_outcome->post_title, 0, 1 );
}
$outcomes[] = array(
'title' => $the_outcome->post_title,
'abbreviation' => $outcome_abbreviation,
);
}
}
return $outcomes;
}
/**
* Build a result title/description from SportsPress result data.
*
* @param WP_Post $post Event post.
* @param array $results Event results data.
* @param string $description Existing description.
* @return array{title:string,description:string}|null
*/
function asc_sp_event_result_meta( WP_Post $post, array $results, $description ) {
unset( $results[0] );
$results = array_filter( $results );
if ( count( $results ) < 2 ) {
return null;
}
if ( 'yes' === get_option( 'sportspress_event_reverse_teams', 'no' ) ) {
$results = array_reverse( $results, true );
}
$teams_result_array = array();
foreach ( $results as $team_id => $result ) {
if ( ! is_array( $result ) ) {
continue;
}
$outcomes = asc_sp_event_result_outcomes( $result );
$first_outcome = ! empty( $outcomes ) ? $outcomes[0] : array( 'title' => __( 'Result', 'tonys-sportspress-enhancements' ), 'abbreviation' => '' );
$team_name = asc_sp_team_short_name( $team_id );
$team_score = isset( $result['r'] ) && '' !== $result['r'] ? $result['r'] : null;
$team_score = null !== $team_score ? (string) $team_score : '';
if ( '' === $team_score ) {
continue;
}
$teams_result_array[] = array(
'score' => $team_score,
'outcome' => $first_outcome['title'],
'outcome_abbreviation' => $first_outcome['abbreviation'],
'team_name' => $team_name,
);
}
if ( count( $teams_result_array ) < 2 ) {
return null;
}
$special_result_suffix_abbreviation = '';
$special_result_suffix = '';
foreach ( $teams_result_array as $team ) {
$outcome_abbreviation = strtoupper( (string) $team['outcome_abbreviation'] );
if ( 'TF-W' === $outcome_abbreviation ) {
$special_result_suffix_abbreviation = 'TF-W';
$special_result_suffix = 'Technical Forfeit Win';
break;
}
if ( 'TF-L' === $outcome_abbreviation ) {
$special_result_suffix_abbreviation = 'TF';
$special_result_suffix = 'Technical Forfeit';
break;
}
if ( 'F-W' === $outcome_abbreviation || 'F-L' === $outcome_abbreviation ) {
$special_result_suffix_abbreviation = 'Forfeit';
$special_result_suffix = 'Forfeit';
break;
}
}
$publish_date = asc_generate_short_date( $post, false );
$title = sprintf(
'%1$s %2$s-%3$s %4$s%s',
$teams_result_array[0]['team_name'],
$teams_result_array[0]['score'],
$teams_result_array[1]['score'],
$teams_result_array[1]['team_name'],
$publish_date ? ' - ' . $publish_date : ''
);
if ( $special_result_suffix ) {
$title .= " ({$special_result_suffix_abbreviation})";
}
$description .= sprintf(
' %1$s (%2$s), %3$s (%4$s).',
$teams_result_array[0]['team_name'],
$teams_result_array[0]['outcome'],
$teams_result_array[1]['team_name'],
$teams_result_array[1]['outcome']
);
return array(
'title' => $title,
'description' => $description,
);
}
/**
* Build all Open Graph values for an event.
*
* @param int|WP_Post $post Event post or ID.
* @return array<string,mixed>
*/
function asc_sp_event_open_graph_data( $post ) {
$post = asc_sp_event_get_post( $post );
if ( ! $post ) {
return array();
}
$event = asc_sp_event_object( $post );
$venue_name = asc_sp_event_venue_name( $post );
$publish_date_and_time = get_the_date( 'F j, Y g:i A', $post );
$description = trim( "{$publish_date_and_time} at {$venue_name}." );
$title = asc_generate_sp_event_title( $post );
$sp_status = get_post_meta( $post->ID, 'sp_status', true );
$status = asc_sp_event_status( $post, $event );
if ( in_array( $sp_status, array( 'postponed', 'cancelled', 'tbd' ), true ) ) {
$status_label = strtoupper( $sp_status );
$description = "{$status_label} - {$description}";
$title = trim( "{$status_label} - {$title} - " . asc_generate_short_date( $post ) . " - {$venue_name}", ' -' );
} elseif ( 'future' === $status ) {
$title = trim( $title . ' - ' . asc_generate_short_date( $post ) . " - {$venue_name}", ' -' );
} elseif ( 'results' === $status ) {
$result_meta = asc_sp_event_result_meta( $post, asc_sp_event_results( $event ), $description );
if ( $result_meta ) {
$title = $result_meta['title'];
$description = $result_meta['description'];
}
}
$body_excerpt = asc_sp_event_body_excerpt( $post );
if ( '' !== $body_excerpt ) {
$description = trim( $description . ' ' . $body_excerpt );
}
return array(
'type' => 'article',
'images' => asc_sp_event_open_graph_images( $post ),
'image' => asc_sp_event_matchup_image_url( $post, 'wide' ),
'image_width' => '1200',
'image_height' => '628',
'title' => $title,
'description' => $description,
'url' => get_permalink( $post ),
);
}
/**
* Echo Open Graph meta tags for single SportsPress events.
* @return void
*/
function custom_open_graph_tags_with_sportspress_integration() {
if ( ! is_single() ) {
return;
}
$post = asc_sp_event_get_post();
global $post;
if ( ! $post ) {
if ( ! $post || $post->post_type !== 'sp_event' ) {
return;
}
$meta = asc_sp_event_open_graph_data( $post );
$event = new SP_Event( $post->ID );
$venue_terms = get_the_terms( $post->ID, 'sp_venue' );
$venue_name = ( $venue_terms && ! is_wp_error( $venue_terms ) ) ? $venue_terms[0]->name : 'Venue TBD';
$title = asc_generate_sp_event_title( $post );
$sp_status = get_post_meta( $post->ID, 'sp_status', true );
$status = $event->status();
$publish_date_and_time = get_the_date( 'F j, Y g:i A', $post );
$description = "{$publish_date_and_time} at {$venue_name}.";
if ( empty( $meta ) ) {
return;
if ( in_array( $sp_status, array( 'postponed', 'cancelled', 'tbd' ), true ) ) {
$label = strtoupper( $sp_status );
$description = "{$label}{$description}";
$title = "{$label}{$title}" . asc_generate_short_date( $post ) . "{$venue_name}";
} elseif ( 'future' === $status ) {
$title = $title . ' — ' . asc_generate_short_date( $post ) . ' — ' . $venue_name;
} elseif ( 'results' === $status ) {
$data = $event->results();
// First row is column labels; remove it.
unset( $data[0] );
$data = array_filter( $data );
if ( ! empty( $data ) ) {
if ( get_option( 'sportspress_event_reverse_teams', 'no' ) === 'yes' ) {
$data = array_reverse( $data, true );
}
$teams_result_array = array();
foreach ( $data as $team_id => $result ) {
$result_outcome = sp_array_value( $result, 'outcome' );
$the_outcome = null;
if ( is_array( $result_outcome ) ) {
foreach ( $result_outcome as $outcome_slug ) {
$found = get_page_by_path( $outcome_slug, OBJECT, 'sp_outcome' );
if ( is_object( $found ) ) {
$the_outcome = $found;
break;
}
}
}
unset( $result['outcome'] );
$outcome_title = $the_outcome ? $the_outcome->post_title : '';
$outcome_abbreviation = '';
if ( $the_outcome ) {
$outcome_abbreviation = get_post_meta( $the_outcome->ID, 'sp_abbreviation', true );
if ( ! $outcome_abbreviation ) {
$outcome_abbreviation = sp_substr( $the_outcome->post_title, 0, 1 );
}
}
$teams_result_array[] = array(
'result' => $result,
'outcome' => $outcome_title,
'outcome_abbreviation' => $outcome_abbreviation,
'team_name' => sp_team_short_name( $team_id ),
'team_abbreviation' => sp_team_abbreviation( $team_id ),
);
}
if ( count( $teams_result_array ) >= 2 ) {
$special_abbreviation = '';
$special_label = '';
foreach ( $teams_result_array as $team ) {
$abbr = strtoupper( $team['outcome_abbreviation'] );
if ( 'TF-W' === $abbr ) {
$special_abbreviation = 'TF-W';
$special_label = 'Technical Forfeit Win';
break;
} elseif ( 'TF-L' === $abbr ) {
$special_abbreviation = 'TF';
$special_label = 'Technical Forfeit';
break;
} elseif ( 'F-W' === $abbr || 'F-L' === $abbr ) {
$special_abbreviation = 'Forfeit';
$special_label = 'Forfeit';
break;
}
}
$short_date = asc_generate_short_date( $post, false );
$t0 = $teams_result_array[0];
$t1 = $teams_result_array[1];
$score = isset( $t0['result']['r'] ) && isset( $t1['result']['r'] )
? "{$t0['result']['r']}-{$t1['result']['r']}"
: '';
$suffix = $special_label ? " ({$special_abbreviation})" : '';
$title = "{$t0['team_name']} {$score} {$t1['team_name']}{$short_date}{$suffix}";
$description .= " {$t0['team_name']} ({$t0['outcome']}), {$t1['team_name']} ({$t1['outcome']}).";
}
}
}
echo '<meta property="og:type" content="' . esc_attr( $meta['type'] ) . '" />' . "\n";
foreach ( $meta['images'] as $image ) {
echo '<meta property="og:image" content="' . esc_url( $image['url'] ) . '" />' . "\n";
echo '<meta property="og:image:width" content="' . esc_attr( $image['width'] ) . '" />' . "\n";
echo '<meta property="og:image:height" content="' . esc_attr( $image['height'] ) . '" />' . "\n";
}
echo '<meta property="og:title" content="' . esc_attr( $meta['title'] ) . '" />' . "\n";
echo '<meta property="og:description" content="' . esc_attr( $meta['description'] ) . '" />' . "\n";
echo '<meta property="og:url" content="' . esc_url( $meta['url'] ) . '" />' . "\n";
$description .= ' ' . $post->post_content;
$image = asc_sp_event_matchup_image_url( $post );
echo '<meta property="og:type" content="article" />' . "\n";
echo '<meta property="og:image" content="' . esc_url( $image ) . '" />' . "\n";
echo '<meta property="og:title" content="' . esc_attr( $title ) . '" />' . "\n";
echo '<meta property="og:description" content="' . esc_attr( $description ) . '" />' . "\n";
echo '<meta property="og:url" content="' . esc_url( get_permalink() ) . '" />' . "\n";
}

View File

@@ -62,9 +62,7 @@ function tse_sp_event_export_get_column_definitions() {
'time' => __( 'Time', 'tonys-sportspress-enhancements' ),
'season' => __( 'Season', 'tonys-sportspress-enhancements' ),
'league' => __( 'League', 'tonys-sportspress-enhancements' ),
'title' => __( 'Title', 'tonys-sportspress-enhancements' ),
'team_name' => __( 'Team', 'tonys-sportspress-enhancements' ),
'separator' => __( 'Separator', 'tonys-sportspress-enhancements' ),
'opponent_name' => __( 'Opponent', 'tonys-sportspress-enhancements' ),
'location_flag' => __( 'Home/Away', 'tonys-sportspress-enhancements' ),
'field_name' => __( 'Field Name', 'tonys-sportspress-enhancements' ),
@@ -87,33 +85,12 @@ function tse_sp_event_export_get_column_definitions() {
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', 'title', 'field_name' ),
'team' => array( 'label', 'date', 'time', 'season', 'league', 'opponent_name', 'location_flag', 'field_name' ),
);
return isset( $defaults[ $format ] ) ? $defaults[ $format ] : $defaults['matchup'];
}
/**
* Get supported event title formats.
*
* @return string[]
*/
function tse_sp_event_export_get_title_formats() {
return array( 'selected_first', 'matchup' );
}
/**
* Sanitize event title format.
*
* @param string $format Raw title format.
* @return string
*/
function tse_sp_event_export_sanitize_title_format( $format ) {
$format = sanitize_key( (string) $format );
return in_array( $format, tse_sp_event_export_get_title_formats(), true ) ? $format : 'selected_first';
}
/**
* Sanitize an export format.
*
@@ -196,26 +173,24 @@ function tse_sp_event_export_parse_id_list( $value ) {
* @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();
$title_format = isset( $source['title_format'] ) ? tse_sp_event_export_sanitize_title_format( wp_unslash( $source['title_format'] ) ) : 'selected_first';
$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,
'title_format' => $title_format,
'columns' => isset( $source['columns'] ) ? tse_sp_event_export_sanitize_columns( $format, wp_unslash( $source['columns'] ) ) : tse_sp_event_export_get_default_columns( $format ),
'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 ),
);
}
@@ -237,6 +212,9 @@ function tse_sp_event_export_validate_filters( $filters ) {
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 ) );
}
}
/**
@@ -316,10 +294,11 @@ function tse_sp_event_export_query_posts( $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();
$title_format = tse_sp_event_export_sanitize_title_format( isset( $filters['title_format'] ) ? $filters['title_format'] : 'selected_first' );
$query_posts = tse_sp_event_export_query_posts( $filters );
$events = 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;
@@ -335,22 +314,21 @@ function tse_sp_event_export_get_events( $filters ) {
$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 );
$context = tse_sp_event_export_get_team_context( $home_id, $away_id, $selected_ids );
$context_team_id = isset( $context['team_id'] ) ? (int) $context['team_id'] : 0;
$opponent_id = isset( $context['opponent_id'] ) ? (int) $context['opponent_id'] : 0;
$separator = isset( $context['separator'] ) ? (string) $context['separator'] : '';
$location_flag = isset( $context['location_flag'] ) ? (string) $context['location_flag'] : '';
$title = tse_sp_event_export_get_event_title_value( $home_id, $away_id, $selected_ids, $title_format );
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 ) ) ),
'title' => $title,
'team_name' => $context_team_id > 0 ? get_the_title( $context_team_id ) : '',
'separator' => $separator,
'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 ) : '',
@@ -374,100 +352,6 @@ function tse_sp_event_export_get_events( $filters ) {
return $events;
}
/**
* Resolve a display title for a schedule event.
*
* @param int $home_id Home team ID.
* @param int $away_id Away team ID.
* @param int[] $selected_ids Selected team IDs.
* @param string $title_format Title format.
* @return string
*/
function tse_sp_event_export_get_event_title_value( $home_id, $away_id, $selected_ids, $title_format ) {
$home_id = absint( $home_id );
$away_id = absint( $away_id );
$selected_ids = array_values( array_filter( array_map( 'absint', (array) $selected_ids ) ) );
$title_format = tse_sp_event_export_sanitize_title_format( $title_format );
if ( 'matchup' === $title_format ) {
return tse_sp_event_export_join_title_parts( $away_id, 'at', $home_id );
}
$context = tse_sp_event_export_get_team_context( $home_id, $away_id, $selected_ids );
if ( empty( $context['team_id'] ) ) {
return '';
}
if ( 1 === count( $selected_ids ) ) {
$opponent_id = isset( $context['opponent_id'] ) ? (int) $context['opponent_id'] : 0;
return $opponent_id > 0 ? get_the_title( $opponent_id ) : '';
}
return tse_sp_event_export_join_title_parts(
isset( $context['team_id'] ) ? (int) $context['team_id'] : 0,
isset( $context['separator'] ) ? (string) $context['separator'] : '',
isset( $context['opponent_id'] ) ? (int) $context['opponent_id'] : 0
);
}
/**
* Join two team names with a title separator.
*
* @param int $first_id First team ID.
* @param string $separator Separator.
* @param int $second_id Second team ID.
* @return string
*/
function tse_sp_event_export_join_title_parts( $first_id, $separator, $second_id ) {
$first_name = $first_id > 0 ? get_the_title( $first_id ) : '';
$second_name = $second_id > 0 ? get_the_title( $second_id ) : '';
$parts = array_values( array_filter( array( $first_name, trim( $separator ), $second_name ), 'strlen' ) );
return implode( ' ', $parts );
}
/**
* Resolve the team-layout perspective for an event.
*
* The selected team is always first. If both selected teams are in the game,
* use the away team first and the home team second.
*
* @param int $home_id Home team ID.
* @param int $away_id Away team ID.
* @param int[] $selected_ids Selected team IDs.
* @return array
*/
function tse_sp_event_export_get_team_context( $home_id, $away_id, $selected_ids ) {
$home_id = absint( $home_id );
$away_id = absint( $away_id );
$selected_ids = array_values( array_filter( array_map( 'absint', (array) $selected_ids ) ) );
if ( $away_id > 0 && in_array( $away_id, $selected_ids, true ) ) {
return array(
'team_id' => $away_id,
'opponent_id' => $home_id,
'separator' => 'at',
'location_flag' => 'Away',
);
}
if ( $home_id > 0 && in_array( $home_id, $selected_ids, true ) ) {
return array(
'team_id' => $home_id,
'opponent_id' => $away_id,
'separator' => 'vs',
'location_flag' => 'Home',
);
}
return array(
'team_id' => 0,
'opponent_id' => 0,
'separator' => '',
'location_flag' => '',
);
}
/**
* Get event term names as a semicolon-delimited string.
*
@@ -592,22 +476,25 @@ function tse_sp_event_export_fold_ical_line( $line ) {
* @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();
$title_format = tse_sp_event_export_sanitize_title_format( isset( $filters['title_format'] ) ? $filters['title_format'] : 'selected_first' );
$team_id = isset( $team_ids[0] ) ? $team_ids[0] : 0;
$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;
$context = tse_sp_event_export_get_team_context( $home_id, $away_id, $team_ids );
if ( ! empty( $context['team_id'] ) ) {
$summary = tse_sp_event_export_get_event_title_value( $home_id, $away_id, $team_ids, $title_format );
if ( '' === $summary ) {
$summary = $event->post_title;
}
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 );
}
@@ -1028,14 +915,13 @@ 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'] ) : '',
'title_format' => isset( $filters['title_format'] ) ? tse_sp_event_export_sanitize_title_format( $filters['title_format'] ) : 'selected_first',
'columns' => implode( ',', $filters['columns'] ),
'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

@@ -1,81 +1,93 @@
<?php
/*
Plugin Name: Custom Event Permalinks
Description: Adds a custom permalink structure for the sp_event post type.
Version: 1.0
Author: Your Name
*/
/**
* Custom permalink structure for sp_event post type.
*
* @package Tonys_Sportspress_Enhancements
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
// Register custom rewrite rules
/**
* Register custom rewrite rules for sp_event.
*
* @return void
*/
function custom_event_rewrite_rules() {
add_rewrite_rule(
'(?:event|game)/.*?[/]?([0-9]+)[/]?$',
'index.php?post_type=sp_event&p=$matches[1]',
'top'
);
add_rewrite_rule(
'(?:event|game)/.*?[/]?([0-9]+)[/]?$',
'index.php?post_type=sp_event&p=$matches[1]',
'top'
);
}
add_action('init', 'custom_event_rewrite_rules');
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;
}
/**
* Customize the permalink structure for sp_event posts.
*
* @param string $permalink Existing permalink.
* @param WP_Post $post Post object.
* @return string
*/
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";
};
$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 );
// Get the teams associated with the event
$team_1 = get_post($teams[0]);
$team_2 = get_post($teams[1]);
if ( $seasons ) {
$seasons_slug = implode(
'-',
array_map( function( $season ) { return $season->slug; }, $seasons )
);
} else {
$seasons_slug = 'no-season';
}
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;
}
$team_1 = isset( $teams[0] ) ? get_post( $teams[0] ) : null;
$team_2 = isset( $teams[1] ) ? get_post( $teams[1] ) : null;
if ($team_1 && $team_2) {
$permalink = home_url($format_string ."/". $seasons_slug . '/' . $team_1->post_name . '-' . $team_2->post_name . '/' . $post->ID);
}
switch ( $format ) {
case 'league':
case 'tournament':
$format_string = 'game';
break;
default:
$format_string = 'event';
break;
}
return $permalink;
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);
add_filter( 'post_type_link', 'custom_event_permalink', 10, 2 );
// Flush rewrite rules on activation and deactivation
/**
* Flush rewrite rules on activation.
*
* @return void
*/
function custom_event_rewrite_flush() {
custom_event_rewrite_rules();
flush_rewrite_rules();
custom_event_rewrite_rules();
flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'custom_event_rewrite_flush');
register_deactivation_hook(__FILE__, '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.
/**
* Allow scheduled events to resolve on the frontend.
*
* @param WP_Query $query Current query.
* @return void
*/
function custom_event_parse_request( $query ) {
if ( ! $query instanceof WP_Query ) {
return;
@@ -98,4 +110,4 @@ function custom_event_parse_request( $query ) {
$query->set( 'p', $post_id );
$query->set( 'post_status', array( 'publish', 'future' ) );
}
add_action('pre_get_posts', 'custom_event_parse_request');
add_action( 'pre_get_posts', 'custom_event_parse_request' );

View File

@@ -83,7 +83,11 @@ if ( ! class_exists( 'Tony_Sportspress_GitHub_Updater' ) ) {
$remote_version = $this->normalize_version( $release['version'] );
$current_version = $this->normalize_version( TONY_SPORTSPRESS_ENHANCEMENTS_VERSION );
$plugin_data = (object) array(
if ( version_compare( $remote_version, $current_version, '<=' ) ) {
return $transient;
}
$transient->response[ $this->plugin_basename ] = (object) array(
'id' => $release['url'],
'slug' => $this->plugin_slug,
'plugin' => $this->plugin_basename,
@@ -98,16 +102,6 @@ if ( ! class_exists( 'Tony_Sportspress_GitHub_Updater' ) ) {
'translations' => array(),
);
if ( version_compare( $remote_version, $current_version, '<=' ) ) {
unset( $transient->response[ $this->plugin_basename ] );
$transient->no_update[ $this->plugin_basename ] = $plugin_data;
return $transient;
}
unset( $transient->no_update[ $this->plugin_basename ] );
$transient->response[ $this->plugin_basename ] = $plugin_data;
return $transient;
}

File diff suppressed because it is too large Load Diff

View File

@@ -86,13 +86,11 @@ function tse_sp_schedule_exporter_render_shortcode() {
$seasons = tse_sp_schedule_exporter_get_seasons();
$season_id = tse_sp_schedule_exporter_resolve_season_id( $seasons );
$teams = tse_sp_schedule_exporter_get_teams( $league_id, $season_id );
$team_ids = tse_sp_schedule_exporter_resolve_team_ids( $teams );
$fields = tse_sp_schedule_exporter_get_fields();
$field_id = tse_sp_schedule_exporter_resolve_field_id( $fields );
$export_type = tse_sp_schedule_exporter_resolve_export_type();
$subformat = tse_sp_schedule_exporter_resolve_subformat();
$title_format = tse_sp_schedule_exporter_resolve_title_format();
$black_white = tse_sp_schedule_exporter_resolve_black_white();
$team_id = tse_sp_schedule_exporter_resolve_team_id( $teams );
$fields = tse_sp_schedule_exporter_get_fields();
$field_id = tse_sp_schedule_exporter_resolve_field_id( $fields );
$export_type = tse_sp_schedule_exporter_resolve_export_type();
$subformat = tse_sp_schedule_exporter_resolve_subformat();
if ( empty( $teams ) ) {
return '<p>' . esc_html__( 'No SportsPress teams match the selected league and season.', 'tonys-sportspress-enhancements' ) . '</p>';
@@ -117,15 +115,6 @@ function tse_sp_schedule_exporter_render_shortcode() {
<p class="description"><?php esc_html_e( 'CSV builds a feed URL, iCal Link builds a subscription URL, and Printable opens the printable page.', 'tonys-sportspress-enhancements' ); ?></p>
</div>
<div data-title-format-wrap="1" style="margin-bottom:16px;">
<label for="tse-public-title-format"><strong><?php esc_html_e( 'Title Format', 'tonys-sportspress-enhancements' ); ?></strong></label><br />
<select id="tse-public-title-format" name="title_format">
<option value="selected_first" <?php selected( $title_format, 'selected_first' ); ?>><?php esc_html_e( 'Selected Teams First', 'tonys-sportspress-enhancements' ); ?></option>
<option value="matchup" <?php selected( $title_format, 'matchup' ); ?>><?php esc_html_e( 'Matchup', 'tonys-sportspress-enhancements' ); ?></option>
</select>
<p class="description"><?php esc_html_e( 'Selected Teams First shows only the opponent when one team is selected; Matchup always shows away at home.', 'tonys-sportspress-enhancements' ); ?></p>
</div>
<div data-subformat-wrap="1" style="margin-bottom:16px;">
<label for="tse-public-subformat"><strong><?php esc_html_e( 'CSV Layout', 'tonys-sportspress-enhancements' ); ?></strong></label><br />
<select id="tse-public-subformat" name="subformat">
@@ -135,7 +124,7 @@ function tse_sp_schedule_exporter_render_shortcode() {
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e( 'Matchup is away vs home. Team puts a selected team first with vs or at as the separator.', 'tonys-sportspress-enhancements' ); ?></p>
<p class="description"><?php esc_html_e( 'Matchup is away vs home. Team is opponent-based and requires one specific team.', 'tonys-sportspress-enhancements' ); ?></p>
</div>
<div style="margin-bottom:16px;">
@@ -163,14 +152,14 @@ function tse_sp_schedule_exporter_render_shortcode() {
<div style="margin-bottom:16px;">
<label for="tse-public-team"><strong><?php esc_html_e( 'Team', 'tonys-sportspress-enhancements' ); ?></strong></label><br />
<select id="tse-public-team" name="team_id[]" multiple="multiple" size="<?php echo esc_attr( (string) min( 8, max( 3, count( $teams ) ) ) ); ?>">
<select id="tse-public-team" name="team_id">
<option value="0"><?php esc_html_e( 'All teams', 'tonys-sportspress-enhancements' ); ?></option>
<?php foreach ( $teams as $team ) : ?>
<option value="<?php echo esc_attr( (string) $team->ID ); ?>" <?php selected( in_array( (int) $team->ID, $team_ids, true ) ); ?>>
<option value="<?php echo esc_attr( (string) $team->ID ); ?>" <?php selected( $team_id, (int) $team->ID ); ?>>
<?php echo esc_html( $team->post_title ); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e( 'Select one or more teams. Team layout keeps one selected team first for each game.', 'tonys-sportspress-enhancements' ); ?></p>
</div>
<div style="margin-bottom:16px;">
@@ -185,36 +174,15 @@ function tse_sp_schedule_exporter_render_shortcode() {
</select>
</div>
<div style="margin-bottom:16px;">
<label for="tse-public-month-pages" style="display:inline-flex;align-items:center;gap:6px;">
<input id="tse-public-month-pages" type="checkbox" name="month_pages" value="1" <?php checked( isset( $_GET['month_pages'] ) && '1' === (string) wp_unslash( $_GET['month_pages'] ) ); ?> />
<strong><?php esc_html_e( 'Print each month on its own page', 'tonys-sportspress-enhancements' ); ?></strong>
</label>
</div>
<div style="margin-bottom:16px;">
<label for="tse-public-black-white" style="display:inline-flex;align-items:center;gap:6px;">
<input id="tse-public-black-white" type="checkbox" name="black_white" value="1" <?php checked( $black_white ); ?> />
<strong><?php esc_html_e( 'High-legibility greyscale printable calendar', 'tonys-sportspress-enhancements' ); ?></strong>
</label>
</div>
</form>
<?php tse_sp_schedule_exporter_render_column_picker( 'matchup', 'public', $subformat ); ?>
<?php tse_sp_schedule_exporter_render_column_picker( 'team', 'public', $subformat ); ?>
<?php
$feed_args = array(
'team_id' => $team_ids,
'season_id' => $season_id,
'league_id' => $league_id,
'field_id' => $field_id,
'title_format' => $title_format,
);
$csv_url = tse_sp_event_export_get_feed_url( array_merge( $feed_args, array( 'format' => $subformat ) ), 'csv' );
$ics_url = tse_sp_event_export_get_feed_url( $feed_args, 'ics' );
$print_url = tse_sp_schedule_exporter_get_printable_url( $team_ids, $season_id, 'letter', $league_id, false, $field_id, false, 'name', 'name', $title_format, $black_white );
$csv_url = tse_sp_event_export_get_feed_url( array( 'team_id' => $team_id, 'season_id' => $season_id, 'league_id' => $league_id, 'field_id' => $field_id, 'format' => $subformat ), 'csv' );
$ics_url = tse_sp_event_export_get_feed_url( array( 'team_id' => $team_id, 'season_id' => $season_id, 'league_id' => $league_id, 'field_id' => $field_id ), 'ics' );
$print_url = tse_sp_schedule_exporter_get_printable_url( $team_id, $season_id, 'letter', $league_id );
$current_url = tse_sp_schedule_exporter_get_output_url( $export_type, $csv_url, $ics_url, $print_url );
?>
<div style="display:flex;align-items:center;gap:8px;max-width:100%;margin-top:16px;">
@@ -363,34 +331,26 @@ function tse_sp_schedule_exporter_get_teams( $league_id = 0, $season_id = 0 ) {
}
/**
* Resolve selected team IDs.
* Resolve selected team ID.
*
* @param WP_Post[] $teams Team posts.
* @return int[]
* @return int
*/
function tse_sp_schedule_exporter_resolve_team_ids( $teams ) {
$available = array();
foreach ( $teams as $team ) {
if ( $team instanceof WP_Post ) {
$available[] = (int) $team->ID;
function tse_sp_schedule_exporter_resolve_team_id( $teams ) {
$requested = isset( $_GET['team_id'] ) ? absint( wp_unslash( $_GET['team_id'] ) ) : 0;
if ( $requested > 0 && 'sp_team' === get_post_type( $requested ) ) {
foreach ( $teams as $team ) {
if ( $team instanceof WP_Post && (int) $team->ID === $requested ) {
return $requested;
}
}
}
$requested = array();
if ( isset( $_GET['team_id'] ) ) {
$requested = tse_sp_event_export_parse_id_list( wp_unslash( $_GET['team_id'] ) );
if ( isset( $teams[0] ) && $teams[0] instanceof WP_Post ) {
return (int) $teams[0]->ID;
}
$selected = array_values( array_intersect( $requested, $available ) );
if ( ! empty( $selected ) ) {
return $selected;
}
if ( isset( $available[0] ) ) {
return array( (int) $available[0] );
}
return array();
return 0;
}
/**
@@ -583,38 +543,6 @@ function tse_sp_schedule_exporter_resolve_subformat() {
return tse_sp_event_export_sanitize_format( $requested );
}
/**
* Resolve selected title format.
*
* @return string
*/
function tse_sp_schedule_exporter_resolve_title_format() {
$requested = isset( $_GET['title_format'] ) ? sanitize_key( wp_unslash( $_GET['title_format'] ) ) : 'selected_first';
return tse_sp_schedule_exporter_resolve_title_format_value( $requested );
}
/**
* Normalize a title format value.
*
* @param string $value Raw value.
* @return string
*/
function tse_sp_schedule_exporter_resolve_title_format_value( $value ) {
$value = sanitize_key( (string) $value );
return in_array( $value, tse_sp_event_export_get_title_formats(), true ) ? $value : 'selected_first';
}
/**
* Resolve whether printable URLs should use black-and-white styling.
*
* @return bool
*/
function tse_sp_schedule_exporter_resolve_black_white() {
return isset( $_GET['black_white'] ) && '1' === (string) wp_unslash( $_GET['black_white'] );
}
/**
* Get current output URL for the selected export type.
*
@@ -772,29 +700,17 @@ function tse_sp_schedule_exporter_get_primary_venue( $event_id ) {
* @param int $season_id Season ID.
* @param string $paper Paper size.
* @param int $league_id League ID.
* @param string $team_label_mode Team label mode.
* @param string $field_label_mode Field label mode.
* @param string $title_format Title format.
* @param bool $black_white Whether to use black-and-white printable styling.
* @return string
*/
function tse_sp_schedule_exporter_get_printable_url( $team_id, $season_id, $paper, $league_id = 0, $autoprint = false, $field_id = 0, $month_pages = false, $team_label_mode = 'name', $field_label_mode = 'name', $title_format = 'selected_first', $black_white = false ) {
$team_ids = is_array( $team_id ) ? array_values( array_filter( array_map( 'absint', $team_id ) ) ) : array( absint( $team_id ) );
function tse_sp_schedule_exporter_get_printable_url( $team_id, $season_id, $paper, $league_id = 0, $autoprint = false ) {
return add_query_arg(
array(
Tony_Sportspress_Printable_Calendars::QUERY_FLAG => '1',
'sp_team' => implode( ',', $team_ids ),
'sp_team' => (string) absint( $team_id ),
'sp_season' => $season_id > 0 ? (string) absint( $season_id ) : '',
'sp_league' => $league_id > 0 ? (string) absint( $league_id ) : '',
'sp_field' => $field_id > 0 ? (string) absint( $field_id ) : '',
'team_label' => sanitize_key( (string) $team_label_mode ),
'field_label' => sanitize_key( (string) $field_label_mode ),
'title_format' => tse_sp_schedule_exporter_resolve_title_format_value( $title_format ),
'paper' => $paper,
'autoprint' => $autoprint ? '1' : '',
'month_pages' => $month_pages ? '1' : '',
'black_white' => $black_white ? '1' : '',
),
home_url( '/' )
);
@@ -842,28 +758,17 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
var league = form.querySelector('[name="league_id"]');
var season = form.querySelector('[name="season_id"]');
var team = form.querySelector('[name="team_id[]"], [name="team_id"]');
var team = form.querySelector('[name="team_id"]');
var exportType = form.querySelector('[name="export_type"]');
var subformat = form.querySelector('[name="subformat"]');
var titleFormat = form.querySelector('[name="title_format"]');
var field = form.querySelector('[name="field_id"]');
var monthPages = form.querySelector('[name="month_pages"]');
var blackWhite = form.querySelector('[name="black_white"]');
var outputUrl = scope.querySelector('.tse-output-url');
var openButton = scope.querySelector('.tse-open-link');
var iosButton = scope.querySelector('.tse-ics-ios-link');
var androidButton = scope.querySelector('.tse-ics-android-link');
var outputNote = scope.querySelector('.tse-output-note');
var copyButton = scope.querySelector('.tse-copy-link');
var teamValues = team ? Array.prototype.slice.call(team.selectedOptions || []).map(function(option){
return option.value;
}).filter(function(value){
return value && value !== '0';
}) : [];
if (!teamValues.length && team && team.value && team.value !== '0') {
teamValues = [team.value];
}
var teamValue = teamValues.length ? teamValues.join(',') : '0';
var teamValue = team ? (team.value || '0') : '0';
var activeSubformat = subformat ? (subformat.value || 'matchup') : 'matchup';
var selectedExportType = exportType ? (exportType.value || 'csv') : 'csv';
@@ -887,7 +792,6 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
if (season) csvUrl.searchParams.set('season_id', season.value || '0');
if (team) csvUrl.searchParams.set('team_id', teamValue);
if (field) csvUrl.searchParams.set('field_id', field.value || '0');
if (titleFormat) csvUrl.searchParams.set('title_format', titleFormat.value || 'selected_first');
csvUrl.searchParams.set('format', activeSubformat);
var columns = Array.prototype.slice.call(scope.querySelectorAll('[data-columns-format="' + activeSubformat + '"]:checked')).map(function(input){
return input.value;
@@ -904,7 +808,6 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
if (season) icsUrl.searchParams.set('season_id', season.value || '0');
if (team) icsUrl.searchParams.set('team_id', teamValue);
if (field) icsUrl.searchParams.set('field_id', field.value || '0');
if (titleFormat) icsUrl.searchParams.set('title_format', titleFormat.value || 'selected_first');
icsUrl.searchParams.delete('format');
icsUrl.searchParams.delete('columns');
}
@@ -913,18 +816,6 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
if (league) printUrl.searchParams.set('sp_league', league.value || '0');
if (season) printUrl.searchParams.set('sp_season', season.value || '0');
if (team) printUrl.searchParams.set('sp_team', teamValue);
if (field) printUrl.searchParams.set('sp_field', field.value || '0');
if (titleFormat) printUrl.searchParams.set('title_format', titleFormat.value || 'selected_first');
if (monthPages && monthPages.checked) {
printUrl.searchParams.set('month_pages', '1');
} else {
printUrl.searchParams.delete('month_pages');
}
if (blackWhite && blackWhite.checked) {
printUrl.searchParams.set('black_white', '1');
} else {
printUrl.searchParams.delete('black_white');
}
printUrl.searchParams.set('paper', 'letter');
}
@@ -942,11 +833,11 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
resolvedUrl = printUrl.toString();
if (teamValue === '0') {
disabled = true;
note = 'Printable requires at least one selected team.';
note = 'Printable requires a specific team. All teams is not supported.';
}
} else if (selectedExportType === 'csv' && activeSubformat === 'team' && teamValue === '0') {
disabled = true;
note = 'CSV team layout requires at least one selected team.';
note = 'CSV team layout requires a specific team. All teams is not supported.';
}
if (outputUrl) {
@@ -994,10 +885,10 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
syncLinks(scope);
scope.querySelectorAll('.tse-schedule-exporter-form select, .tse-schedule-exporter-form input[type="checkbox"]').forEach(function(input){
input.addEventListener('change', function(){
if (input.dataset.autoSubmit === '1') {
input.form.submit();
scope.querySelectorAll('.tse-schedule-exporter-form select').forEach(function(select){
select.addEventListener('change', function(){
if (select.dataset.autoSubmit === '1') {
select.form.submit();
return;
}

View File

@@ -160,6 +160,8 @@ function tse_sp_url_builder_render_script() {
}
var baseUrl = <?php echo wp_json_encode( $base_url ); ?>;
var labelOpenIcs = <?php echo wp_json_encode( __( 'Open ICS Feed', 'tonys-sportspress-enhancements' ) ); ?>;
var labelOpenCsv = <?php echo wp_json_encode( __( 'Open Feed URL', 'tonys-sportspress-enhancements' ) ); ?>;
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');
@@ -223,7 +225,7 @@ function tse_sp_url_builder_render_script() {
output.value = url.toString();
openLink.href = url.toString();
openLink.textContent = selectedFeedType === 'ics' ? 'Open ICS Feed' : 'Open Feed URL';
openLink.textContent = selectedFeedType === 'ics' ? labelOpenIcs : labelOpenCsv;
}
syncColumnGroups();

View File

@@ -2,7 +2,7 @@
/**
* Venue term metadata support.
*
* Adds short name, abbreviation, and ground rules fields to SportsPress venues.
* Adds short name and abbreviation fields to SportsPress venues.
*/
if ( ! defined( 'ABSPATH' ) ) {
@@ -40,319 +40,9 @@ function tony_sportspress_register_venue_term_meta() {
},
)
);
register_term_meta(
'sp_venue',
'tse_ground_rules',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => 'wp_kses_post',
'show_in_rest' => true,
'auth_callback' => static function() {
return current_user_can( 'manage_categories' );
},
)
);
}
add_action( 'init', 'tony_sportspress_register_venue_term_meta' );
/**
* Determine whether the field page should show its event list.
*
* The setting is stored in the core SportsPress Events settings page under
* the venue/field section.
*
* @return bool
*/
function tony_sportspress_field_event_list_enabled() {
$enabled = get_option( 'sportspress_event_show_venue_list', 'yes' ) === 'yes';
return (bool) apply_filters( 'tony_sportspress_field_event_list_enabled', $enabled );
}
/**
* Get the venue map caption text.
*
* @return string
*/
function tony_sportspress_get_venue_map_caption() {
return (string) apply_filters( 'tony_sportspress_venue_map_caption', __( 'Field Map', 'tonys-sportspress-enhancements' ) );
}
/**
* Add the field page event list setting to SportsPress > Settings > Games > Fields.
*
* @param array $options Existing venue settings.
* @return array
*/
function tony_sportspress_add_venue_settings( $options ) {
$options[] = array(
'title' => esc_attr__( 'Event List', 'tonys-sportspress-enhancements' ),
'desc' => esc_attr__( 'Display event list on field pages', 'tonys-sportspress-enhancements' ),
'id' => 'sportspress_event_show_venue_list',
'default' => 'yes',
'type' => 'checkbox',
);
return $options;
}
add_filter( 'sportspress_venue_options', 'tony_sportspress_add_venue_settings' );
/**
* Enqueue the visual editor on the venue taxonomy screen.
*
* This turns the built-in description textarea into the standard WordPress
* TinyMCE editor so venue descriptions can use markup and links.
*
* @param string $hook_suffix Current admin page hook.
*/
function tony_sportspress_enqueue_venue_description_editor( $hook_suffix ) {
if ( ! in_array( $hook_suffix, array( 'edit-tags.php', 'term.php' ), true ) ) {
return;
}
$taxonomy = isset( $_GET['taxonomy'] ) ? sanitize_key( wp_unslash( $_GET['taxonomy'] ) ) : '';
if ( 'sp_venue' !== $taxonomy ) {
return;
}
if ( ! function_exists( 'wp_enqueue_editor' ) ) {
return;
}
wp_enqueue_editor();
$script = <<<'JS'
(function() {
function initEditor(id) {
var textarea = document.getElementById(id);
if (!textarea || textarea.dataset.tseEditorInitialized) {
return;
}
textarea.dataset.tseEditorInitialized = '1';
window.wp.editor.initialize(id, {
tinymce: {
wpautop: true,
menubar: false,
statusbar: true,
toolbar1: 'formatselect,bold,italic,bullist,numlist,blockquote,alignleft,aligncenter,alignright,link,unlink,wp_more,spellchecker,fullscreen',
toolbar2: 'strikethrough,hr,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
block_formats: 'Paragraph=p; Heading 2=h2; Heading 3=h3; Heading 4=h4; Preformatted=pre',
},
quicktags: true,
mediaButtons: true
});
}
document.addEventListener('DOMContentLoaded', function() {
if (!window.wp || !wp.editor) {
return;
}
// Different taxonomy screens use different IDs for the same description field.
initEditor('description');
initEditor('tag-description');
});
})();
JS;
wp_add_inline_script( 'editor', $script );
}
add_action( 'admin_enqueue_scripts', 'tony_sportspress_enqueue_venue_description_editor' );
/**
* Hide the built-in venue archive description output.
*
* Venue content is rendered as SportsPress-style sections instead of inside
* the archive header.
*
* @param string $description Archive description HTML.
* @return string
*/
function tony_sportspress_hide_venue_archive_description( $description ) {
if ( is_tax( 'sp_venue' ) ) {
return '';
}
return $description;
}
add_filter( 'get_the_archive_description', 'tony_sportspress_hide_venue_archive_description', 99 );
/**
* Determine whether the current venue has map coordinates.
*
* @return bool
*/
function tony_sportspress_current_venue_has_map() {
$term = get_queried_object();
if ( ! $term instanceof WP_Term ) {
return false;
}
$latitude = trim( (string) get_term_meta( $term->term_id, 'sp_latitude', true ) );
$longitude = trim( (string) get_term_meta( $term->term_id, 'sp_longitude', true ) );
return '' !== $latitude && '' !== $longitude;
}
/**
* Render the venue ground rules section.
*
* @return void
*/
function tony_sportspress_render_venue_ground_rules_section() {
if ( ! is_tax( 'sp_venue' ) ) {
return;
}
$term = get_queried_object();
if ( ! $term instanceof WP_Term ) {
return;
}
$ground_rules = get_term_meta( $term->term_id, 'tse_ground_rules', true );
if ( ! is_string( $ground_rules ) || '' === trim( $ground_rules ) ) {
return;
}
$ground_rules = apply_filters( 'the_content', $ground_rules );
echo '<div class="sp-section-content sp-section-content-details"><div class="sp-template sp-template-venue-details tse-ground-rules"><h4 class="sp-table-caption">' . esc_html__( 'Ground Rules', 'tonys-sportspress-enhancements' ) . '</h4><div class="sp-table-wrapper tse-ground-rules-content">' . $ground_rules . '</div></div></div>';
}
add_action( 'sportspress_before_venue_map', 'tony_sportspress_render_venue_ground_rules_section', 5 );
/**
* Open the venue map section wrapper.
*
* @return void
*/
function tony_sportspress_open_venue_map_section() {
if ( ! is_tax( 'sp_venue' ) || ! tony_sportspress_current_venue_has_map() ) {
return;
}
$GLOBALS['tse_venue_map_section_open'] = true;
echo '<div class="sp-section-content sp-section-content-venue"><div class="sp-template sp-template-venue-map"><h4 class="sp-table-caption tse-venue-map-caption">' . esc_html( tony_sportspress_get_venue_map_caption() ) . '</h4><div class="sp-table-wrapper tse-venue-map-content">';
}
add_action( 'sportspress_before_venue_map', 'tony_sportspress_open_venue_map_section', 15 );
/**
* Close the venue map section wrapper.
*
* @return void
*/
function tony_sportspress_close_venue_map_section() {
if ( empty( $GLOBALS['tse_venue_map_section_open'] ) ) {
return;
}
$GLOBALS['tse_venue_map_section_open'] = false;
echo '</div></div></div>';
}
add_action( 'sportspress_after_venue_map', 'tony_sportspress_close_venue_map_section', 5 );
/**
* Enqueue venue section styles.
*
* The venue map and ground rules are rendered as SportsPress-style sections
* rather than archive header content, so they need section-scoped typography
* and list styling.
*
* @return void
*/
function tony_sportspress_enqueue_venue_section_styles() {
if ( ! is_tax( 'sp_venue' ) ) {
return;
}
wp_register_style( 'tony-sportspress-venue-sections', false, array(), TONY_SPORTSPRESS_ENHANCEMENTS_VERSION );
wp_enqueue_style( 'tony-sportspress-venue-sections' );
$event_list_display = tony_sportspress_field_event_list_enabled() ? 'block' : 'none';
$css = <<<CSS
body.tax-sp_venue .sp-template-venue-map .sp-table-wrapper,
body.tax-sp_venue .sp-template-venue-details .sp-table-wrapper {
background: #fff;
border: 1px solid #e0e0e0;
border-top: 0;
}
body.tax-sp_venue .site-main article.sp_event,
body.tax-sp_venue .sp-template-event-list,
body.tax-sp_venue .sp-template-event-blocks,
body.tax-sp_venue .sp-template-event-logos,
body.tax-sp_venue .sp-template-event-results {
display: {$event_list_display};
}
body.tax-sp_venue .sp-template-venue-map .sp-google-map {
display: block;
margin: 0;
}
body.tax-sp_venue .tse-ground-rules-content {
line-height: 1.7;
padding: 1em 15px 0;
}
body.tax-sp_venue .tse-ground-rules-content h2,
body.tax-sp_venue .tse-ground-rules-content h3,
body.tax-sp_venue .tse-ground-rules-content h4,
body.tax-sp_venue .tse-ground-rules-content h5,
body.tax-sp_venue .tse-ground-rules-content h6 {
margin: 1.25em 0 0.55em;
font-weight: 700;
line-height: 1.25;
text-transform: none;
}
body.tax-sp_venue .tse-ground-rules-content h2 {
font-size: 2rem;
}
body.tax-sp_venue .tse-ground-rules-content h3 {
font-size: 1.75rem;
}
body.tax-sp_venue .tse-ground-rules-content h4 {
font-size: 1.5rem;
}
body.tax-sp_venue .tse-ground-rules-content p,
body.tax-sp_venue .tse-ground-rules-content ul,
body.tax-sp_venue .tse-ground-rules-content ol,
body.tax-sp_venue .tse-ground-rules-content blockquote,
body.tax-sp_venue .tse-ground-rules-content pre {
margin: 0 0 1em;
}
body.tax-sp_venue .tse-ground-rules-content ul,
body.tax-sp_venue .tse-ground-rules-content ol {
margin-left: 1.5em;
padding-left: 1.5em;
list-style-position: outside;
}
body.tax-sp_venue .tse-ground-rules-content ul {
list-style: disc;
}
body.tax-sp_venue .tse-ground-rules-content ol {
list-style: decimal;
}
body.tax-sp_venue .tse-ground-rules-content li {
margin: 0.35em 0;
}
CSS;
wp_add_inline_style( 'tony-sportspress-venue-sections', $css );
}
add_action( 'wp_enqueue_scripts', 'tony_sportspress_enqueue_venue_section_styles' );
/**
* Render add-form fields for venue metadata.
*/
@@ -368,24 +58,6 @@ function tony_sportspress_add_venue_meta_fields() {
<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>
<div class="form-field term-ground-rules-wrap">
<label for="tse_ground_rules"><?php esc_html_e( 'Ground Rules', 'tonys-sportspress-enhancements' ); ?></label>
<?php
wp_editor(
'',
'tse_ground_rules',
array(
'textarea_name' => 'tse_ground_rules',
'textarea_rows' => 10,
'media_buttons' => true,
'teeny' => false,
'quicktags' => true,
'drag_drop_upload' => true,
)
);
?>
<p><?php esc_html_e( 'Supports headings, lists, links, and images.', 'tonys-sportspress-enhancements' ); ?></p>
</div>
<?php
}
add_action( 'sp_venue_add_form_fields', 'tony_sportspress_add_venue_meta_fields' );
@@ -398,7 +70,6 @@ add_action( 'sp_venue_add_form_fields', 'tony_sportspress_add_venue_meta_fields'
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 );
$ground_rules = get_term_meta( $term->term_id, 'tse_ground_rules', true );
?>
<tr class="form-field term-short-name-wrap">
<th scope="row">
@@ -418,28 +89,6 @@ function tony_sportspress_edit_venue_meta_fields( $term ) {
<p class="description"><?php esc_html_e( 'Optional abbreviation such as CC East or Field 1.', 'tonys-sportspress-enhancements' ); ?></p>
</td>
</tr>
<tr class="form-field term-ground-rules-wrap">
<th scope="row">
<label for="tse_ground_rules"><?php esc_html_e( 'Ground Rules', 'tonys-sportspress-enhancements' ); ?></label>
</th>
<td>
<?php
wp_editor(
(string) $ground_rules,
'tse_ground_rules',
array(
'textarea_name' => 'tse_ground_rules',
'textarea_rows' => 10,
'media_buttons' => true,
'teeny' => false,
'quicktags' => true,
'drag_drop_upload' => true,
)
);
?>
<p class="description"><?php esc_html_e( 'Supports headings, lists, links, and images.', 'tonys-sportspress-enhancements' ); ?></p>
</td>
</tr>
<?php
}
add_action( 'sp_venue_edit_form_fields', 'tony_sportspress_edit_venue_meta_fields' );
@@ -460,12 +109,8 @@ function tony_sportspress_save_venue_meta_fields( $term_id ) {
$abbreviation = isset( $_POST['tse_abbreviation'] ) ? sanitize_text_field( wp_unslash( $_POST['tse_abbreviation'] ) ) : '';
$abbreviation = is_string( $abbreviation ) ? trim( $abbreviation ) : '';
$ground_rules = isset( $_POST['tse_ground_rules'] ) ? wp_kses_post( wp_unslash( $_POST['tse_ground_rules'] ) ) : '';
$ground_rules = is_string( $ground_rules ) ? trim( $ground_rules ) : '';
update_term_meta( $term_id, 'tse_short_name', $short_name );
update_term_meta( $term_id, 'tse_abbreviation', $abbreviation );
update_term_meta( $term_id, 'tse_ground_rules', $ground_rules );
}
add_action( 'created_sp_venue', 'tony_sportspress_save_venue_meta_fields' );
add_action( 'edited_sp_venue', 'tony_sportspress_save_venue_meta_fields' );

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,222 +0,0 @@
<?php
/**
* Tests for the SportsPress event image generator.
*
* @package Tonys_Sportspress_Enhancements
*/
/**
* Featured image generator tests.
*/
class Test_Featured_Image_Generator extends WP_UnitTestCase {
/**
* Temp files created during a test.
*
* @var string[]
*/
private $temp_files = array();
/**
* Clean up temp files.
*/
public function tear_down(): void {
foreach ( $this->temp_files as $file ) {
if ( file_exists( $file ) ) {
unlink( $file );
}
}
$this->temp_files = array();
parent::tear_down();
}
/**
* Create a post.
*
* @param string $type Post type.
* @param string $title Title.
* @return int
*/
private function create_post_of_type( $type, $title ) {
return self::factory()->post->create(
array(
'post_type' => $type,
'post_title' => $title,
'post_status' => 'publish',
)
);
}
/**
* Create a small raster fixture.
*
* @param string $extension File extension.
* @return string
*/
private function create_raster_fixture( $extension ) {
$image = imagecreatetruecolor( 24, 24 );
$red = imagecolorallocate( $image, 200, 0, 0 );
imagefilledrectangle( $image, 0, 0, 23, 23, $red );
$file = tempnam( sys_get_temp_dir(), 'sp-img-' );
$path = $file . '.' . $extension;
rename( $file, $path );
switch ( $extension ) {
case 'jpg':
imagejpeg( $image, $path );
break;
case 'gif':
imagegif( $image, $path );
break;
case 'webp':
imagewebp( $image, $path );
break;
case 'png':
default:
imagepng( $image, $path );
break;
}
asc_sp_event_image_destroy( $image );
$this->temp_files[] = $path;
return $path;
}
/**
* Invalid IDs and non-event posts produce request errors.
*/
public function test_invalid_and_non_event_requests_prepare_404_errors() {
$this->assertWPError( asc_sp_event_prepare_image_request( 999999 ) );
$post_id = $this->create_post_of_type( 'post', 'Regular Post' );
$error = asc_sp_event_prepare_image_request( $post_id );
$this->assertWPError( $error );
$this->assertSame( 'invalid_event', $error->get_error_code() );
}
/**
* Missing team logo paths fall back to generated text and valid dimensions.
*/
public function test_missing_logo_path_generates_png_with_expected_dimensions() {
$image_data = generate_bisected_image( '#123456', '#abcdef', '/missing-left.png', '/missing-right.png', 'Hawks', 'Electrons' );
$image = imagecreatefromstring( $image_data );
$this->assertNotFalse( $image );
$this->assertSame( 1200, imagesx( $image ) );
$this->assertSame( 628, imagesy( $image ) );
asc_sp_event_image_destroy( $image );
}
/**
* Square image variant generates square PNG dimensions.
*/
public function test_square_variant_generates_expected_dimensions() {
$dimensions = asc_sp_event_image_variant_dimensions( 'square' );
$image_data = generate_bisected_image( '#123456', '#abcdef', '/missing-left.png', '/missing-right.png', 'Hawks', 'Electrons', $dimensions['width'], $dimensions['height'] );
$image = imagecreatefromstring( $image_data );
$this->assertNotFalse( $image );
$this->assertSame( 1200, imagesx( $image ) );
$this->assertSame( 1200, imagesy( $image ) );
asc_sp_event_image_destroy( $image );
}
/**
* Raster loader supports common GD-backed formats.
*/
public function test_raster_loader_supports_common_formats_when_available() {
$formats = array(
'png' => 'imagecreatefrompng',
'jpg' => 'imagecreatefromjpeg',
'gif' => 'imagecreatefromgif',
);
if ( function_exists( 'imagewebp' ) && function_exists( 'imagecreatefromwebp' ) ) {
$formats['webp'] = 'imagecreatefromwebp';
}
foreach ( $formats as $extension => $function ) {
if ( ! function_exists( $function ) ) {
continue;
}
$path = $this->create_raster_fixture( $extension );
$image = asc_sp_event_image_create_from_file( $path );
$this->assertNotFalse( $image, "Failed loading {$extension}" );
$this->assertSame( 24, imagesx( $image ) );
$this->assertSame( 24, imagesy( $image ) );
asc_sp_event_image_destroy( $image );
}
}
/**
* Bundled sporty font is available for fallback text.
*/
public function test_bundled_bebas_neue_font_is_available() {
$this->assertFileExists( asc_sp_event_image_font_path() );
$this->assertIsReadable( asc_sp_event_image_font_path() );
}
/**
* Prepared event request includes fallback text for missing logos.
*/
public function test_prepare_image_request_uses_team_short_name_fallbacks() {
$team1 = $this->create_post_of_type( 'sp_team', 'Hawks' );
$team2 = $this->create_post_of_type( 'sp_team', 'Electrons' );
$event = $this->create_post_of_type( 'sp_event', 'Hawks vs Electrons' );
add_post_meta( $event, 'sp_team', $team1 );
add_post_meta( $event, 'sp_team', $team2 );
$request = asc_sp_event_prepare_image_request( $event );
$this->assertIsArray( $request );
$this->assertSame( 'Hawks', $request['team1_fallback'] );
$this->assertSame( 'Electrons', $request['team2_fallback'] );
$this->assertSame( '', $request['team1_logo'] );
$this->assertSame( '', $request['team2_logo'] );
}
/**
* Invalid colors are safely normalized.
*/
public function test_invalid_colors_fall_back_to_configured_defaults() {
$this->assertSame( '#4B5563', asc_sp_event_image_color( 'not-a-color' ) );
$this->assertSame( '#6B7280', asc_sp_event_image_color( 'not-a-color', '#6B7280' ) );
$this->assertSame( '#112233', asc_sp_event_image_color( '#112233' ) );
}
/**
* Image cache keys include the generator version and style hash.
*/
public function test_prepare_image_request_uses_versioned_style_cache_key() {
$team1 = $this->create_post_of_type( 'sp_team', 'Hawks' );
$team2 = $this->create_post_of_type( 'sp_team', 'Electrons' );
$event = $this->create_post_of_type( 'sp_event', 'Hawks vs Electrons' );
add_post_meta( $event, 'sp_team', $team1 );
add_post_meta( $event, 'sp_team', $team2 );
$request = asc_sp_event_prepare_image_request( $event );
$this->assertStringStartsWith( 'team_image_v' . ASC_SP_EVENT_IMAGE_CACHE_VERSION . '_' . asc_sp_event_image_cache_style_hash(), $request['cache_key'] );
$this->assertSame( 'wide', $request['variant'] );
$this->assertSame( 1200, $request['width'] );
$this->assertSame( 628, $request['height'] );
$square_request = asc_sp_event_prepare_image_request( $event, 'square' );
$this->assertStringContainsString( '_square_', $square_request['cache_key'] );
$this->assertSame( 'square', $square_request['variant'] );
$this->assertSame( 1200, $square_request['width'] );
$this->assertSame( 1200, $square_request['height'] );
}
}

View File

@@ -1,267 +0,0 @@
<?php
/**
* Tests for SportsPress event Open Graph output.
*
* @package Tonys_Sportspress_Enhancements
*/
if ( ! class_exists( 'SP_Event' ) ) {
/**
* Minimal SportsPress event test double.
*/
class SP_Event {
/**
* Event post ID.
*
* @var int
*/
private $id;
/**
* Status values by event ID.
*
* @var array<int,string>
*/
public static $statuses = array();
/**
* Result values by event ID.
*
* @var array<int,array>
*/
public static $results = array();
/**
* Constructor.
*
* @param int $id Event post ID.
*/
public function __construct( $id ) {
$this->id = absint( $id );
}
/**
* Get event status.
*
* @return string
*/
public function status() {
return self::$statuses[ $this->id ] ?? '';
}
/**
* Get event results.
*
* @return array
*/
public function results() {
return self::$results[ $this->id ] ?? array();
}
}
}
/**
* Open Graph tests.
*/
class Test_Open_Graph_Tags extends WP_UnitTestCase {
/**
* Reset mock SportsPress state.
*/
public function set_up(): void {
parent::set_up();
if ( property_exists( 'SP_Event', 'statuses' ) ) {
SP_Event::$statuses = array();
SP_Event::$results = array();
}
update_option( 'sportspress_event_reverse_teams', 'no' );
update_option( 'sportspress_event_teams_delimiter', 'vs' );
}
/**
* Create a team.
*
* @param string $name Team name.
* @return int
*/
private function create_team( $name ) {
return self::factory()->post->create(
array(
'post_type' => 'sp_team',
'post_title' => $name,
)
);
}
/**
* Create an event.
*
* @param array $args Post args.
* @return int
*/
private function create_event( array $args = array() ) {
return self::factory()->post->create(
wp_parse_args(
$args,
array(
'post_type' => 'sp_event',
'post_title' => 'Test Event',
'post_status' => 'future',
'post_date' => '2026-05-02 13:00:00',
'post_content' => 'First pitch at one.',
)
)
);
}
/**
* Future event emits complete Open Graph data.
*/
public function test_future_event_emits_core_open_graph_values() {
$home = $this->create_team( 'Hawks' );
$away = $this->create_team( 'Electrons' );
$event = $this->create_event();
add_post_meta( $event, 'sp_team', $home );
add_post_meta( $event, 'sp_team', $away );
if ( property_exists( 'SP_Event', 'statuses' ) ) {
SP_Event::$statuses[ $event ] = 'future';
}
$meta = asc_sp_event_open_graph_data( $event );
$this->assertSame( 'article', $meta['type'] );
$this->assertStringContainsString( 'Hawks vs Electrons', $meta['title'] );
$this->assertStringContainsString( 'First pitch at one.', $meta['description'] );
$this->assertCount( 1, $meta['images'] );
$this->assertSame( '1200', $meta['images'][0]['width'] );
$this->assertSame( '628', $meta['images'][0]['height'] );
$this->assertSame( '1200', $meta['image_width'] );
$this->assertSame( '628', $meta['image_height'] );
$this->assertStringContainsString( '/head-to-head?post=' . $event, $meta['image'] );
$this->assertNotEmpty( $meta['url'] );
}
/**
* Postponed, cancelled, and TBD labels appear in title and description.
*
* @dataProvider status_provider
*
* @param string $status Status slug.
*/
public function test_schedule_status_appears_in_title_and_description( $status ) {
$home = $this->create_team( 'Hawks' );
$away = $this->create_team( 'Electrons' );
$event = $this->create_event();
add_post_meta( $event, 'sp_team', $home );
add_post_meta( $event, 'sp_team', $away );
update_post_meta( $event, 'sp_status', $status );
$meta = asc_sp_event_open_graph_data( $event );
$label = strtoupper( $status );
$this->assertStringStartsWith( $label, $meta['title'] );
$this->assertStringStartsWith( $label, $meta['description'] );
}
/**
* Status provider.
*
* @return array
*/
public function status_provider() {
return array(
array( 'postponed' ),
array( 'cancelled' ),
array( 'tbd' ),
);
}
/**
* Result events with scores emit score titles.
*/
public function test_result_event_with_scores_emits_score_title() {
$home = $this->create_team( 'Hawks' );
$away = $this->create_team( 'Electrons' );
$event = $this->create_event( array( 'post_status' => 'publish' ) );
add_post_meta( $event, 'sp_team', $home );
add_post_meta( $event, 'sp_team', $away );
if ( property_exists( 'SP_Event', 'statuses' ) ) {
SP_Event::$statuses[ $event ] = 'results';
SP_Event::$results[ $event ] = array(
0 => array( 'r' => 'R' ),
$home => array( 'r' => '7' ),
$away => array( 'r' => '4' ),
);
}
$meta = asc_sp_event_open_graph_data( $event );
$this->assertStringContainsString( 'Hawks 7-4 Electrons', $meta['title'] );
}
/**
* Missing teams/results/outcomes still produce valid data.
*/
public function test_missing_sportspress_data_does_not_break_meta_generation() {
$event = $this->create_event(
array(
'post_title' => 'Sparse Event',
'post_content' => '',
)
);
if ( property_exists( 'SP_Event', 'statuses' ) ) {
SP_Event::$statuses[ $event ] = 'results';
SP_Event::$results[ $event ] = array();
}
$meta = asc_sp_event_open_graph_data( $event );
$this->assertSame( 'Sparse Event', $meta['title'] );
$this->assertNotEmpty( $meta['description'] );
$this->assertSame( '1200', $meta['image_width'] );
}
/**
* HTML-heavy post content is stripped and escaped in rendered tags.
*/
public function test_description_strips_html_and_rendered_tags_are_escaped() {
$home = $this->create_team( 'Hawks "A"' );
$away = $this->create_team( 'Electrons <B>' );
$event = $this->create_event(
array(
'post_content' => '<script>alert("x")</script><p>Bring <strong>bats</strong> & gloves.</p>',
)
);
add_post_meta( $event, 'sp_team', $home );
add_post_meta( $event, 'sp_team', $away );
$meta = asc_sp_event_open_graph_data( $event );
$this->assertStringNotContainsString( '<script', $meta['description'] );
$this->assertStringContainsString( 'Bring bats & gloves.', $meta['description'] );
$GLOBALS['post'] = get_post( $event );
$GLOBALS['wp_query']->is_single = true;
ob_start();
custom_open_graph_tags_with_sportspress_integration();
$output = ob_get_clean();
$this->assertStringContainsString( 'og:image:width', $output );
$this->assertSame( 1, substr_count( $output, 'property="og:image" content=' ) );
$this->assertStringContainsString( 'content="628"', $output );
$this->assertStringContainsString( 'content="1200"', $output );
$this->assertStringContainsString( 'Hawks &quot;A&quot;', $output );
$this->assertStringNotContainsString( '<B>', $output );
$this->assertStringNotContainsString( '<script', $output );
}
}

View File

@@ -1,341 +0,0 @@
<?php
/**
* Tests for schedule exporter and printable schedule helpers.
*
* @package Tonys_Sportspress_Enhancements
*/
/**
* Schedule exporter tests.
*/
class Test_SP_Schedule_Exporter extends WP_UnitTestCase {
/**
* Original request globals.
*
* @var array
*/
private $original_get = array();
/**
* Set up shared test fixtures.
*/
public function set_up() {
parent::set_up();
$this->original_get = $_GET;
foreach ( array( 'sp_venue', 'sp_league', 'sp_season' ) as $taxonomy ) {
if ( ! taxonomy_exists( $taxonomy ) ) {
register_taxonomy( $taxonomy, 'sp_event' );
}
}
}
/**
* Restore request globals.
*/
public function tear_down() {
$_GET = $this->original_get;
parent::tear_down();
}
/**
* Create a SportsPress team post.
*
* @param string $name Team name.
* @return int
*/
private function create_team( $name ) {
return self::factory()->post->create(
array(
'post_type' => 'sp_team',
'post_status' => 'publish',
'post_title' => $name,
)
);
}
/**
* Create a SportsPress event with ordered teams.
*
* @param int $home_id Home team ID.
* @param int $away_id Away team ID.
* @param int[] $venue_ids Venue IDs.
* @return int
*/
private function create_event( $home_id, $away_id, $venue_ids = array() ) {
$event_id = self::factory()->post->create(
array(
'post_type' => 'sp_event',
'post_status' => 'publish',
'post_title' => 'Game',
'post_date' => '2026-05-20 18:00:00',
'post_date_gmt' => '2026-05-20 23:00:00',
)
);
add_post_meta( $event_id, 'sp_team', (string) $home_id );
add_post_meta( $event_id, 'sp_team', (string) $away_id );
if ( ! empty( $venue_ids ) ) {
wp_set_object_terms( $event_id, $venue_ids, 'sp_venue' );
}
return $event_id;
}
/**
* Multiple selected team IDs should be retained in request order.
*/
public function test_resolve_team_ids_accepts_multiple_request_values() {
$team_one = $this->create_team( 'Blue' );
$team_two = $this->create_team( 'Red' );
$teams = array( get_post( $team_one ), get_post( $team_two ) );
$_GET['team_id'] = array( (string) $team_one, (string) $team_two );
$this->assertSame( array( $team_one, $team_two ), tse_sp_schedule_exporter_resolve_team_ids( $teams ) );
}
/**
* Printable URLs should carry multiple team IDs and the selected field.
*/
public function test_printable_url_accepts_multiple_teams_and_field() {
$url = tse_sp_schedule_exporter_get_printable_url( array( 12, 34 ), 56, 'letter', 78, false, 90, true );
$query = array();
wp_parse_str( (string) wp_parse_url( $url, PHP_URL_QUERY ), $query );
$this->assertSame( '12,34', $query['sp_team'] );
$this->assertSame( '90', $query['sp_field'] );
$this->assertSame( 'name', $query['team_label'] );
$this->assertSame( 'name', $query['field_label'] );
$this->assertSame( 'selected_first', $query['title_format'] );
$this->assertSame( '1', $query['month_pages'] );
}
/**
* Printable URLs should carry selected team and field label modes.
*/
public function test_printable_url_accepts_label_modes() {
$url = tse_sp_schedule_exporter_get_printable_url( 12, 56, 'letter', 78, false, 90, false, 'shortname', 'abbreviation' );
$query = array();
wp_parse_str( (string) wp_parse_url( $url, PHP_URL_QUERY ), $query );
$this->assertSame( 'shortname', $query['team_label'] );
$this->assertSame( 'abbreviation', $query['field_label'] );
}
/**
* Printable URLs should carry black-and-white mode when requested.
*/
public function test_printable_url_accepts_black_white_mode() {
$url = tse_sp_schedule_exporter_get_printable_url( 12, 56, 'letter', 78, false, 90, false, 'name', 'name', 'selected_first', true );
$query = array();
wp_parse_str( (string) wp_parse_url( $url, PHP_URL_QUERY ), $query );
$this->assertSame( '1', $query['black_white'] );
}
/**
* Single-team printable entries should keep the existing opponent perspective.
*/
public function test_printable_single_team_entries_keep_opponent_perspective() {
$home_id = $this->create_team( 'Home Team' );
$away_id = $this->create_team( 'Away Team' );
$venue_id = self::factory()->term->create( array( 'taxonomy' => 'sp_venue', 'name' => 'North Field' ) );
$this->create_event( $home_id, $away_id, array( $venue_id ) );
$printable = Tony_Sportspress_Printable_Calendars::instance();
$method = new ReflectionMethod( $printable, 'get_schedule_entries' );
$method->setAccessible( true );
$entries = $method->invoke( $printable, $home_id, 0, 0, array() );
$this->assertCount( 1, $entries );
$this->assertFalse( $entries[0]['is_matchup'] );
$this->assertTrue( $entries[0]['is_home'] );
$this->assertSame( 'Away Team', $entries[0]['opponent_name'] );
}
/**
* Printable entries should honor requested team and field labels.
*/
public function test_printable_entries_honor_label_modes() {
$home_id = $this->create_team( 'Home Team' );
$away_id = $this->create_team( 'Away Team' );
$venue_id = self::factory()->term->create( array( 'taxonomy' => 'sp_venue', 'name' => 'North Field' ) );
update_post_meta( $away_id, 'sp_abbreviation', 'AWY' );
update_term_meta( $venue_id, 'tse_abbreviation', 'NF' );
$this->create_event( $home_id, $away_id, array( $venue_id ) );
$printable = Tony_Sportspress_Printable_Calendars::instance();
$method = new ReflectionMethod( $printable, 'get_schedule_entries' );
$method->setAccessible( true );
$entries = $method->invoke( $printable, $home_id, 0, 0, array(), 'abbreviation', 'abbreviation' );
$this->assertCount( 1, $entries );
$this->assertSame( 'AWY', $entries[0]['opponent_name'] );
$this->assertSame( 'NF', $entries[0]['venue_label'] );
}
/**
* Team CSV layout should support multiple selected teams with away-first context.
*/
public function test_team_export_multiple_selected_teams_uses_away_team_first() {
$home_id = $this->create_team( 'Home Team' );
$away_id = $this->create_team( 'Away Team' );
$this->create_event( $home_id, $away_id );
$events = tse_sp_event_export_get_events(
array(
'team_ids' => array( $home_id, $away_id ),
)
);
$this->assertCount( 1, $events );
$this->assertSame( 'Away Team', $events[0]['team_name'] );
$this->assertSame( 'Away Team at Home Team', $events[0]['title'] );
$this->assertSame( 'at', $events[0]['separator'] );
$this->assertSame( 'Home Team', $events[0]['opponent_name'] );
$this->assertSame( 'Away', $events[0]['location_flag'] );
}
/**
* Team CSV layout should use vs when the selected team is home.
*/
public function test_team_export_home_selected_team_uses_vs_separator() {
$home_id = $this->create_team( 'Home Team' );
$away_id = $this->create_team( 'Away Team' );
$this->create_event( $home_id, $away_id );
$events = tse_sp_event_export_get_events(
array(
'team_ids' => array( $home_id ),
)
);
$this->assertCount( 1, $events );
$this->assertSame( 'Home Team', $events[0]['team_name'] );
$this->assertSame( 'Away Team', $events[0]['title'] );
$this->assertSame( 'vs', $events[0]['separator'] );
$this->assertSame( 'Away Team', $events[0]['opponent_name'] );
$this->assertSame( 'Home', $events[0]['location_flag'] );
}
/**
* Team CSV layout should use at when the selected team is away.
*/
public function test_team_export_away_selected_team_uses_at_separator() {
$home_id = $this->create_team( 'Home Team' );
$away_id = $this->create_team( 'Away Team' );
$this->create_event( $home_id, $away_id );
$events = tse_sp_event_export_get_events(
array(
'team_ids' => array( $away_id ),
)
);
$this->assertCount( 1, $events );
$this->assertSame( 'Away Team', $events[0]['team_name'] );
$this->assertSame( 'Home Team', $events[0]['title'] );
$this->assertSame( 'at', $events[0]['separator'] );
$this->assertSame( 'Home Team', $events[0]['opponent_name'] );
$this->assertSame( 'Away', $events[0]['location_flag'] );
}
/**
* Matchup title format should ignore selected-team perspective.
*/
public function test_team_export_matchup_title_format_uses_away_at_home() {
$home_id = $this->create_team( 'Home Team' );
$away_id = $this->create_team( 'Away Team' );
$this->create_event( $home_id, $away_id );
$events = tse_sp_event_export_get_events(
array(
'team_ids' => array( $home_id ),
'title_format' => 'matchup',
)
);
$this->assertCount( 1, $events );
$this->assertSame( 'Away Team at Home Team', $events[0]['title'] );
}
/**
* Multi-team printable entries should use selected-team-first titles by default.
*/
public function test_printable_multi_team_entries_use_selected_first_titles() {
$home_id = $this->create_team( 'Home Team' );
$away_id = $this->create_team( 'Away Team' );
$venue_id = self::factory()->term->create( array( 'taxonomy' => 'sp_venue', 'name' => 'North Field' ) );
$this->create_event( $home_id, $away_id, array( $venue_id ) );
$printable = Tony_Sportspress_Printable_Calendars::instance();
$method = new ReflectionMethod( $printable, 'get_schedule_entries' );
$method->setAccessible( true );
$entries = $method->invoke( $printable, array( $home_id, $away_id ), 0, 0, array() );
$this->assertCount( 1, $entries );
$this->assertFalse( $entries[0]['is_matchup'] );
$this->assertSame( 'Away Team', $entries[0]['title_team_name'] );
$this->assertSame( 'at', $entries[0]['title_separator'] );
$this->assertSame( 'Home Team', $entries[0]['title_opponent_name'] );
}
/**
* Exactly one selected field should suppress per-event venue labels.
*/
public function test_render_month_grid_can_suppress_event_venue_label() {
$printable = Tony_Sportspress_Printable_Calendars::instance();
$method = new ReflectionMethod( $printable, 'render_month_grid' );
$method->setAccessible( true );
$entries = array(
'2026-05-20' => array(
array(
'day_key' => '2026-05-20',
'month_key' => '2026-05',
'timestamp' => strtotime( '2026-05-20 18:00:00' ),
'is_matchup' => true,
'away_team_name' => 'Away',
'home_team_name' => 'Home',
'event_time' => '6:00 PM',
'venue_label' => 'North',
'venue_name' => 'North Field',
'venue_key' => 'v:1',
),
),
);
ob_start();
$method->invoke(
$printable,
'2026-05',
$entries,
array( 'v:1' => array( 'name' => 'North Field', 'color' => '#1D4ED8' ) ),
array(
'primary' => '#1D4ED8',
'secondary' => '#DC2626',
'accent' => '#1D4ED8',
'ink' => '#111827',
'muted_ink' => '#334155',
),
true,
true
);
$output = ob_get_clean();
$this->assertStringContainsString( 'Away', $output );
$this->assertStringContainsString( 'Home', $output );
$this->assertStringNotContainsString( 'class="event-venue"', $output );
}
}

View File

@@ -39,331 +39,6 @@ class Test_SP_Webhooks extends WP_UnitTestCase {
$this->assertStringContainsString( '"id":55', $rendered );
}
/**
* Venue aliases and split schedule fields should render from the context.
*/
public function test_render_template_supports_field_alias_and_schedule_parts() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = 'Field={{ event.field.short_name }} Venue={{ event.venue.abbreviation }} Time={{ event.scheduled.time }} {{ event.scheduled.timezone }}';
$context = array(
'event' => array(
'field' => array(
'short_name' => 'North',
),
'venue' => array(
'abbreviation' => 'NF',
),
'scheduled' => array(
'time' => '7:30 PM',
'timezone' => 'CDT',
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Field=North Venue=NF Time=7:30 PM CDT', $rendered );
}
/**
* Event context should expose home and away team aliases.
*/
public function test_render_template_supports_event_team_aliases() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = 'Home={{ event.home_team.name }} Away={{ event.away_team.name }}';
$context = array(
'event' => array(
'home_team' => array(
'name' => 'Home Team',
),
'away_team' => array(
'name' => 'Away Team',
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Home=Home Team Away=Away Team', $rendered );
}
/**
* Event context should expose the current SportsPress schedule status.
*/
public function test_render_template_supports_event_status_alias() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = 'Status={{ event.status }}';
$context = array(
'event' => array(
'status' => 'Postponed',
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Status=Postponed', $rendered );
}
/**
* Date filter should accept PHP date format strings for schedule values.
*/
public function test_render_template_supports_date_filter() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = 'Time={{ event.scheduled.timestamp|date("g:i A") }} ISO={{ event.scheduled.local_iso|date("m/d g:i A") }}';
$context = array(
'event' => array(
'scheduled' => array(
'timestamp' => 1714005000,
'local_iso' => '2024-04-24T19:30:00-05:00',
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Time=7:30 PM ISO=04/24 7:30 PM', $rendered );
}
/**
* Change notifications should expose before and after venue/time values.
*/
public function test_render_template_supports_before_after_venue_and_time() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = 'Venue {{ changes.previous.venue.name }} -> {{ changes.current.venue.name }} Time {{ changes.previous.time }} -> {{ changes.current.time }}';
$context = array(
'changes' => array(
'previous' => array(
'time' => '6:00 PM',
'venue' => array(
'name' => 'North Field',
),
),
'current' => array(
'time' => '7:30 PM',
'venue' => array(
'name' => 'South Field',
),
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Venue North Field -> South Field Time 6:00 PM -> 7:30 PM', $rendered );
}
/**
* Change notifications should expose before and after home/away team values.
*/
public function test_render_template_supports_before_after_teams() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = 'Home {{ changes.previous.home_team.name }} -> {{ changes.current.home_team.name }} Away {{ changes.previous.away_team.name }} -> {{ changes.current.away_team.name }}';
$context = array(
'changes' => array(
'previous' => array(
'home_team' => array(
'name' => 'Old Home',
),
'away_team' => array(
'name' => 'Old Away',
),
),
'current' => array(
'home_team' => array(
'name' => 'New Home',
),
'away_team' => array(
'name' => 'New Away',
),
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Home Old Home -> New Home Away Old Away -> New Away', $rendered );
}
/**
* Change notifications should expose before and after status values.
*/
public function test_render_template_supports_before_after_status() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = 'Status {{ changes.previous.status }} -> {{ changes.current.status }}';
$context = array(
'changes' => array(
'previous' => array(
'status' => 'On time',
),
'current' => array(
'status' => 'Postponed',
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Status On time -> Postponed', $rendered );
}
/**
* Schedule snapshots should treat status changes as meaningful changes.
*/
public function test_schedule_snapshot_signature_changes_when_status_changes() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$method = new ReflectionMethod( $webhooks, 'schedule_snapshots_match' );
$method->setAccessible( true );
$left = array(
'local_iso' => '2026-05-02T10:30:00-05:00',
'gmt_iso' => '2026-05-02T15:30:00+00:00',
'status' => 'On time',
'venue' => array(
'name' => 'Winnemac Park',
),
'teams' => array(
array( 'name' => 'Hawks' ),
array( 'name' => 'Electrons' ),
),
);
$right = $left;
$right['status'] = 'Canceled';
$this->assertFalse( $method->invoke( $webhooks, $left, $right ) );
}
/**
* Status snapshots should expose a display label and keep the raw SportsPress key.
*/
public function test_build_change_snapshot_normalizes_status_label_and_slug() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$method = new ReflectionMethod( $webhooks, 'build_change_snapshot' );
$method->setAccessible( true );
$snapshot = $method->invoke( $webhooks, array(), array(), array(), 'cancelled' );
$this->assertSame( 'Canceled', $snapshot['status'] );
$this->assertSame( 'cancelled', $snapshot['sp_status'] );
}
/**
* Conditionals should support simple comparisons and else branches.
*/
public function test_render_template_supports_conditionals() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = "{% if changes.previous.time != changes.current.time %}Time changed: {{ changes.previous.time }} -> {{ changes.current.time }}\n{% endif %}{% if changes.previous.field.name == changes.current.field.name %}Field unchanged{% else %}Field changed: {{ changes.previous.field.name }} -> {{ changes.current.field.name }}{% endif %}";
$context = array(
'changes' => array(
'previous' => array(
'time' => '6:00 PM',
'field' => array(
'name' => 'North Field',
),
),
'current' => array(
'time' => '7:30 PM',
'field' => array(
'name' => 'South Field',
),
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( "Time changed: 6:00 PM -> 7:30 PM\nField changed: North Field -> South Field", $rendered );
}
/**
* Truthy conditionals should render when the referenced value exists.
*/
public function test_render_template_supports_truthy_conditionals() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = '{% if changes.current.home_team.name %}Home: {{ changes.current.home_team.name }}{% endif %}';
$context = array(
'changes' => array(
'current' => array(
'home_team' => array(
'name' => 'Home Team',
),
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Home: Home Team', $rendered );
}
/**
* Team change conditionals should stay false when only schedule fields changed.
*/
public function test_team_conditionals_do_not_fire_for_schedule_only_changes() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = '{% if changes.previous.away_team.name != changes.current.away_team.name %}Away changed{% else %}Away unchanged{% endif %}';
$context = array(
'changes' => array(
'previous' => array(
'time' => '6:00 PM',
'away_team' => array(
'name' => 'Away Team',
),
),
'current' => array(
'time' => '7:30 PM',
'away_team' => array(
'name' => 'Away Team',
),
),
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Away unchanged', $rendered );
}
/**
* Conditionals should support simple or expressions with else branches.
*/
public function test_render_template_supports_or_conditionals() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$template = '{% if event.status == "Postponed" or event.status == "Canceled" %}Delayed{% else %}Normal{% endif %}';
$context = array(
'event' => array(
'status' => 'Canceled',
),
);
$rendered = $webhooks->render_template( $template, $context );
$this->assertSame( 'Delayed', $rendered );
}
/**
* Test webhook AJAX should honor the submitted row index.
*/
public function test_get_submitted_test_webhook_row_uses_matching_index() {
$webhooks = Tony_Sportspress_Webhooks::instance();
$method = new ReflectionMethod( $webhooks, 'get_submitted_test_webhook_row' );
$method->setAccessible( true );
$result = $method->invoke(
$webhooks,
array(
'2' => array(
'name' => 'Second Row',
),
),
array(
'2' => '123',
)
);
$this->assertSame( 'Second Row', $result['row']['name'] );
$this->assertSame( 123, $result['event_id'] );
}
/**
* Sanitization should keep only complete provider-specific webhook rows.
*/

View File

@@ -8,13 +8,13 @@
* Text Domain: tonys-sportspress-enhancements
* Domain Path: /languages
* Update URI: https://github.com/anthonyscorrea/tonys-sportspress-enhancements
* Version: 0.1.15
* Version: 0.1.9
*
* @package Tonys_Sportspress_Enhancements
*/
if ( ! defined( 'TONY_SPORTSPRESS_ENHANCEMENTS_VERSION' ) ) {
define( 'TONY_SPORTSPRESS_ENHANCEMENTS_VERSION', '0.1.15' );
define( 'TONY_SPORTSPRESS_ENHANCEMENTS_VERSION', '0.1.9' );
}
if ( ! defined( 'TONY_SPORTSPRESS_ENHANCEMENTS_FILE' ) ) {
@@ -33,21 +33,20 @@ if ( ! defined( 'TONY_SPORTSPRESS_ENHANCEMENTS_PLUGIN_BASENAME' ) ) {
define( 'TONY_SPORTSPRESS_ENHANCEMENTS_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
}
// Include other files here
require_once plugin_dir_path(__FILE__) . 'includes/sp-github-updater.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-officials-manager-role.php';
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/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-webhooks.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-schedule-exporter.php';
require_once plugin_dir_path(__FILE__) . 'includes/sp-venue-meta.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-github-updater.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-officials-manager-role.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/open-graph-tags.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/featured-image-generator.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-event-permalink.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-event-export.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-event-csv.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-event-admin-week-filter.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-event-quick-edit-officials.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-event-team-ordering.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-printable-calendars.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-url-builder.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-webhooks.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-schedule-exporter.php';
require_once TONY_SPORTSPRESS_ENHANCEMENTS_DIR . 'includes/sp-venue-meta.php';
register_activation_hook( __FILE__, 'tony_sportspress_sync_officials_manager_roles' );