+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
- |
-
- |
- |
-
-
- |
-
- |
- |
-
-
- |
-
- |
- |
-
-
-
+
+
+
+ $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 );
+ ?>
+
+
+
+
+
+
'sp_venue',
+ 'hide_empty' => false,
+ 'orderby' => 'name',
+ 'order' => 'ASC',
+ )
+ );
+
+ if ( is_wp_error( $fields ) || ! is_array( $fields ) ) {
+ return array();
+ }
+
+ return $fields;
+}
+
/**
* Get teams for the exporter.
*
@@ -552,6 +534,70 @@ function tse_sp_schedule_exporter_resolve_team_id( $teams ) {
return 0;
}
+/**
+ * Resolve selected field ID.
+ *
+ * @param WP_Term[] $fields Field terms.
+ * @return int
+ */
+function tse_sp_schedule_exporter_resolve_field_id( $fields ) {
+ $requested = isset( $_GET['field_id'] ) ? absint( wp_unslash( $_GET['field_id'] ) ) : 0;
+
+ if ( 0 === $requested ) {
+ return 0;
+ }
+
+ foreach ( $fields as $field ) {
+ if ( $field instanceof WP_Term && (int) $field->term_id === $requested ) {
+ return $requested;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Render selectable columns for a format.
+ *
+ * @param string $format Export format.
+ * @param string $context Render context suffix.
+ * @return void
+ */
+function tse_sp_schedule_exporter_render_column_picker( $format, $context, $active_format = '' ) {
+ $format = tse_sp_event_export_sanitize_format( $format );
+ $definitions = tse_sp_event_export_get_column_definitions();
+ $columns = isset( $definitions[ $format ] ) ? $definitions[ $format ] : array();
+ $selected = tse_sp_event_export_get_default_columns( $format );
+ $formats = tse_sp_event_export_get_formats();
+ $legend = isset( $formats[ $format ]['label'] ) ? $formats[ $format ]['label'] : ucfirst( $format );
+
+ if ( empty( $columns ) ) {
+ return;
+ }
+
+ $style = 'margin:18px 0;padding:16px;border:1px solid #d7d7db;';
+ if ( $active_format && $active_format !== $format ) {
+ $style .= 'display:none;';
+ }
+
+ echo '
';
+}
+
/**
* Resolve selected league ID.
*
@@ -631,6 +677,92 @@ function tse_sp_schedule_exporter_resolve_paper_size() {
return array_key_exists( $paper, tse_sp_schedule_exporter_get_paper_sizes() ) ? $paper : 'letter';
}
+/**
+ * Resolve selected export format.
+ *
+ * @return string
+ */
+function tse_sp_schedule_exporter_resolve_format() {
+ $requested = isset( $_GET['format'] ) ? sanitize_key( wp_unslash( $_GET['format'] ) ) : 'matchup';
+
+ return tse_sp_event_export_sanitize_format( $requested );
+}
+
+/**
+ * Get supported exporter output types.
+ *
+ * @return array
+ */
+function tse_sp_schedule_exporter_get_export_types() {
+ return array(
+ 'csv' => __( 'CSV', 'tonys-sportspress-enhancements' ),
+ 'ics' => __( 'iCal Link', 'tonys-sportspress-enhancements' ),
+ 'printable' => __( 'Printable', 'tonys-sportspress-enhancements' ),
+ );
+}
+
+/**
+ * Resolve selected exporter output type.
+ *
+ * @return string
+ */
+function tse_sp_schedule_exporter_resolve_export_type() {
+ $requested = isset( $_GET['export_type'] ) ? sanitize_key( wp_unslash( $_GET['export_type'] ) ) : 'csv';
+ $types = tse_sp_schedule_exporter_get_export_types();
+
+ return isset( $types[ $requested ] ) ? $requested : 'csv';
+}
+
+/**
+ * Resolve selected CSV subformat.
+ *
+ * @return string
+ */
+function tse_sp_schedule_exporter_resolve_subformat() {
+ $requested = isset( $_GET['subformat'] ) ? sanitize_key( wp_unslash( $_GET['subformat'] ) ) : 'matchup';
+
+ return tse_sp_event_export_sanitize_format( $requested );
+}
+
+/**
+ * Get current output URL for the selected export type.
+ *
+ * @param string $export_type Export type.
+ * @param string $csv_url CSV URL.
+ * @param string $ics_url ICS URL.
+ * @param string $print_url Printable URL.
+ * @return string
+ */
+function tse_sp_schedule_exporter_get_output_url( $export_type, $csv_url, $ics_url, $print_url ) {
+ if ( 'ics' === $export_type ) {
+ return $ics_url;
+ }
+
+ if ( 'printable' === $export_type ) {
+ return $print_url;
+ }
+
+ return $csv_url;
+}
+
+/**
+ * Get current output button label for the selected export type.
+ *
+ * @param string $export_type Export type.
+ * @return string
+ */
+function tse_sp_schedule_exporter_get_output_label( $export_type ) {
+ if ( 'ics' === $export_type ) {
+ return __( 'Subscribe to iCal / ICS', 'tonys-sportspress-enhancements' );
+ }
+
+ if ( 'printable' === $export_type ) {
+ return __( 'Open Printable Page', 'tonys-sportspress-enhancements' );
+ }
+
+ return __( 'Open CSV Feed', 'tonys-sportspress-enhancements' );
+}
+
/**
* Collect team schedule events for export.
*
@@ -761,32 +893,7 @@ function tse_sp_schedule_exporter_get_primary_venue( $event_id ) {
}
/**
- * Build an export download URL.
- *
- * @param int $team_id Team ID.
- * @param int $season_id Season ID.
- * @param string $format Export format.
- * @param int $league_id League ID.
- * @return string
- */
-function tse_sp_schedule_exporter_get_export_url( $team_id, $season_id, $format, $league_id = 0 ) {
- return wp_nonce_url(
- add_query_arg(
- array(
- 'action' => 'tse_schedule_export',
- 'league_id' => absint( $league_id ),
- 'team_id' => absint( $team_id ),
- 'season_id' => absint( $season_id ),
- 'format' => sanitize_key( $format ),
- ),
- admin_url( 'admin-post.php' )
- ),
- 'tse_schedule_export'
- );
-}
-
-/**
- * Build the printable PDF URL.
+ * Build the printable page URL.
*
* @param int $team_id Team ID.
* @param int $season_id Season ID.
@@ -794,7 +901,7 @@ function tse_sp_schedule_exporter_get_export_url( $team_id, $season_id, $format,
* @param int $league_id League ID.
* @return string
*/
-function tse_sp_schedule_exporter_get_pdf_url( $team_id, $season_id, $paper, $league_id = 0 ) {
+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',
@@ -802,7 +909,7 @@ function tse_sp_schedule_exporter_get_pdf_url( $team_id, $season_id, $paper, $le
'sp_season' => $season_id > 0 ? (string) absint( $season_id ) : '',
'sp_league' => $league_id > 0 ? (string) absint( $league_id ) : '',
'paper' => $paper,
- 'autoprint' => '1',
+ 'autoprint' => $autoprint ? '1' : '',
),
home_url( '/' )
);
@@ -818,6 +925,30 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
$script = <<
(function(){
+ function copyText(text, done){
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ navigator.clipboard.writeText(text).then(done).catch(function(){
+ var input = document.createElement('input');
+ input.value = text;
+ document.body.appendChild(input);
+ input.focus();
+ input.select();
+ document.execCommand('copy');
+ document.body.removeChild(input);
+ done();
+ });
+ return;
+ }
+ var input = document.createElement('input');
+ input.value = text;
+ document.body.appendChild(input);
+ input.focus();
+ input.select();
+ document.execCommand('copy');
+ document.body.removeChild(input);
+ done();
+ }
+
function syncLinks(scope){
var form = scope.querySelector('.tse-schedule-exporter-form');
if (!form) {
@@ -827,25 +958,103 @@ 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 paper = form.querySelector('[name="paper"]');
+ var exportType = form.querySelector('[name="export_type"]');
+ var subformat = form.querySelector('[name="subformat"]');
+ var field = form.querySelector('[name="field_id"]');
+ var outputUrl = scope.querySelector('.tse-output-url');
+ var openButton = scope.querySelector('.tse-open-link');
+ var outputNote = scope.querySelector('.tse-output-note');
+ var copyButton = scope.querySelector('.tse-copy-link');
+ var teamValue = team ? (team.value || '0') : '0';
+ var activeSubformat = subformat ? (subformat.value || 'matchup') : 'matchup';
+ var selectedExportType = exportType ? (exportType.value || 'csv') : 'csv';
- scope.querySelectorAll('.tse-export-link').forEach(function(link){
- var url = new URL(link.href, window.location.origin);
- if (league) url.searchParams.set('league_id', league.value || '0');
- if (season) url.searchParams.set('season_id', season.value || '0');
- if (team) url.searchParams.set('team_id', team.value || '0');
- if (link.dataset.format) url.searchParams.set('format', link.dataset.format);
- link.href = url.toString();
+ scope.querySelectorAll('[data-column-group]').forEach(function(group){
+ var visible = selectedExportType === 'csv' && group.getAttribute('data-column-group') === activeSubformat;
+ group.style.display = visible ? 'block' : 'none';
});
- scope.querySelectorAll('.tse-pdf-link').forEach(function(link){
- var url = new URL(link.href, window.location.origin);
- if (league) url.searchParams.set('sp_league', league.value || '0');
- if (season) url.searchParams.set('sp_season', season.value || '0');
- if (team) url.searchParams.set('sp_team', team.value || '0');
- if (paper) url.searchParams.set('paper', paper.value || 'letter');
- link.href = url.toString();
- });
+ if (scope.querySelector('[data-subformat-wrap]')) {
+ scope.querySelectorAll('[data-subformat-wrap]').forEach(function(wrap){
+ wrap.style.display = selectedExportType === 'csv' ? 'block' : 'none';
+ });
+ }
+
+ var csvUrl = openButton ? new URL(openButton.dataset.csvUrl, window.location.origin) : null;
+ var icsUrl = openButton ? new URL(openButton.dataset.icsUrl, window.location.origin) : null;
+ var printUrl = openButton ? new URL(openButton.dataset.printUrl, window.location.origin) : null;
+
+ if (csvUrl) {
+ if (league) csvUrl.searchParams.set('league_id', league.value || '0');
+ 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');
+ csvUrl.searchParams.set('format', activeSubformat);
+ var columns = Array.prototype.slice.call(scope.querySelectorAll('[data-columns-format="' + activeSubformat + '"]:checked')).map(function(input){
+ return input.value;
+ }).filter(Boolean);
+ if (columns.length) {
+ csvUrl.searchParams.set('columns', columns.join(','));
+ } else {
+ csvUrl.searchParams.delete('columns');
+ }
+ }
+
+ if (icsUrl) {
+ if (league) icsUrl.searchParams.set('league_id', league.value || '0');
+ 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');
+ icsUrl.searchParams.delete('format');
+ icsUrl.searchParams.delete('columns');
+ }
+
+ if (printUrl) {
+ 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);
+ printUrl.searchParams.set('paper', 'letter');
+ }
+
+ var resolvedUrl = csvUrl ? csvUrl.toString() : '';
+ var label = 'Open CSV Feed';
+ var disabled = false;
+ var note = 'Use the buttons to copy or open the generated URL.';
+
+ if (selectedExportType === 'ics' && icsUrl) {
+ resolvedUrl = icsUrl.toString();
+ label = 'Subscribe to iCal / ICS';
+ } else if (selectedExportType === 'printable' && printUrl) {
+ resolvedUrl = printUrl.toString();
+ label = 'Open Printable Page';
+ if (teamValue === '0') {
+ disabled = true;
+ 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 a specific team. All teams is not supported.';
+ }
+
+ if (outputUrl) {
+ outputUrl.value = resolvedUrl;
+ }
+
+ if (openButton) {
+ openButton.dataset.currentUrl = resolvedUrl;
+ openButton.textContent = label;
+ openButton.disabled = disabled;
+ openButton.setAttribute('aria-disabled', disabled ? 'true' : 'false');
+ openButton.style.opacity = disabled ? '0.55' : '1';
+ }
+
+ if (outputNote) {
+ outputNote.textContent = note;
+ }
+
+ if (copyButton) {
+ copyButton.disabled = disabled;
+ }
}
document.querySelectorAll('.tse-schedule-exporter, .wrap').forEach(function(scope){
@@ -865,6 +1074,43 @@ function tse_sp_schedule_exporter_render_link_sync_script( $echo = false ) {
syncLinks(scope);
});
});
+
+ scope.querySelectorAll('[data-columns-format]').forEach(function(input){
+ input.addEventListener('change', function(){
+ syncLinks(scope);
+ });
+ });
+
+ var copyButton = scope.querySelector('.tse-copy-link');
+ var openButton = scope.querySelector('.tse-open-link');
+ var outputUrl = scope.querySelector('.tse-output-url');
+ if (copyButton && outputUrl) {
+ copyButton.addEventListener('click', function(){
+ if (copyButton.disabled || !outputUrl.value) {
+ return;
+ }
+
+ var defaultTitle = copyButton.getAttribute('data-default-title') || copyButton.title || 'Copy URL';
+ copyButton.setAttribute('data-default-title', defaultTitle);
+
+ copyText(outputUrl.value, function(){
+ copyButton.title = 'Copied';
+ window.setTimeout(function(){
+ copyButton.title = defaultTitle;
+ }, 1200);
+ });
+ });
+ }
+
+ if (openButton && outputUrl) {
+ openButton.addEventListener('click', function(){
+ if (openButton.disabled || !outputUrl.value) {
+ return;
+ }
+
+ window.open(outputUrl.value, '_blank', 'noopener,noreferrer');
+ });
+ }
});
})();
diff --git a/includes/sp-url-builder.php b/includes/sp-url-builder.php
new file mode 100644
index 0000000..50a39b0
--- /dev/null
+++ b/includes/sp-url-builder.php
@@ -0,0 +1,285 @@
+' . esc_html__( 'Feed Builder', 'tonys-sportspress-enhancements' ) . '';
+ echo '
' . esc_html__( 'Build a shareable CSV feed URL with format, filters, and custom columns. This does not save settings.', 'tonys-sportspress-enhancements' ) . '
';
+ echo '
';
+ echo '
';
+
+ echo '
';
+ foreach ( $columns as $format_key => $format_columns ) {
+ $default_columns = function_exists( 'tse_sp_event_export_get_default_columns' ) ? tse_sp_event_export_get_default_columns( $format_key ) : array();
+ $label = isset( $formats[ $format_key ]['label'] ) ? $formats[ $format_key ]['label'] : ucfirst( $format_key );
+
+ echo '
';
+ }
+ echo '
';
+
+ echo '
' . esc_html__( 'Generated URL', 'tonys-sportspress-enhancements' ) . '
';
+ echo '
';
+ echo '';
+ echo '';
+ echo '
';
+ echo '
' . esc_html__( 'Open Feed URL', 'tonys-sportspress-enhancements' ) . '
';
+ echo '
' . esc_html__( 'The builder always generates the standalone CSV feed endpoint using the selected filters and columns.', 'tonys-sportspress-enhancements' ) . '
';
+ echo '
';
+
+ tse_sp_url_builder_render_script();
+}
+add_action( 'tse_tonys_settings_render_tab_url-builder', 'tse_sp_url_builder_render_tab' );
+
+/**
+ * Render builder script.
+ *
+ * @return void
+ */
+function tse_sp_url_builder_render_script() {
+ $base_url = home_url( '/' );
+ ?>
+
+