Enhance printable calendar URL controls
Add URL-driven label mode controls for printable calendars, including team_label and field_label parameters that support name, shortname, and abbreviation output. Wire those modes through the Tony's settings URL builder, printable title, opponent and matchup labels, selected field metadata, event venue labels, and footer legend labels. Expand the printable URL builder with field selection, team/field label format dropdowns, and month-page output. Keep existing URLs compatible by normalizing old label mode values such as full_name and short_name to the new name and shortname modes. Improve printable calendar rendering for field-filtered and combined-team schedules. Support multiple selected teams, matchup-style event cells, per-month pages, selected-field venue suppression, and neutral colors for multi-team calendars. Fix season-scoped venue color settings persistence by merging submitted field color and team-primary flags for the active season instead of wiping other seasons. Resolve field colors so team primary is only used for single-team calendars when the venue flag is set; multi-team calendars use the saved or suggested field color. Add PHPUnit coverage for multi-team printable URLs, label mode parameters, label resolution in printable entries, multi-team matchup entries, and suppressing venue labels when a single field is selected.
This commit is contained in:
@@ -27,10 +27,7 @@ body {
|
||||
|
||||
.print-shell {
|
||||
margin: 0;
|
||||
width: calc(100% / var(--sheet-scale));
|
||||
background: #fff;
|
||||
transform-origin: top left;
|
||||
transform: scale(var(--sheet-scale));
|
||||
}
|
||||
|
||||
.print-page {
|
||||
@@ -48,7 +45,10 @@ 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,6 +60,103 @@ 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));
|
||||
}
|
||||
|
||||
body.month-pages .matchup-name {
|
||||
font-size: calc(13px * var(--month-font-scale));
|
||||
line-height: 1.05;
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -252,12 +349,35 @@ body {
|
||||
color: var(--day-num-color, #fff);
|
||||
}
|
||||
|
||||
.day.has-matchups {
|
||||
background: #fff;
|
||||
border: 1px solid 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 {
|
||||
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: calc(var(--corner-badge-size) + 5px) 2px 2px;
|
||||
}
|
||||
|
||||
.event {
|
||||
--event-top-band: calc(var(--corner-badge-size, 11px) + var(--corner-badge-offset, 2px));
|
||||
--event-bottom-band: 26px;
|
||||
@@ -275,6 +395,17 @@ body {
|
||||
--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);
|
||||
@@ -293,6 +424,17 @@ body {
|
||||
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);
|
||||
@@ -322,10 +464,36 @@ body {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
.ha-flag {
|
||||
position: absolute;
|
||||
top: var(--corner-badge-offset, 2px);
|
||||
@@ -341,7 +509,7 @@ body {
|
||||
font-weight: 900;
|
||||
font-variation-settings: "wdth" 84, "wght" 900;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.01em;
|
||||
letter-spacing: 0;
|
||||
background: #111;
|
||||
color: #fff;
|
||||
}
|
||||
@@ -366,6 +534,19 @@ body {
|
||||
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%;
|
||||
@@ -381,6 +562,12 @@ body {
|
||||
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%;
|
||||
@@ -395,6 +582,12 @@ body {
|
||||
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;
|
||||
@@ -487,6 +680,7 @@ body {
|
||||
width: auto;
|
||||
min-height: auto;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.print-page {
|
||||
@@ -500,4 +694,27 @@ body {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,8 +146,12 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
$vars[] = 'sp_team';
|
||||
$vars[] = 'sp_season';
|
||||
$vars[] = 'sp_league';
|
||||
$vars[] = 'sp_field';
|
||||
$vars[] = 'team_label';
|
||||
$vars[] = 'field_label';
|
||||
$vars[] = 'paper';
|
||||
$vars[] = 'autoprint';
|
||||
$vars[] = 'month_pages';
|
||||
|
||||
return $vars;
|
||||
}
|
||||
@@ -221,16 +225,24 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
$this->default_settings()
|
||||
);
|
||||
|
||||
$current = wp_parse_args(
|
||||
is_array( $input ) ? $input : array(),
|
||||
$existing
|
||||
);
|
||||
$current = is_array( $input ) ? $input : array();
|
||||
$active_season_id = isset( $current['active_season_id'] ) ? absint( (string) $current['active_season_id'] ) : 0;
|
||||
|
||||
return array(
|
||||
'calendar_feed_url' => isset( $existing['calendar_feed_url'] ) && is_string( $existing['calendar_feed_url'] ) ? $existing['calendar_feed_url'] : '',
|
||||
'sync_interval_minutes' => isset( $existing['sync_interval_minutes'] ) ? absint( (string) $existing['sync_interval_minutes'] ) : 60,
|
||||
'venue_color_overrides' => $this->sanitize_venue_color_overrides( isset( $current['venue_color_overrides'] ) ? $current['venue_color_overrides'] : array() ),
|
||||
'venue_use_team_primary' => $this->sanitize_venue_primary_flags( isset( $current['venue_use_team_primary'] ) ? $current['venue_use_team_primary'] : array() ),
|
||||
'venue_color_overrides' => $this->merge_sanitized_season_settings(
|
||||
isset( $existing['venue_color_overrides'] ) && is_array( $existing['venue_color_overrides'] ) ? $existing['venue_color_overrides'] : array(),
|
||||
$this->sanitize_venue_color_overrides( isset( $current['venue_color_overrides'] ) ? $current['venue_color_overrides'] : array() ),
|
||||
$active_season_id,
|
||||
true
|
||||
),
|
||||
'venue_use_team_primary' => $this->merge_sanitized_season_settings(
|
||||
isset( $existing['venue_use_team_primary'] ) && is_array( $existing['venue_use_team_primary'] ) ? $existing['venue_use_team_primary'] : array(),
|
||||
$this->sanitize_venue_primary_flags( isset( $current['venue_use_team_primary'] ) ? $current['venue_use_team_primary'] : array() ),
|
||||
$active_season_id,
|
||||
false
|
||||
),
|
||||
'printable_venue_label_mode' => $this->sanitize_venue_label_mode( isset( $current['printable_venue_label_mode'] ) ? $current['printable_venue_label_mode'] : '' ),
|
||||
);
|
||||
}
|
||||
@@ -322,6 +334,7 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
|
||||
echo '<form method="post" action="options.php">';
|
||||
settings_fields( self::OPTION_GROUP );
|
||||
echo '<input type="hidden" name="' . esc_attr( self::OPTION_KEY . '[active_season_id]' ) . '" value="' . esc_attr( (string) $season_id ) . '" />';
|
||||
|
||||
echo '<h2>' . esc_html__( 'Field Colors By Season', 'tonys-sportspress-enhancements' ) . '</h2>';
|
||||
echo '<p>' . esc_html__( 'Pick venue colors per season. Colors are darkened automatically when needed so white text still reads clearly.', 'tonys-sportspress-enhancements' ) . '</p>';
|
||||
@@ -359,11 +372,11 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
echo '<th scope="row"><label for="asc-sp-printable-venue-label-mode">' . esc_html__( 'Venue Label Under Time', 'tonys-sportspress-enhancements' ) . '</label></th>';
|
||||
echo '<td>';
|
||||
echo '<select id="asc-sp-printable-venue-label-mode" name="' . esc_attr( self::OPTION_KEY . '[printable_venue_label_mode]' ) . '">';
|
||||
foreach ( $this->get_venue_label_mode_options() as $mode => $label ) {
|
||||
foreach ( $this->get_label_mode_options() as $mode => $label ) {
|
||||
echo '<option value="' . esc_attr( $mode ) . '" ' . selected( $this->get_venue_label_mode(), $mode, false ) . '>' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
echo '<p class="description">' . esc_html__( 'Choose whether the printable schedule shows the venue full name, abbreviation, or short name below each game time.', 'tonys-sportspress-enhancements' ) . '</p>';
|
||||
echo '<p class="description">' . esc_html__( 'Default field label mode for printable URLs that do not include a field_label parameter.', 'tonys-sportspress-enhancements' ) . '</p>';
|
||||
echo '</td>';
|
||||
echo '</tr>';
|
||||
echo '</tbody></table>';
|
||||
@@ -426,6 +439,7 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
private function render_printable_url_builder( $season_id ) {
|
||||
$leagues = function_exists( 'tse_sp_schedule_exporter_get_leagues' ) ? tse_sp_schedule_exporter_get_leagues() : array();
|
||||
$teams = function_exists( 'tse_sp_schedule_exporter_get_teams' ) ? tse_sp_schedule_exporter_get_teams() : array();
|
||||
$fields = function_exists( 'tse_sp_schedule_exporter_get_fields' ) ? tse_sp_schedule_exporter_get_fields() : array();
|
||||
$paper = '11x17';
|
||||
|
||||
echo '<div class="tse-printable-url-builder" style="max-width:1100px;margin-top:28px;padding:20px 24px;border:1px solid #dcdcde;background:#fff;">';
|
||||
@@ -470,6 +484,18 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
echo '</select>';
|
||||
echo '</td></tr>';
|
||||
|
||||
echo '<tr><th scope="row"><label for="tse-printable-builder-field">' . esc_html__( 'Field', 'tonys-sportspress-enhancements' ) . '</label></th><td>';
|
||||
echo '<select id="tse-printable-builder-field" style="min-width:280px;">';
|
||||
echo '<option value="0">' . esc_html__( 'Any field', 'tonys-sportspress-enhancements' ) . '</option>';
|
||||
foreach ( $fields as $field ) {
|
||||
if ( ! $field instanceof WP_Term ) {
|
||||
continue;
|
||||
}
|
||||
echo '<option value="' . esc_attr( (string) $field->term_id ) . '">' . esc_html( $field->name ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
echo '</td></tr>';
|
||||
|
||||
echo '<tr><th scope="row"><label for="tse-printable-builder-paper">' . esc_html__( 'Paper Size', 'tonys-sportspress-enhancements' ) . '</label></th><td>';
|
||||
echo '<select id="tse-printable-builder-paper" style="min-width:280px;">';
|
||||
foreach ( array( 'letter' => __( 'Letter', 'tonys-sportspress-enhancements' ), '11x17' => __( '11x17 / Ledger', 'tonys-sportspress-enhancements' ) ) as $paper_value => $paper_label ) {
|
||||
@@ -478,11 +504,32 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
echo '</select>';
|
||||
echo '</td></tr>';
|
||||
|
||||
echo '<tr><th scope="row"><label for="tse-printable-builder-team-label">' . esc_html__( 'Team Name Format', 'tonys-sportspress-enhancements' ) . '</label></th><td>';
|
||||
echo '<select id="tse-printable-builder-team-label" style="min-width:280px;">';
|
||||
foreach ( $this->get_label_mode_options() as $mode => $label ) {
|
||||
echo '<option value="' . esc_attr( $mode ) . '" ' . selected( 'name', $mode, false ) . '>' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
echo '</td></tr>';
|
||||
|
||||
echo '<tr><th scope="row"><label for="tse-printable-builder-field-label">' . esc_html__( 'Field Name Format', 'tonys-sportspress-enhancements' ) . '</label></th><td>';
|
||||
echo '<select id="tse-printable-builder-field-label" style="min-width:280px;">';
|
||||
foreach ( $this->get_label_mode_options() as $mode => $label ) {
|
||||
echo '<option value="' . esc_attr( $mode ) . '" ' . selected( $this->get_venue_label_mode(), $mode, false ) . '>' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
echo '</select>';
|
||||
echo '</td></tr>';
|
||||
|
||||
echo '<tr><th scope="row">' . esc_html__( 'Options', 'tonys-sportspress-enhancements' ) . '</th><td>';
|
||||
echo '<label for="tse-printable-builder-autoprint" style="display:inline-flex;align-items:center;gap:6px;">';
|
||||
echo '<input id="tse-printable-builder-autoprint" type="checkbox" value="1" />';
|
||||
echo esc_html__( 'Auto-open print dialog', 'tonys-sportspress-enhancements' );
|
||||
echo '</label>';
|
||||
echo '<br />';
|
||||
echo '<label for="tse-printable-builder-month-pages" style="display:inline-flex;align-items:center;gap:6px;margin-top:8px;">';
|
||||
echo '<input id="tse-printable-builder-month-pages" type="checkbox" value="1" />';
|
||||
echo esc_html__( 'Print each month on its own page', 'tonys-sportspress-enhancements' );
|
||||
echo '</label>';
|
||||
echo '</td></tr>';
|
||||
|
||||
echo '</tbody></table>';
|
||||
@@ -522,8 +569,12 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
var team = root.querySelector('#tse-printable-builder-team');
|
||||
var season = root.querySelector('#tse-printable-builder-season');
|
||||
var league = root.querySelector('#tse-printable-builder-league');
|
||||
var field = root.querySelector('#tse-printable-builder-field');
|
||||
var paper = root.querySelector('#tse-printable-builder-paper');
|
||||
var teamLabel = root.querySelector('#tse-printable-builder-team-label');
|
||||
var fieldLabel = root.querySelector('#tse-printable-builder-field-label');
|
||||
var autoprint = root.querySelector('#tse-printable-builder-autoprint');
|
||||
var monthPages = root.querySelector('#tse-printable-builder-month-pages');
|
||||
var output = root.querySelector('#tse-printable-builder-output');
|
||||
var copyButton = root.querySelector('#tse-printable-builder-copy');
|
||||
var openLink = root.querySelector('#tse-printable-builder-open');
|
||||
@@ -550,22 +601,42 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
url.searchParams.delete('sp_league');
|
||||
}
|
||||
|
||||
if (field.value && field.value !== '0') {
|
||||
url.searchParams.set('sp_field', field.value);
|
||||
} else {
|
||||
url.searchParams.delete('sp_field');
|
||||
}
|
||||
|
||||
if (paper.value) {
|
||||
url.searchParams.set('paper', paper.value);
|
||||
}
|
||||
|
||||
if (teamLabel.value) {
|
||||
url.searchParams.set('team_label', teamLabel.value);
|
||||
}
|
||||
|
||||
if (fieldLabel.value) {
|
||||
url.searchParams.set('field_label', fieldLabel.value);
|
||||
}
|
||||
|
||||
if (autoprint.checked) {
|
||||
url.searchParams.set('autoprint', '1');
|
||||
} else {
|
||||
url.searchParams.delete('autoprint');
|
||||
}
|
||||
|
||||
if (monthPages.checked) {
|
||||
url.searchParams.set('month_pages', '1');
|
||||
} else {
|
||||
url.searchParams.delete('month_pages');
|
||||
}
|
||||
|
||||
output.value = url.toString();
|
||||
openLink.href = url.toString();
|
||||
openLink.toggleAttribute('disabled', !(team.value && team.value !== '0'));
|
||||
}
|
||||
|
||||
[team, season, league, paper, autoprint].forEach(function(input){
|
||||
[team, season, league, field, paper, teamLabel, fieldLabel, autoprint, monthPages].forEach(function(input){
|
||||
input.addEventListener('change', buildUrl);
|
||||
});
|
||||
|
||||
@@ -618,8 +689,8 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$team_id = absint( (string) get_query_var( 'sp_team' ) );
|
||||
if ( $team_id <= 0 || 'sp_team' !== get_post_type( $team_id ) ) {
|
||||
$team_ids = $this->parse_team_ids( get_query_var( 'sp_team' ) );
|
||||
if ( empty( $team_ids ) ) {
|
||||
status_header( 400 );
|
||||
nocache_headers();
|
||||
echo esc_html__( 'Missing or invalid team id.', 'tonys-sportspress-enhancements' );
|
||||
@@ -631,23 +702,31 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
$season_id = absint( (string) get_option( 'sportspress_season', '0' ) );
|
||||
}
|
||||
$league_id = absint( (string) get_query_var( 'sp_league' ) );
|
||||
$field_ids = $this->parse_term_ids( get_query_var( 'sp_field' ), 'sp_venue' );
|
||||
$team_label_mode = $this->sanitize_label_mode( get_query_var( 'team_label' ), 'abbreviation' );
|
||||
$field_label_mode = $this->sanitize_label_mode( get_query_var( 'field_label' ), $this->get_venue_label_mode() );
|
||||
|
||||
$paper = $this->normalize_paper_size( (string) get_query_var( 'paper' ) );
|
||||
$autoprint = '1' === (string) get_query_var( 'autoprint' );
|
||||
$month_pages = '1' === (string) get_query_var( 'month_pages' );
|
||||
|
||||
$team_name = get_the_title( $team_id );
|
||||
$team_logo = get_the_post_thumbnail( $team_id, array( 72, 72 ), array( 'class' => 'team-logo-img' ) );
|
||||
$is_multi_team = count( $team_ids ) > 1;
|
||||
$primary_team_id = isset( $team_ids[0] ) ? (int) $team_ids[0] : 0;
|
||||
$team_name = $this->get_printable_title( $team_ids, $team_label_mode );
|
||||
$team_logo = $is_multi_team ? '' : get_the_post_thumbnail( $primary_team_id, array( 72, 72 ), array( 'class' => 'team-logo-img' ) );
|
||||
$brand_logo = $this->get_header_brand_logo();
|
||||
$site_url = home_url( '/' );
|
||||
$qr_url = 'https://api.qrserver.com/v1/create-qr-code/?size=144x144&data=' . rawurlencode( $site_url );
|
||||
$season_name = '';
|
||||
$entries = $this->get_schedule_entries( $team_id, $season_id, $league_id );
|
||||
$team_palette = $this->get_team_color_palette( $team_id );
|
||||
$team_primary_for_fields = $this->get_strict_team_primary_color( $team_id );
|
||||
$field_name = count( $field_ids ) === 1 ? $this->get_term_label( (int) $field_ids[0], 'sp_venue', $field_label_mode ) : '';
|
||||
$entries = $this->get_schedule_entries( $team_ids, $season_id, $league_id, $field_ids, $team_label_mode, $field_label_mode );
|
||||
$team_palette = $is_multi_team ? $this->get_default_printable_palette() : $this->get_team_color_palette( $primary_team_id );
|
||||
$team_primary_for_fields = $is_multi_team ? '' : $this->get_strict_team_primary_color( $primary_team_id );
|
||||
$entries_by_day = array();
|
||||
$venue_colors = array();
|
||||
$month_keys = array();
|
||||
$layout = array();
|
||||
$suppress_event_venue = 1 === count( $field_ids );
|
||||
|
||||
if ( $season_id > 0 ) {
|
||||
$season = get_term( $season_id, 'sp_season' );
|
||||
@@ -661,14 +740,14 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
|
||||
if ( '' !== $entry['venue_name'] && is_string( $entry['venue_key'] ) && ! isset( $venue_colors[ $entry['venue_key'] ] ) ) {
|
||||
$venue_colors[ $entry['venue_key'] ] = array(
|
||||
'name' => (string) $entry['venue_name'],
|
||||
'color' => $this->get_venue_color( (string) $entry['venue_name'], $season_id, (int) $entry['venue_id'], $team_primary_for_fields ),
|
||||
'name' => ! empty( $entry['venue_label'] ) ? (string) $entry['venue_label'] : (string) $entry['venue_name'],
|
||||
'color' => $this->get_venue_color( (string) $entry['venue_name'], $season_id, (int) $entry['venue_id'], $team_primary_for_fields, $is_multi_team ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$month_keys = $this->get_month_keys( $entries );
|
||||
$layout = $this->get_sheet_layout( count( $month_keys ), $paper );
|
||||
$layout = $this->get_sheet_layout( $month_pages ? 1 : count( $month_keys ), $paper );
|
||||
|
||||
status_header( 200 );
|
||||
nocache_headers();
|
||||
@@ -688,7 +767,7 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
echo '<script>window.addEventListener("load",function(){window.print();});</script>';
|
||||
}
|
||||
echo '</head>';
|
||||
echo '<body class="print-preview ' . esc_attr( $paper ) . '">';
|
||||
echo '<body class="print-preview ' . esc_attr( $paper . ( $month_pages ? ' month-pages' : '' ) ) . '">';
|
||||
|
||||
$root_vars = array(
|
||||
'--sheet-scale:' . $layout['sheet_scale'],
|
||||
@@ -710,7 +789,55 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
);
|
||||
|
||||
echo '<main class="print-shell ' . esc_attr( $paper ) . '" style="' . esc_attr( implode( ';', $root_vars ) . ';' ) . '">';
|
||||
echo '<div class="print-page">';
|
||||
$meta_parts = array( $season_name ? $season_name : __( 'Current', 'tonys-sportspress-enhancements' ) );
|
||||
if ( '' !== $field_name ) {
|
||||
$meta_parts[] = $field_name;
|
||||
}
|
||||
|
||||
if ( empty( $entries ) ) {
|
||||
echo '<div class="print-page">';
|
||||
$this->render_printable_header( $team_name, $meta_parts, $team_logo, $brand_logo );
|
||||
echo '<section class="empty">';
|
||||
echo '<p>' . esc_html__( 'No SportsPress events were found for the selected teams and filters.', 'tonys-sportspress-enhancements' ) . '</p>';
|
||||
echo '</section>';
|
||||
$this->render_printable_footer( $venue_colors, $site_url, $qr_url );
|
||||
echo '</div>';
|
||||
} elseif ( $month_pages ) {
|
||||
foreach ( $month_keys as $month_key ) {
|
||||
echo '<div class="print-page month-page">';
|
||||
$this->render_printable_header( $team_name, $meta_parts, $team_logo, $brand_logo );
|
||||
echo '<section class="sheet-grid">';
|
||||
$this->render_month_grid( $month_key, $entries_by_day, $venue_colors, $team_palette, $is_multi_team, $suppress_event_venue );
|
||||
echo '</section>';
|
||||
$this->render_printable_footer( $venue_colors, $site_url, $qr_url );
|
||||
echo '</div>';
|
||||
}
|
||||
} else {
|
||||
echo '<div class="print-page">';
|
||||
$this->render_printable_header( $team_name, $meta_parts, $team_logo, $brand_logo );
|
||||
echo '<section class="sheet-grid">';
|
||||
foreach ( $month_keys as $month_key ) {
|
||||
$this->render_month_grid( $month_key, $entries_by_day, $venue_colors, $team_palette, $is_multi_team, $suppress_event_venue );
|
||||
}
|
||||
echo '</section>';
|
||||
$this->render_printable_footer( $venue_colors, $site_url, $qr_url );
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</main>';
|
||||
echo '</body></html>';
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the printable page header.
|
||||
*
|
||||
* @param string $team_name Header title.
|
||||
* @param array $meta_parts Header metadata strings.
|
||||
* @param string $team_logo Team logo markup.
|
||||
* @param string $brand_logo Brand logo markup.
|
||||
* @return void
|
||||
*/
|
||||
private function render_printable_header( $team_name, $meta_parts, $team_logo, $brand_logo ) {
|
||||
echo '<header class="header">';
|
||||
echo '<div class="header-brand">';
|
||||
if ( '' !== $team_logo ) {
|
||||
@@ -718,7 +845,7 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
}
|
||||
echo '<div class="header-copy">';
|
||||
echo '<h1 class="title">' . esc_html( $team_name ) . '</h1>';
|
||||
echo '<p class="meta">' . esc_html( $season_name ? $season_name : __( 'Current', 'tonys-sportspress-enhancements' ) ) . '</p>';
|
||||
echo '<p class="meta">' . esc_html( implode( ' | ', array_filter( array_map( 'strval', $meta_parts ) ) ) ) . '</p>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
|
||||
@@ -726,19 +853,17 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
echo '<span class="league-logo" aria-hidden="true">' . wp_kses_post( $brand_logo ) . '</span>';
|
||||
}
|
||||
echo '</header>';
|
||||
}
|
||||
|
||||
if ( empty( $entries ) ) {
|
||||
echo '<section class="empty">';
|
||||
echo '<p>' . esc_html__( 'No SportsPress events were found for this team and season.', 'tonys-sportspress-enhancements' ) . '</p>';
|
||||
echo '</section>';
|
||||
} else {
|
||||
echo '<section class="sheet-grid">';
|
||||
foreach ( $month_keys as $month_key ) {
|
||||
$this->render_month_grid( $month_key, $entries_by_day, $venue_colors, $team_palette );
|
||||
}
|
||||
echo '</section>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the printable page footer.
|
||||
*
|
||||
* @param array $venue_colors Venue legend data.
|
||||
* @param string $site_url Site URL.
|
||||
* @param string $qr_url QR code image URL.
|
||||
* @return void
|
||||
*/
|
||||
private function render_printable_footer( $venue_colors, $site_url, $qr_url ) {
|
||||
echo '<footer class="footer-meta">';
|
||||
if ( ! empty( $venue_colors ) ) {
|
||||
echo '<div class="legend legend-bottom">';
|
||||
@@ -762,10 +887,6 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
echo '<img class="footer-qr-image" src="' . esc_url( $qr_url ) . '" alt="' . esc_attr__( 'QR code for website', 'tonys-sportspress-enhancements' ) . '" loading="lazy" decoding="async">';
|
||||
echo '</div>';
|
||||
echo '</footer>';
|
||||
echo '</div>';
|
||||
echo '</main>';
|
||||
echo '</body></html>';
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -776,8 +897,9 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
* @param string $paper Paper size.
|
||||
* @return string
|
||||
*/
|
||||
private function build_url( $team_id, $season_id, $paper, $league_id = 0 ) {
|
||||
private function build_url( $team_id, $season_id, $paper, $league_id = 0, $field_id = 0, $month_pages = false, $team_label_mode = 'name', $field_label_mode = '' ) {
|
||||
$paper = $this->normalize_paper_size( $paper );
|
||||
$field_label_mode = '' === $field_label_mode ? $this->get_venue_label_mode() : $field_label_mode;
|
||||
|
||||
return add_query_arg(
|
||||
array(
|
||||
@@ -785,12 +907,130 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
'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' => $this->sanitize_label_mode( $team_label_mode, 'name' ),
|
||||
'field_label' => $this->sanitize_label_mode( $field_label_mode, $this->get_venue_label_mode() ),
|
||||
'paper' => (string) $paper,
|
||||
'month_pages' => $month_pages ? '1' : '',
|
||||
),
|
||||
home_url( '/' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and validate one or more team IDs.
|
||||
*
|
||||
* @param mixed $value Raw query value.
|
||||
* @return int[]
|
||||
*/
|
||||
private function parse_team_ids( $value ) {
|
||||
$ids = $this->parse_id_list( $value );
|
||||
$valid = array();
|
||||
|
||||
foreach ( $ids as $id ) {
|
||||
if ( 'sp_team' === get_post_type( $id ) ) {
|
||||
$valid[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values( array_unique( $valid ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and validate one or more term IDs for a taxonomy.
|
||||
*
|
||||
* @param mixed $value Raw query value.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return int[]
|
||||
*/
|
||||
private function parse_term_ids( $value, $taxonomy ) {
|
||||
$ids = $this->parse_id_list( $value );
|
||||
$valid = array();
|
||||
|
||||
foreach ( $ids as $id ) {
|
||||
$term = get_term( $id, $taxonomy );
|
||||
if ( $term && ! is_wp_error( $term ) ) {
|
||||
$valid[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values( array_unique( $valid ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse scalar, array, or comma-delimited ID values.
|
||||
*
|
||||
* @param mixed $value Raw value.
|
||||
* @return int[]
|
||||
*/
|
||||
private function parse_id_list( $value ) {
|
||||
$values = is_array( $value ) ? $value : explode( ',', (string) $value );
|
||||
$ids = array();
|
||||
|
||||
foreach ( $values as $raw_value ) {
|
||||
$id = absint( trim( (string) $raw_value ) );
|
||||
if ( $id > 0 ) {
|
||||
$ids[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values( array_unique( $ids ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the printable title for selected teams.
|
||||
*
|
||||
* @param int[] $team_ids Team IDs.
|
||||
* @return string
|
||||
*/
|
||||
private function get_printable_title( $team_ids, $mode = 'name' ) {
|
||||
$names = array();
|
||||
|
||||
foreach ( $team_ids as $team_id ) {
|
||||
$title = $this->get_team_label( $team_id, $mode );
|
||||
if ( is_string( $title ) && '' !== trim( $title ) ) {
|
||||
$names[] = trim( $title );
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $names ) ) {
|
||||
return __( 'Printable Schedule', 'tonys-sportspress-enhancements' );
|
||||
}
|
||||
|
||||
return implode( ', ', $names );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a term display name.
|
||||
*
|
||||
* @param int $term_id Term ID.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return string
|
||||
*/
|
||||
private function get_term_name( $term_id, $taxonomy ) {
|
||||
$term = get_term( $term_id, $taxonomy );
|
||||
|
||||
return $term && ! is_wp_error( $term ) && isset( $term->name ) ? (string) $term->name : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a term display label.
|
||||
*
|
||||
* @param int $term_id Term ID.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @param string $mode Label mode.
|
||||
* @return string
|
||||
*/
|
||||
private function get_term_label( $term_id, $taxonomy, $mode = 'name' ) {
|
||||
$term = get_term( $term_id, $taxonomy );
|
||||
|
||||
if ( ! $term || is_wp_error( $term ) || ! isset( $term->name ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->get_configured_venue_label( $term, $mode );
|
||||
}
|
||||
|
||||
/**
|
||||
* Default option payload.
|
||||
*
|
||||
@@ -802,7 +1042,7 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
'sync_interval_minutes' => 60,
|
||||
'venue_color_overrides' => array(),
|
||||
'venue_use_team_primary'=> array(),
|
||||
'printable_venue_label_mode' => 'full_name',
|
||||
'printable_venue_label_mode' => 'name',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -836,18 +1076,44 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supported venue label modes.
|
||||
* Get supported label modes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_venue_label_mode_options() {
|
||||
private function get_label_mode_options() {
|
||||
return array(
|
||||
'full_name' => __( 'Full Name', 'tonys-sportspress-enhancements' ),
|
||||
'name' => __( 'Name', 'tonys-sportspress-enhancements' ),
|
||||
'shortname' => __( 'Short Name', 'tonys-sportspress-enhancements' ),
|
||||
'abbreviation' => __( 'Abbreviation', 'tonys-sportspress-enhancements' ),
|
||||
'short_name' => __( 'Short Name', 'tonys-sportspress-enhancements' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize label mode.
|
||||
*
|
||||
* @param mixed $value Raw value.
|
||||
* @param string $fallback Fallback mode.
|
||||
* @return string
|
||||
*/
|
||||
private function sanitize_label_mode( $value, $fallback = 'name' ) {
|
||||
$mode = is_string( $value ) ? sanitize_key( $value ) : '';
|
||||
$aliases = array(
|
||||
'full_name' => 'name',
|
||||
'fullname' => 'name',
|
||||
'short_name' => 'shortname',
|
||||
'short' => 'shortname',
|
||||
'abbr' => 'abbreviation',
|
||||
);
|
||||
if ( isset( $aliases[ $mode ] ) ) {
|
||||
$mode = $aliases[ $mode ];
|
||||
}
|
||||
|
||||
$allowed = array_keys( $this->get_label_mode_options() );
|
||||
$fallback = isset( $aliases[ $fallback ] ) ? $aliases[ $fallback ] : $fallback;
|
||||
|
||||
return in_array( $mode, $allowed, true ) ? $mode : ( in_array( $fallback, $allowed, true ) ? $fallback : 'name' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize venue label mode.
|
||||
*
|
||||
@@ -855,10 +1121,7 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
* @return string
|
||||
*/
|
||||
private function sanitize_venue_label_mode( $value ) {
|
||||
$mode = is_string( $value ) ? sanitize_key( $value ) : '';
|
||||
$allowed = array_keys( $this->get_venue_label_mode_options() );
|
||||
|
||||
return in_array( $mode, $allowed, true ) ? $mode : 'full_name';
|
||||
return $this->sanitize_label_mode( $value, 'name' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -943,6 +1206,36 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge submitted season-scoped settings without preserving unchecked boxes.
|
||||
*
|
||||
* @param array $existing Existing settings.
|
||||
* @param array $submitted Sanitized submitted settings.
|
||||
* @param int $active_season_id Active season ID.
|
||||
* @param bool $preserve_missing Whether to preserve season if missing.
|
||||
* @return array
|
||||
*/
|
||||
private function merge_sanitized_season_settings( $existing, $submitted, $active_season_id, $preserve_missing ) {
|
||||
$merged = is_array( $existing ) ? $existing : array();
|
||||
|
||||
if ( $active_season_id <= 0 ) {
|
||||
return ! empty( $submitted ) ? $submitted : $merged;
|
||||
}
|
||||
|
||||
$season_key = (string) $active_season_id;
|
||||
if ( isset( $submitted[ $season_key ] ) && is_array( $submitted[ $season_key ] ) ) {
|
||||
$merged[ $season_key ] = $submitted[ $season_key ];
|
||||
} elseif ( $preserve_missing ) {
|
||||
if ( isset( $existing[ $season_key ] ) && is_array( $existing[ $season_key ] ) ) {
|
||||
$merged[ $season_key ] = $existing[ $season_key ];
|
||||
}
|
||||
} else {
|
||||
unset( $merged[ $season_key ] );
|
||||
}
|
||||
|
||||
return $merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get seasons list.
|
||||
*
|
||||
@@ -1085,12 +1378,24 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
/**
|
||||
* Collect event entries for the calendar.
|
||||
*
|
||||
* @param int $team_id Team ID.
|
||||
* @param int $season_id Season ID.
|
||||
* @param int $league_id League ID.
|
||||
* @param int|int[] $team_ids Team IDs.
|
||||
* @param int $season_id Season ID.
|
||||
* @param int $league_id League ID.
|
||||
* @param int[] $field_ids Field IDs.
|
||||
* @return array
|
||||
*/
|
||||
private function get_schedule_entries( $team_id, $season_id, $league_id = 0 ) {
|
||||
private function get_schedule_entries( $team_ids, $season_id, $league_id = 0, $field_ids = array(), $team_label_mode = 'abbreviation', $field_label_mode = '' ) {
|
||||
$team_ids = is_array( $team_ids ) ? array_values( array_filter( array_map( 'absint', $team_ids ) ) ) : array( absint( $team_ids ) );
|
||||
$primary_team = isset( $team_ids[0] ) ? (int) $team_ids[0] : 0;
|
||||
$is_multi_team = count( $team_ids ) > 1;
|
||||
$field_ids = is_array( $field_ids ) ? array_values( array_filter( array_map( 'absint', $field_ids ) ) ) : array();
|
||||
$team_label_mode = $this->sanitize_label_mode( $team_label_mode, 'abbreviation' );
|
||||
$field_label_mode = '' === $field_label_mode ? $this->get_venue_label_mode() : $this->sanitize_label_mode( $field_label_mode, $this->get_venue_label_mode() );
|
||||
|
||||
if ( empty( $team_ids ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'post_type' => 'sp_event',
|
||||
'post_status' => array( 'publish', 'future' ),
|
||||
@@ -1101,7 +1406,7 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => 'sp_team',
|
||||
'value' => array( (string) $team_id ),
|
||||
'value' => array_map( 'strval', $team_ids ),
|
||||
'compare' => 'IN',
|
||||
),
|
||||
),
|
||||
@@ -1125,6 +1430,14 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $field_ids ) ) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => 'sp_venue',
|
||||
'field' => 'term_id',
|
||||
'terms' => $field_ids,
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $tax_query ) ) {
|
||||
if ( count( $tax_query ) > 1 ) {
|
||||
$tax_query['relation'] = 'AND';
|
||||
@@ -1143,7 +1456,8 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
}
|
||||
|
||||
$teams = array_values( array_unique( array_map( 'intval', get_post_meta( $event_id, 'sp_team', false ) ) ) );
|
||||
if ( ! in_array( $team_id, $teams, true ) ) {
|
||||
$matching_team_ids = array_values( array_intersect( $team_ids, $teams ) );
|
||||
if ( empty( $matching_team_ids ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1152,11 +1466,16 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$home_id = isset( $teams[0] ) ? (int) $teams[0] : 0;
|
||||
$away_id = isset( $teams[1] ) ? (int) $teams[1] : 0;
|
||||
$context_id = $is_multi_team ? 0 : $primary_team;
|
||||
$opponent_id = 0;
|
||||
foreach ( $teams as $team_option_id ) {
|
||||
if ( $team_option_id !== $team_id ) {
|
||||
$opponent_id = $team_option_id;
|
||||
break;
|
||||
if ( ! $is_multi_team ) {
|
||||
foreach ( $teams as $team_option_id ) {
|
||||
if ( $team_option_id !== $context_id ) {
|
||||
$opponent_id = $team_option_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1167,7 +1486,7 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
if ( $venue ) {
|
||||
$venue_name = (string) $venue->name;
|
||||
$venue_id = (int) $venue->term_id;
|
||||
$venue_label = $this->get_configured_venue_label( $venue );
|
||||
$venue_label = $this->get_configured_venue_label( $venue, $field_label_mode );
|
||||
}
|
||||
|
||||
$entries[] = array(
|
||||
@@ -1175,9 +1494,14 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
'day_key' => wp_date( 'Y-m-d', $timestamp ),
|
||||
'month_key' => wp_date( 'Y-m', $timestamp ),
|
||||
'timestamp' => $timestamp,
|
||||
'is_home' => isset( $teams[0] ) && $teams[0] === $team_id,
|
||||
'is_home' => ! $is_multi_team && $home_id === $context_id,
|
||||
'is_matchup' => $is_multi_team,
|
||||
'opponent_id' => $opponent_id,
|
||||
'opponent_name' => $opponent_id > 0 ? $this->get_team_label( $opponent_id ) : __( 'TBD', 'tonys-sportspress-enhancements' ),
|
||||
'opponent_name' => $opponent_id > 0 ? $this->get_team_label( $opponent_id, $team_label_mode ) : __( 'TBD', 'tonys-sportspress-enhancements' ),
|
||||
'home_team_id' => $home_id,
|
||||
'away_team_id' => $away_id,
|
||||
'home_team_name' => $home_id > 0 ? $this->get_team_label( $home_id, $team_label_mode ) : __( 'TBD', 'tonys-sportspress-enhancements' ),
|
||||
'away_team_name' => $away_id > 0 ? $this->get_team_label( $away_id, $team_label_mode ) : __( 'TBD', 'tonys-sportspress-enhancements' ),
|
||||
'event_time' => function_exists( 'sp_get_time' ) ? sp_get_time( $event_id ) : get_post_time( get_option( 'time_format' ), false, $event_id, true ),
|
||||
'venue_name' => $venue_name,
|
||||
'venue_label' => $venue_label,
|
||||
@@ -1230,8 +1554,10 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
* @param array $entries_by_day Entries keyed by day.
|
||||
* @param array $venue_colors Venue colors.
|
||||
* @param array $team_palette Team palette.
|
||||
* @param bool $is_multi_team Whether this is a combined team schedule.
|
||||
* @param bool $suppress_venue Whether to hide per-event venue labels.
|
||||
*/
|
||||
private function render_month_grid( $month_key, $entries_by_day, $venue_colors, $team_palette ) {
|
||||
private function render_month_grid( $month_key, $entries_by_day, $venue_colors, $team_palette, $is_multi_team = false, $suppress_venue = false ) {
|
||||
$month = DateTimeImmutable::createFromFormat( 'Y-m-d', $month_key . '-01' );
|
||||
if ( ! $month instanceof DateTimeImmutable ) {
|
||||
return;
|
||||
@@ -1257,15 +1583,26 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
for ( $day = 1; $day <= $days_in_month; $day++ ) {
|
||||
$day_key = sprintf( '%s-%02d', $month_key, $day );
|
||||
$day_entries = isset( $entries_by_day[ $day_key ] ) ? $entries_by_day[ $day_key ] : array();
|
||||
$day_class = ! empty( $day_entries ) ? 'day has-events' : 'day no-events';
|
||||
$has_matchup = false;
|
||||
foreach ( $day_entries as $day_entry ) {
|
||||
if ( ! empty( $day_entry['is_matchup'] ) || $is_multi_team ) {
|
||||
$has_matchup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$day_class = ! empty( $day_entries ) ? 'day has-events' : 'day no-events';
|
||||
if ( $has_matchup ) {
|
||||
$day_class .= ' has-matchups';
|
||||
}
|
||||
$day_style = '';
|
||||
|
||||
if ( ! empty( $day_entries ) ) {
|
||||
$first_entry = $day_entries[0];
|
||||
$first_is_home = ! empty( $first_entry['is_home'] );
|
||||
$first_is_matchup = ! empty( $first_entry['is_matchup'] ) || $is_multi_team;
|
||||
$first_venue_key = isset( $first_entry['venue_key'] ) && is_string( $first_entry['venue_key'] ) ? $first_entry['venue_key'] : '';
|
||||
$first_venue_color = ( '' !== $first_venue_key && isset( $venue_colors[ $first_venue_key ]['color'] ) ) ? (string) $venue_colors[ $first_venue_key ]['color'] : '';
|
||||
$first_background = '' !== $first_venue_color ? $first_venue_color : ( $first_is_home ? $team_palette['primary'] : $team_palette['secondary'] );
|
||||
$first_background = '' !== $first_venue_color ? $first_venue_color : ( $first_is_home || $first_is_matchup ? $team_palette['primary'] : $team_palette['secondary'] );
|
||||
$first_foreground = $this->get_readable_text_color( $first_background );
|
||||
$first_background = $this->ensure_minimum_contrast( $first_background, $first_foreground, self::MIN_WHITE_CONTRAST );
|
||||
$first_foreground = $this->get_readable_text_color( $first_background );
|
||||
@@ -1280,13 +1617,14 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
echo '<div class="events-stack" style="--event-count:' . esc_attr( (string) count( $day_entries ) ) . ';">';
|
||||
foreach ( $day_entries as $entry ) {
|
||||
$is_home = ! empty( $entry['is_home'] );
|
||||
$event_class = $is_home ? 'h' : 'a';
|
||||
$is_matchup = ! empty( $entry['is_matchup'] ) || $is_multi_team;
|
||||
$event_class = $is_matchup ? 'matchup' : ( $is_home ? 'h' : 'a' );
|
||||
$venue_key = isset( $entry['venue_key'] ) && is_string( $entry['venue_key'] ) ? $entry['venue_key'] : '';
|
||||
$venue_color = ( '' !== $venue_key && isset( $venue_colors[ $venue_key ]['color'] ) ) ? (string) $venue_colors[ $venue_key ]['color'] : '';
|
||||
$opponent_id = isset( $entry['opponent_id'] ) ? (int) $entry['opponent_id'] : 0;
|
||||
$logo = $opponent_id > 0 ? get_the_post_thumbnail( $opponent_id, 'medium', array( 'class' => 'event-logo-img', 'loading' => 'eager', 'decoding' => 'async' ) ) : '';
|
||||
$logo = ( ! $is_matchup && $opponent_id > 0 ) ? get_the_post_thumbnail( $opponent_id, 'medium', array( 'class' => 'event-logo-img', 'loading' => 'eager', 'decoding' => 'async' ) ) : '';
|
||||
$has_logo = '' !== $logo;
|
||||
$event_background = '' !== $venue_color ? $venue_color : ( $is_home ? $team_palette['primary'] : $team_palette['secondary'] );
|
||||
$event_background = '' !== $venue_color ? $venue_color : ( $is_home || $is_matchup ? $team_palette['primary'] : $team_palette['secondary'] );
|
||||
$event_foreground = $this->get_readable_text_color( $event_background );
|
||||
$event_background = $this->ensure_minimum_contrast( $event_background, $event_foreground, self::MIN_WHITE_CONTRAST );
|
||||
$event_foreground = $this->get_readable_text_color( $event_background );
|
||||
@@ -1297,13 +1635,21 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
if ( $has_logo ) {
|
||||
echo wp_kses_post( $logo );
|
||||
} else {
|
||||
echo '<span class="event-name">' . esc_html( isset( $entry['opponent_name'] ) ? (string) $entry['opponent_name'] : '' ) . '</span>';
|
||||
if ( $is_matchup ) {
|
||||
$away_name = isset( $entry['away_team_name'] ) ? (string) $entry['away_team_name'] : __( 'TBD', 'tonys-sportspress-enhancements' );
|
||||
$home_name = isset( $entry['home_team_name'] ) ? (string) $entry['home_team_name'] : __( 'TBD', 'tonys-sportspress-enhancements' );
|
||||
echo '<span class="event-name matchup-name"><span class="matchup-team away-team">' . esc_html( $away_name ) . '</span><span class="matchup-vs">' . esc_html__( 'vs', 'tonys-sportspress-enhancements' ) . '</span><span class="matchup-team home-team">' . esc_html( $home_name ) . '</span></span>';
|
||||
} else {
|
||||
echo '<span class="event-name">' . esc_html( isset( $entry['opponent_name'] ) ? (string) $entry['opponent_name'] : '' ) . '</span>';
|
||||
}
|
||||
}
|
||||
echo '</div>';
|
||||
echo '<span class="ha-flag">' . esc_html( $is_home ? 'H' : 'A' ) . '</span>';
|
||||
if ( ! $is_matchup ) {
|
||||
echo '<span class="ha-flag">' . esc_html( $is_home ? 'H' : 'A' ) . '</span>';
|
||||
}
|
||||
echo '<div class="event-meta">';
|
||||
echo '<div class="event-time">' . esc_html( isset( $entry['event_time'] ) ? (string) $entry['event_time'] : '' ) . '</div>';
|
||||
if ( ! empty( $entry['venue_label'] ) ) {
|
||||
if ( ! $suppress_venue && ! empty( $entry['venue_label'] ) ) {
|
||||
echo '<div class="event-venue" title="' . esc_attr( isset( $entry['venue_name'] ) ? (string) $entry['venue_name'] : '' ) . '">' . esc_html( (string) $entry['venue_label'] ) . '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
@@ -1333,9 +1679,32 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
* @param int $team_id Team ID.
|
||||
* @return string
|
||||
*/
|
||||
private function get_team_label( $team_id ) {
|
||||
if ( function_exists( 'sp_team_abbreviation' ) ) {
|
||||
$label = (string) sp_team_abbreviation( $team_id );
|
||||
private function get_team_label( $team_id, $mode = 'abbreviation' ) {
|
||||
$mode = $this->sanitize_label_mode( $mode, 'abbreviation' );
|
||||
|
||||
if ( 'shortname' === $mode ) {
|
||||
if ( function_exists( 'sp_team_short_name' ) ) {
|
||||
$label = trim( (string) sp_team_short_name( $team_id ) );
|
||||
if ( '' !== $label ) {
|
||||
return $label;
|
||||
}
|
||||
}
|
||||
|
||||
$label = trim( (string) get_post_meta( $team_id, 'sp_short_name', true ) );
|
||||
if ( '' !== $label ) {
|
||||
return $label;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'abbreviation' === $mode ) {
|
||||
if ( function_exists( 'sp_team_abbreviation' ) ) {
|
||||
$label = trim( (string) sp_team_abbreviation( $team_id ) );
|
||||
if ( '' !== $label ) {
|
||||
return $label;
|
||||
}
|
||||
}
|
||||
|
||||
$label = trim( (string) get_post_meta( $team_id, 'sp_abbreviation', true ) );
|
||||
if ( '' !== $label ) {
|
||||
return $label;
|
||||
}
|
||||
@@ -1369,21 +1738,21 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
* @param WP_Term $venue Venue term.
|
||||
* @return string
|
||||
*/
|
||||
private function get_configured_venue_label( $venue ) {
|
||||
private function get_configured_venue_label( $venue, $mode = '' ) {
|
||||
$full_name = isset( $venue->name ) ? trim( (string) $venue->name ) : '';
|
||||
if ( '' === $full_name || ! isset( $venue->term_id ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$term_id = (int) $venue->term_id;
|
||||
$mode = $this->get_venue_label_mode();
|
||||
$mode = '' === $mode ? $this->get_venue_label_mode() : $this->sanitize_label_mode( $mode, $this->get_venue_label_mode() );
|
||||
|
||||
if ( 'abbreviation' === $mode ) {
|
||||
$abbreviation = trim( (string) get_term_meta( $term_id, 'tse_abbreviation', true ) );
|
||||
return '' !== $abbreviation ? $abbreviation : $full_name;
|
||||
}
|
||||
|
||||
if ( 'short_name' === $mode ) {
|
||||
if ( 'shortname' === $mode ) {
|
||||
$short_name = trim( (string) get_term_meta( $term_id, 'tse_short_name', true ) );
|
||||
return '' !== $short_name ? $short_name : $full_name;
|
||||
}
|
||||
@@ -1397,15 +1766,27 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
* @param string $venue_name Venue name.
|
||||
* @param int $season_id Season ID.
|
||||
* @param int $venue_id Venue ID.
|
||||
* @param string $team_primary Team primary color.
|
||||
* @param string $team_primary Team primary color.
|
||||
* @param bool $ignore_team_primary Whether to ignore venue primary flags.
|
||||
* @return string
|
||||
*/
|
||||
private function get_venue_color( $venue_name, $season_id, $venue_id, $team_primary ) {
|
||||
if ( $this->should_use_team_primary_for_venue( $season_id, $venue_id ) && '' !== $this->sanitize_color( $team_primary ) ) {
|
||||
private function get_venue_color( $venue_name, $season_id, $venue_id, $team_primary, $ignore_team_primary = false ) {
|
||||
$custom = $this->get_custom_venue_color( $season_id, $venue_id );
|
||||
if ( $ignore_team_primary && '' !== $custom ) {
|
||||
return $custom;
|
||||
}
|
||||
|
||||
if ( $ignore_team_primary ) {
|
||||
$suggested = $this->get_suggested_venue_color( $season_id, $venue_id );
|
||||
if ( '' !== $suggested ) {
|
||||
return $suggested;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ignore_team_primary && $this->should_use_team_primary_for_venue( $season_id, $venue_id ) && '' !== $this->sanitize_color( $team_primary ) ) {
|
||||
return $this->sanitize_color( $team_primary );
|
||||
}
|
||||
|
||||
$custom = $this->get_custom_venue_color( $season_id, $venue_id );
|
||||
if ( '' !== $custom ) {
|
||||
return $custom;
|
||||
}
|
||||
@@ -1416,6 +1797,36 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
return $palette[ $index ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings-screen suggested venue color for a season.
|
||||
*
|
||||
* @param int $season_id Season ID.
|
||||
* @param int $venue_id Venue ID.
|
||||
* @return string
|
||||
*/
|
||||
private function get_suggested_venue_color( $season_id, $venue_id ) {
|
||||
if ( $season_id <= 0 || $venue_id <= 0 ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$venues = $this->get_venues_for_season( $season_id );
|
||||
if ( empty( $venues ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$palette_count = count( $this->suggested_palette );
|
||||
foreach ( $venues as $index => $venue ) {
|
||||
if ( ! is_array( $venue ) || ! isset( $venue['id'] ) || (int) $venue['id'] !== (int) $venue_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$suggested = isset( $this->suggested_palette[ $index % max( 1, $palette_count ) ] ) ? $this->suggested_palette[ $index % max( 1, $palette_count ) ] : '';
|
||||
return '' !== $suggested ? $this->adjust_for_white_text( $suggested, self::MIN_WHITE_CONTRAST ) : '';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve primary team color without site fallbacks.
|
||||
*
|
||||
@@ -1615,6 +2026,21 @@ if ( ! class_exists( 'Tony_Sportspress_Printable_Calendars' ) ) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get neutral printable colors for combined-team schedules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_printable_palette() {
|
||||
return array(
|
||||
'primary' => '#334155',
|
||||
'secondary' => '#64748B',
|
||||
'accent' => '#475569',
|
||||
'ink' => '#111827',
|
||||
'muted_ink' => '#334155',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a six-digit hex color.
|
||||
*
|
||||
|
||||
@@ -86,11 +86,14 @@ 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_id = tse_sp_schedule_exporter_resolve_team_id( $teams );
|
||||
$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();
|
||||
if ( 'team' === $subformat && count( $team_ids ) > 1 ) {
|
||||
$subformat = 'matchup';
|
||||
}
|
||||
|
||||
if ( empty( $teams ) ) {
|
||||
return '<p>' . esc_html__( 'No SportsPress teams match the selected league and season.', 'tonys-sportspress-enhancements' ) . '</p>';
|
||||
@@ -152,14 +155,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">
|
||||
<option value="0"><?php esc_html_e( 'All teams', 'tonys-sportspress-enhancements' ); ?></option>
|
||||
<select id="tse-public-team" name="team_id[]" multiple="multiple" size="<?php echo esc_attr( (string) min( 8, max( 3, count( $teams ) ) ) ); ?>">
|
||||
<?php foreach ( $teams as $team ) : ?>
|
||||
<option value="<?php echo esc_attr( (string) $team->ID ); ?>" <?php selected( $team_id, (int) $team->ID ); ?>>
|
||||
<option value="<?php echo esc_attr( (string) $team->ID ); ?>" <?php selected( in_array( (int) $team->ID, $team_ids, true ) ); ?>>
|
||||
<?php echo esc_html( $team->post_title ); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<p class="description"><?php esc_html_e( 'Select one or more teams. Multiple teams use the matchup CSV layout and a combined printable schedule.', 'tonys-sportspress-enhancements' ); ?></p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:16px;">
|
||||
@@ -174,15 +177,22 @@ 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>
|
||||
|
||||
</form>
|
||||
|
||||
<?php tse_sp_schedule_exporter_render_column_picker( 'matchup', 'public', $subformat ); ?>
|
||||
<?php tse_sp_schedule_exporter_render_column_picker( 'team', 'public', $subformat ); ?>
|
||||
|
||||
<?php
|
||||
$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 );
|
||||
$csv_url = tse_sp_event_export_get_feed_url( array( 'team_id' => $team_ids, '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_ids, 'season_id' => $season_id, 'league_id' => $league_id, 'field_id' => $field_id ), 'ics' );
|
||||
$print_url = tse_sp_schedule_exporter_get_printable_url( $team_ids, $season_id, 'letter', $league_id, false, $field_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;">
|
||||
@@ -331,26 +341,34 @@ function tse_sp_schedule_exporter_get_teams( $league_id = 0, $season_id = 0 ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve selected team ID.
|
||||
* Resolve selected team IDs.
|
||||
*
|
||||
* @param WP_Post[] $teams Team posts.
|
||||
* @return int
|
||||
* @return int[]
|
||||
*/
|
||||
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;
|
||||
}
|
||||
function tse_sp_schedule_exporter_resolve_team_ids( $teams ) {
|
||||
$available = array();
|
||||
foreach ( $teams as $team ) {
|
||||
if ( $team instanceof WP_Post ) {
|
||||
$available[] = (int) $team->ID;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $teams[0] ) && $teams[0] instanceof WP_Post ) {
|
||||
return (int) $teams[0]->ID;
|
||||
$requested = array();
|
||||
if ( isset( $_GET['team_id'] ) ) {
|
||||
$requested = tse_sp_event_export_parse_id_list( wp_unslash( $_GET['team_id'] ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
$selected = array_values( array_intersect( $requested, $available ) );
|
||||
if ( ! empty( $selected ) ) {
|
||||
return $selected;
|
||||
}
|
||||
|
||||
if ( isset( $available[0] ) ) {
|
||||
return array( (int) $available[0] );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -700,17 +718,25 @@ 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.
|
||||
* @return string
|
||||
*/
|
||||
function tse_sp_schedule_exporter_get_printable_url( $team_id, $season_id, $paper, $league_id = 0, $autoprint = false ) {
|
||||
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' ) {
|
||||
$team_ids = is_array( $team_id ) ? array_values( array_filter( array_map( 'absint', $team_id ) ) ) : array( absint( $team_id ) );
|
||||
|
||||
return add_query_arg(
|
||||
array(
|
||||
Tony_Sportspress_Printable_Calendars::QUERY_FLAG => '1',
|
||||
'sp_team' => (string) absint( $team_id ),
|
||||
'sp_team' => implode( ',', $team_ids ),
|
||||
'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 ),
|
||||
'paper' => $paper,
|
||||
'autoprint' => $autoprint ? '1' : '',
|
||||
'month_pages' => $month_pages ? '1' : '',
|
||||
),
|
||||
home_url( '/' )
|
||||
);
|
||||
@@ -758,19 +784,34 @@ 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"]');
|
||||
var team = form.querySelector('[name="team_id[]"], [name="team_id"]');
|
||||
var exportType = form.querySelector('[name="export_type"]');
|
||||
var subformat = form.querySelector('[name="subformat"]');
|
||||
var field = form.querySelector('[name="field_id"]');
|
||||
var monthPages = form.querySelector('[name="month_pages"]');
|
||||
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 teamValue = team ? (team.value || '0') : '0';
|
||||
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 activeSubformat = subformat ? (subformat.value || 'matchup') : 'matchup';
|
||||
var selectedExportType = exportType ? (exportType.value || 'csv') : 'csv';
|
||||
if (teamValues.length > 1 && activeSubformat === 'team') {
|
||||
activeSubformat = 'matchup';
|
||||
if (subformat) {
|
||||
subformat.value = 'matchup';
|
||||
}
|
||||
}
|
||||
|
||||
scope.querySelectorAll('[data-column-group]').forEach(function(group){
|
||||
var visible = selectedExportType === 'csv' && group.getAttribute('data-column-group') === activeSubformat;
|
||||
@@ -816,6 +857,12 @@ 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 (monthPages && monthPages.checked) {
|
||||
printUrl.searchParams.set('month_pages', '1');
|
||||
} else {
|
||||
printUrl.searchParams.delete('month_pages');
|
||||
}
|
||||
printUrl.searchParams.set('paper', 'letter');
|
||||
}
|
||||
|
||||
@@ -833,11 +880,11 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
|
||||
resolvedUrl = printUrl.toString();
|
||||
if (teamValue === '0') {
|
||||
disabled = true;
|
||||
note = 'Printable requires a specific team. All teams is not supported.';
|
||||
note = 'Printable requires at least one selected team.';
|
||||
}
|
||||
} else if (selectedExportType === 'csv' && activeSubformat === 'team' && teamValue === '0') {
|
||||
disabled = true;
|
||||
note = 'CSV team layout requires a specific team. All teams is not supported.';
|
||||
note = 'CSV team layout requires one selected team.';
|
||||
}
|
||||
|
||||
if (outputUrl) {
|
||||
@@ -885,10 +932,10 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
|
||||
|
||||
syncLinks(scope);
|
||||
|
||||
scope.querySelectorAll('.tse-schedule-exporter-form select').forEach(function(select){
|
||||
select.addEventListener('change', function(){
|
||||
if (select.dataset.autoSubmit === '1') {
|
||||
select.form.submit();
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
242
tests/test-sp-schedule-exporter.php
Normal file
242
tests/test-sp-schedule-exporter.php
Normal file
@@ -0,0 +1,242 @@
|
||||
<?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( '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'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-team printable entries should return one matchup row per event.
|
||||
*/
|
||||
public function test_printable_multi_team_entries_are_matchup_rows() {
|
||||
$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->assertTrue( $entries[0]['is_matchup'] );
|
||||
$this->assertSame( 'Away Team', $entries[0]['away_team_name'] );
|
||||
$this->assertSame( 'Home Team', $entries[0]['home_team_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 );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user