Files
wp-cycling-stats/includes/frontend/shortcodes.php
T
2026-02-05 12:06:38 +01:00

393 lines
15 KiB
PHP

<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rejestruje shortcode [moje_statystyki].
*/
function mystat_register_shortcode() {
add_shortcode( 'moje_statystyki', 'mystat_shortcode_handler' );
add_shortcode( 'moje_statystyki_wpis', 'mystat_single_activity_shortcode_handler' );
}
/**
* Funkcja obsługująca shortcode.
* @param array $atts Atrybuty shortcode'u (np. year, month).
* @return string HTML do wyświetlenia.
*/
function mystat_shortcode_handler( $atts ) {
wp_enqueue_style( 'mystat-frontend-styles' );
global $wpdb;
// Ustawienie domyślnych atrybutów (bieżący rok i miesiąc)
$atts = shortcode_atts(
array(
'year' => current_time( 'Y' ),
'month' => current_time( 'n' ),
),
$atts,
'moje_statystyki'
);
$year = intval( $atts['year'] );
$month = intval( $atts['month'] );
// Pobieranie danych z bazy
$table_activities = $wpdb->prefix . 'mystat_activities';
$sql = $wpdb->prepare(
"
SELECT a.*, c.name as category_name, eq.name as equipment_name
FROM $table_activities a
LEFT JOIN {$wpdb->prefix}mystat_categories c ON a.category_id = c.id
LEFT JOIN {$wpdb->prefix}mystat_equipment eq ON a.equipment_id = eq.id
WHERE YEAR(a.date) = %d AND MONTH(a.date) = %d
ORDER BY a.date ASC
",
$year,
$month
);
$activities = $wpdb->get_results( $sql );
// Obliczanie podsumowań
$total_distance = 0;
$total_seconds = 0;
foreach ( $activities as $activity ) {
$total_distance += $activity->distance;
if ( ! empty( $activity->duration ) ) {
list($h, $m, $s) = explode( ':', $activity->duration );
$total_seconds += $h * 3600 + $m * 60 + $s;
}
}
$hours = floor( $total_seconds / 3600 );
$minutes = floor( ( $total_seconds % 3600 ) / 60 );
$total_duration_formatted = sprintf( '%d godz. %d min.', $hours, $minutes );
// Rozpoczęcie buforowania wyjścia
ob_start();
?>
<div class="mystats-shortcode-container">
<h3>Podsumowanie miesiąca</h3>
<table class="mystats-summary-table">
<thead>
<tr>
<th>Całkowity dystans</th>
<th>Całkowity czas</th>
</tr>
</thead>
<tbody>
<tr>
<td><?php echo number_format( $total_distance, 2, ',', ' ' ); ?> km</td>
<td><?php echo esc_html( $total_duration_formatted ); ?></td>
</tr>
</tbody>
</table>
<h3>Lista aktywności</h3>
<table class="mystats-activity-table">
<thead>
<tr>
<th>Data</th>
<th>Tytuł</th>
<th>Kategoria</th>
<th>Dystans</th>
<th>Czas</th>
<th>Sprzęt</th>
</tr>
</thead>
<tbody>
<?php if ( ! empty( $activities ) ) : ?>
<?php foreach ( $activities as $row ) : ?>
<tr>
<td><?php echo esc_html( date_i18n( 'd.m.Y', strtotime( $row->date ) ) ); ?></td>
<td><?php echo esc_html( $row->title ); ?></td>
<td><?php echo esc_html( $row->category_name ); ?></td>
<td><?php echo number_format( $row->distance, 2, ',', ' ' ); ?> km</td>
<td><?php echo esc_html( $row->duration ); ?></td>
<td><?php echo esc_html( $row->equipment_name ); ?></td>
</tr>
<?php if ( $row->avg_speed || $row->avg_heart_rate || $row->avg_cadence ) : ?>
<tr class="mystats-activity-details-row">
<td colspan="6" class="mystats-activity-details-cell">
<table class="mystats-nested-details-table">
<thead>
<tr>
<?php if ( $row->avg_speed ) { echo '<th>Śr. prędkość</th>'; } ?>
<?php if ( $row->avg_heart_rate ) { echo '<th>Śr. tętno</th>'; } ?>
<?php if ( $row->avg_cadence ) { echo '<th>Śr. kadencja</th>'; } ?>
</tr>
</thead>
<tbody>
<tr>
<?php if ( $row->avg_speed ) { echo '<td>' . number_format( $row->avg_speed, 1, ',', ' ' ) . ' km/h</td>'; } ?>
<?php if ( $row->avg_heart_rate ) { echo '<td>' . $row->avg_heart_rate . ' bpm</td>'; } ?>
<?php if ( $row->avg_cadence ) { echo '<td>' . $row->avg_cadence . ' rpm</td>'; } ?>
</tr>
</tbody>
</table>
</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
<?php else : ?>
<tr>
<td colspan="6">Brak aktywności w tym miesiącu.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php
// Zwrócenie zawartości bufora
return ob_get_clean();
}
function mystat_single_activity_shortcode_handler( $atts ) {
wp_enqueue_style( 'mystat-frontend-styles' );
global $wpdb;
$atts = shortcode_atts(
array(
'id' => 0,
),
$atts,
'moje_statystyki_wpis'
);
$activity_id = intval( $atts['id'] );
if ( 0 === $activity_id ) {
return '<p><strong>Błąd:</strong> Nie podano ID wpisu w shortcode.</p>';
}
// Pobieranie danych z bazy
$table_activities = $wpdb->prefix . 'mystat_activities';
$sql = $wpdb->prepare(
"
SELECT a.*, c.name as category_name, c.color as category_color, et.name as event_type_name, eq.name as equipment_name
FROM $table_activities a
LEFT JOIN {$wpdb->prefix}mystat_categories c ON a.category_id = c.id
LEFT JOIN {$wpdb->prefix}mystat_event_types et ON a.event_type_id = et.id
LEFT JOIN {$wpdb->prefix}mystat_equipment eq ON a.equipment_id = eq.id
WHERE a.id = %d
",
$activity_id
);
$activity = $wpdb->get_row( $sql );
if ( ! $activity ) {
return '<p><strong>Błąd:</strong> Nie znaleziono wpisu o ID ' . esc_html( $activity_id ) . '.</p>';
}
// Funkcja pomocnicza do wyświetlania wiersza
$render_row = function( $label, $value, $unit = '' ) {
if ( ! is_null( $value ) && '' !== $value && 0 != $value ) {
echo '<tr>';
echo '<th>' . esc_html( $label ) . '</th>';
echo '<td>' . esc_html( $value ) . ( $unit ? ' ' . esc_html( $unit ) : '' ) . '</td>';
echo '</tr>';
}
};
ob_start();
// Prepare map and chart data if GPX exists
$gpx_data = array();
$has_gpx_data = false;
if ( ! empty( $activity->gpx_url ) ) {
$gpx_data = mystat_parse_gpx_data( $activity->gpx_url );
$has_gpx_data = ! empty( $gpx_data['points'] );
}
$unique_id = 'mystat-activity-' . esc_attr( $activity->id );
if ( $has_gpx_data ) {
wp_enqueue_style( 'leaflet-css', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' );
wp_enqueue_script( 'leaflet-js', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', array(), '1.9.4', true );
wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), null, true );
$map_id = 'map-' . $unique_id;
$chart_id = 'chart-' . $unique_id;
$available_profiles = array();
if ( ! empty( array_filter( $gpx_data['profiles']['elevation'] ) ) ) {
$available_profiles['elevation'] = 'Wysokość';}
if ( ! empty( array_filter( $gpx_data['profiles']['speed'] ) ) ) {
$available_profiles['speed'] = 'Prędkość';}
if ( ! empty( array_filter( $gpx_data['profiles']['hr'] ) ) ) {
$available_profiles['hr'] = 'Tętno';}
if ( ! empty( array_filter( $gpx_data['profiles']['cadence'] ) ) ) {
$available_profiles['cadence'] = 'Kadencja';}
$has_time_data = ! empty( array_filter( $gpx_data['profiles']['time'], fn( $t ) => ! is_null( $t ) ) );
wp_register_script( 'mystat-shortcode-loader-' . $activity->id, false );
wp_enqueue_script( 'mystat-shortcode-loader-' . $activity->id );
$js_script = '
(function() {
document.addEventListener("DOMContentLoaded", function() {
const uniqueId = "' . esc_js( $unique_id ) . '";
const containerEl = document.getElementById(uniqueId);
if (!containerEl || typeof L === "undefined" || typeof Chart === "undefined") return;
const trackPoints = ' . json_encode( $gpx_data['points'] ) . ';
const profiles = ' . json_encode( $gpx_data['profiles'] ) . ';
let activeChart = null;
const mapId = "' . esc_js( $map_id ) . '";
const mapEl = document.getElementById(mapId);
if (mapEl && trackPoints.length > 0) {
if (mapEl._leaflet_id) mapEl._leaflet_id = null;
const map = L.map(mapId);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: \'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>\' }).addTo(map);
const polyline = L.polyline(trackPoints, {color: "' . esc_js( $activity->category_color ) . '"}).addTo(map);
map.fitBounds(polyline.getBounds().pad(0.1));
}
const chartId = "' . esc_js( $chart_id ) . '";
const chartEl = document.getElementById(chartId);
if (!chartEl) return;
const chartConfigs = {
elevation: { label: "Wysokość", unit: "m n.p.m.", color: "#8e44ad" },
speed: { label: "Prędkość", unit: "km/h", color: "#2980b9" },
hr: { label: "Tętno", unit: "bpm", color: "#c0392b" },
cadence: { label: "Kadencja", unit: "rpm", color: "#27ae60" }
};
const xAxisConfigs = {
distance: { label: "Dystans (km)", data: profiles.distance },
time: { label: "Czas", data: profiles.time, formatter: (s) => s === null ? "" : new Date(s * 1000).toISOString().substr(11, 8) }
};
function renderChart() {
if (activeChart) activeChart.destroy();
const activeTab = containerEl.querySelector(".mystat-chart-tab.active");
if (!activeTab) return;
const chartType = activeTab.dataset.type;
const xAxisRadio = containerEl.querySelector(\'input[name="xaxis-\' + uniqueId + \'"]:checked\');
if (!xAxisRadio) return;
const xAxisType = xAxisRadio.value;
const yData = profiles[chartType], xData = xAxisConfigs[xAxisType].data;
const filteredY = [], filteredX = [];
if(yData) {
for(let i=0; i<yData.length; i++) {
if (yData[i] !== null) {
filteredY.push(yData[i]);
filteredX.push(xData[i]);
}
}
}
const ctx = chartEl.getContext("2d");
activeChart = new Chart(ctx, {
type: "line",
data: {
labels: filteredX,
datasets: [{
label: chartConfigs[chartType].label, data: filteredY,
borderColor: chartConfigs[chartType].color, backgroundColor: chartConfigs[chartType].color + "20",
fill: true, pointRadius: 0, tension: 0.1
}]
},
options: {
responsive: true, maintainAspectRatio: false,
scales: {
x: { title: { display: true, text: xAxisConfigs[xAxisType].label }, ticks: { callback: xAxisType === "time" ? xAxisConfigs.time.formatter : (v) => v, maxRotation: 0, autoSkip: true, maxTicksLimit: 7 } },
y: { title: { display: true, text: chartConfigs[chartType].unit } }
},
plugins: { legend: { display: false } },
interaction: { intersect: false, mode: "index" },
}
});
}
containerEl.querySelectorAll(".mystat-chart-tab").forEach(t => t.addEventListener("click", e => {
e.preventDefault();
containerEl.querySelector(".mystat-chart-tab.active").classList.remove("active");
e.currentTarget.classList.add("active");
renderChart();
}));
containerEl.querySelectorAll(\'input[name="xaxis-\' + uniqueId + \'"]\').forEach(r => r.addEventListener("change", renderChart));
if (containerEl.querySelector(".mystat-chart-tab")) renderChart();
});
})();';
wp_add_inline_script( 'mystat-shortcode-loader-' . $activity->id, $js_script );
}
?>
<div class="mystat-single-activity-shortcode" id="<?php echo esc_attr( $unique_id ); ?>">
<h4><?php echo esc_html( $activity->title ); ?></h4>
<p><em><?php echo esc_html( date_i18n( 'j F Y', strtotime( $activity->date ) ) ); ?></em></p>
<div class="mystat-single-columns-container">
<div class="mystat-single-col">
<table class="mystat-single-summary-table">
<tbody>
<?php $render_row( 'Dystans', number_format( $activity->distance, 2, ',', ' ' ), 'km' ); ?>
<?php $render_row( 'Czas trwania', $activity->duration ); ?>
<?php $render_row( 'Średnia prędkość', number_format( $activity->avg_speed, 1, ',', ' ' ), 'km/h' ); ?>
<?php $render_row( 'Suma wzniosów', $activity->total_elevation_gain, 'm' ); ?>
</tbody>
</table>
</div>
<div class="mystat-single-col">
<table class="mystat-single-summary-table">
<tbody>
<?php $render_row( 'Kategoria', $activity->category_name ); ?>
<?php $render_row( 'Sprzęt', $activity->equipment_name ); ?>
<?php if ( ! empty( $activity->strava_url ) ) : ?>
<tr><th>Strava</th><td><a href="<?php echo esc_url( $activity->strava_url ); ?>" target="_blank" rel="noopener noreferrer">Zobacz aktywność</a></td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php if ( $has_gpx_data ) : ?>
<div id="<?php echo esc_attr( $map_id ); ?>" class="mystat-single-map" style="height: 350px; width: 100%; margin-top: 15px; border-radius: 5px; margin-bottom: 20px;"></div>
<?php if ( ! empty( $available_profiles ) ) : ?>
<div class="mystat-charts-container">
<div class="mystat-chart-controls" style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; margin-bottom: 15px; border-bottom: 1px solid #eee; padding-bottom: 10px; gap: 10px;">
<div class="mystat-chart-tabs" style="display: flex; flex-wrap: wrap; gap: 5px;">
<?php
$is_first = true;
foreach ( $available_profiles as $key => $label ) :
?>
<button data-type="<?php echo esc_attr( $key ); ?>" class="mystat-chart-tab <?php
if ( $is_first ) {
echo 'active';
$is_first = false; }
?>
"><?php echo esc_html( $label ); ?></button>
<?php endforeach; ?>
</div>
<?php if ( $has_time_data ) : ?>
<div class="mystat-xaxis-switcher" style="padding-top: 5px; font-size: 0.9em; white-space: nowrap;">
<strong>Oś X:</strong>&nbsp;
<label><input type="radio" name="xaxis-<?php echo esc_attr( $unique_id ); ?>" value="distance" checked> Dystans</label>
&nbsp;
<label><input type="radio" name="xaxis-<?php echo esc_attr( $unique_id ); ?>" value="time"> Czas</label>
</div>
<?php else : ?>
<input type="hidden" name="xaxis-<?php echo esc_attr( $unique_id ); ?>" value="distance" checked>
<?php endif; ?>
</div>
<div class="mystat-chart-wrapper" style="position: relative; height:250px; width:100%;">
<canvas id="<?php echo esc_attr( $chart_id ); ?>"></canvas>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}