get_charset_collate();
$table_categories = $wpdb->prefix . 'mystat_categories';
$table_activities = $wpdb->prefix . 'mystat_activities';
$table_event_types = $wpdb->prefix . 'mystat_event_types';
$table_equipment = $wpdb->prefix . 'mystat_equipment';
// SQL dla Kategorii
$sql_cat = "CREATE TABLE $table_categories (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
icon varchar(50) NOT NULL,
color varchar(20) NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
// SQL dla Typów Wydarzeń
$sql_event_types = "CREATE TABLE $table_event_types (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
// SQL dla Sprzętu
$sql_equipment = "CREATE TABLE $table_equipment (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
// SQL dla Aktywności
$sql_act = "CREATE TABLE $table_activities (
id bigint(20) NOT NULL AUTO_INCREMENT,
category_id mediumint(9) NOT NULL,
date date NOT NULL,
title varchar(255) DEFAULT '' NOT NULL,
distance decimal(10,2) DEFAULT 0.00,
duration time DEFAULT '00:00:00',
calories int(11) DEFAULT 0,
comment text,
strava_url varchar(255) DEFAULT NULL,
avg_heart_rate smallint(5) UNSIGNED DEFAULT NULL,
max_heart_rate smallint(5) UNSIGNED DEFAULT NULL,
avg_speed decimal(5,2) DEFAULT NULL,
max_speed decimal(5,2) DEFAULT NULL,
avg_cadence smallint(5) UNSIGNED DEFAULT NULL,
max_cadence smallint(5) UNSIGNED DEFAULT NULL,
total_elevation_gain int(11) DEFAULT NULL,
total_elevation_loss int(11) DEFAULT NULL,
min_altitude int(11) DEFAULT NULL,
max_altitude int(11) DEFAULT NULL,
equipment_id mediumint(9) DEFAULT NULL,
gpx_url varchar(255) DEFAULT NULL,
event_type_id mediumint(9) DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql_equipment );
dbDelta( $sql_cat );
dbDelta( $sql_event_types );
dbDelta( $sql_act );
// Dodanie domyślnych kategorii, jeśli tabela jest pusta
if ( $wpdb->get_var( "SELECT COUNT(*) FROM $table_categories" ) == 0 ) {
$wpdb->insert( $table_categories, array( 'name' => 'Rower', 'icon' => 'dashicons-buddicons-groups', 'color' => '#3498db' ) );
$wpdb->insert( $table_categories, array( 'name' => 'Bieganie', 'icon' => 'dashicons-businessman', 'color' => '#e74c3c' ) );
}
// Dodanie domyślnych typów wydarzeń, jeśli tabela jest pusta
if ( $wpdb->get_var( "SELECT COUNT(*) FROM $table_event_types" ) == 0 ) {
$default_event_types = ['Bez kategorii', 'Fitness', 'Geocaching', 'Podróżowanie', 'Rekreacyjny', 'Specjalne zdarzenie', 'Transport', 'Trening', 'Wyścig'];
foreach ($default_event_types as $type_name) {
$wpdb->insert( $table_event_types, array( 'name' => $type_name ) );
}
// Ustawienie domyślnego typu "Trening" dla istniejących aktywności, które go nie mają
$training_id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $table_event_types WHERE name = %s", 'Trening' ) );
if ($training_id) {
$wpdb->query( $wpdb->prepare( "UPDATE $table_activities SET event_type_id = %d WHERE event_type_id IS NULL OR event_type_id = 0", $training_id ) );
}
}
// Dodanie domyślnego sprzętu, jeśli tabela jest pusta
if ( $wpdb->get_var( "SELECT COUNT(*) FROM $table_equipment" ) == 0 ) {
$default_equipment = ['Giant Revolt', 'Cube LTD', 'Author Agang', 'Liv Tempt 4', 'Cube Acid 24', 'Mongoose BMX', 'Nextbike - Miejski'];
foreach ($default_equipment as $eq_name) {
$wpdb->insert( $table_equipment, array( 'name' => $eq_name ) );
}
}
}
// --- 2. MENU ADMINA I DASHBOARD ---
add_action( 'admin_menu', 'mystat_add_admin_menu' );
function mystat_add_admin_menu() {
add_menu_page(
'Moje Statystyki', // Tytuł strony
'Statystyki', // Tytuł w menu
'manage_options', // Wymagane uprawnienia
'moje-statystyki', // Slug menu
'mystat_dashboard_page', // Funkcja renderująca stronę główną (dashboard)
'dashicons-chart-line', // Ikona
6 // Pozycja
);
add_submenu_page(
'moje-statystyki', // Slug rodzica
'Dodaj Nowy Trening', // Tytuł strony
'Nowy trening', // Tytuł w podmenu
'manage_options', // Wymagane uprawnienia
'mystat-nowy-trening', // Slug podmenu
'mystat_add_new_page' // Funkcja renderująca stronę dodawania
);
add_submenu_page(
'moje-statystyki',
'Typy Wydarzeń',
'Typy wydarzeń',
'manage_options',
'mystat-event-types',
'mystat_event_types_page'
);
add_submenu_page(
'moje-statystyki',
'Sprzęt',
'Sprzęt',
'manage_options',
'mystat-equipment',
'mystat_equipment_page'
);
add_submenu_page(
null, // Ukryta strona, nie pojawia się w menu
'Szczegóły Treningu', // Tytuł strony
'Szczegóły Treningu', // Tytuł w menu (nieistotny)
'manage_options', // Wymagane uprawnienia
'mystat-view-activity', // Slug podmenu
'mystat_view_activity_page' // Funkcja renderująca
);
add_submenu_page(
null, // Ukryta strona
'Edytuj Trening', // Tytuł strony
'Edytuj Trening', // Tytuł w menu (nieistotny)
'manage_options', // Wymagane uprawnienia
'mystat-edit-activity', // Slug podmenu
'mystat_edit_activity_page' // Funkcja renderująca
);
}
function mystat_dashboard_page() {
echo '
Moje Statystyki Sportowe
';
mystat_render_history_table();
echo '';
}
function mystat_add_new_page() {
echo 'Dodaj Nowy Trening
';
// Obsługa zapisu formularza (musi być przed renderowaniem, aby wyświetlić komunikat)
mystat_handle_activity_form_submission();
// Formularz dodawania
mystat_render_add_form();
echo '';
}
function mystat_event_types_page() {
global $wpdb;
$table_event_types = $wpdb->prefix . 'mystat_event_types';
$message = '';
$notice_class = '';
// Handle POST requests (add/update)
if ( isset( $_POST['submit'] ) && check_admin_referer( 'mystat_manage_event_type' ) ) {
$name = sanitize_text_field( $_POST['event_type_name'] );
$type_id = isset( $_POST['event_type_id'] ) ? intval( $_POST['event_type_id'] ) : 0;
if ( ! empty( $name ) ) {
if ( $type_id > 0 ) { // Update
$wpdb->update( $table_event_types, [ 'name' => $name ], [ 'id' => $type_id ] );
$message = 'Typ wydarzenia zaktualizowany.';
$notice_class = 'notice-success';
} else { // Insert
$wpdb->insert( $table_event_types, [ 'name' => $name ] );
$message = 'Typ wydarzenia dodany.';
$notice_class = 'notice-success';
}
} else {
$message = 'Nazwa typu wydarzenia nie może być pusta.';
$notice_class = 'notice-error';
}
}
// Handle GET requests (delete)
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && $_GET['action'] === 'delete' ) {
if ( wp_verify_nonce( $_GET['_wpnonce'], 'mystat_delete_event_type_' . $_GET['id'] ) ) {
$wpdb->delete( $table_event_types, [ 'id' => intval( $_GET['id'] ) ] );
$message = 'Typ wydarzenia usunięty.';
$notice_class = 'notice-success';
}
}
// Prepare for form (for editing)
$item_to_edit = null;
if ( isset( $_GET['action'], $_GET['id'] ) && $_GET['action'] === 'edit' ) {
$item_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_event_types WHERE id = %d", intval( $_GET['id'] ) ) );
}
$event_types = $wpdb->get_results( "SELECT * FROM $table_event_types ORDER BY name ASC" );
?>
prefix . 'mystat_equipment';
$message = '';
$notice_class = '';
// Handle POST requests (add/update)
if ( isset( $_POST['submit'] ) && check_admin_referer( 'mystat_manage_equipment' ) ) {
$name = sanitize_text_field( $_POST['equipment_name'] );
$item_id = isset( $_POST['equipment_id'] ) ? intval( $_POST['equipment_id'] ) : 0;
if ( ! empty( $name ) ) {
if ( $item_id > 0 ) { // Update
$wpdb->update( $table_equipment, [ 'name' => $name ], [ 'id' => $item_id ] );
$message = 'Sprzęt zaktualizowany.';
$notice_class = 'notice-success';
} else { // Insert
$wpdb->insert( $table_equipment, [ 'name' => $name ] );
$message = 'Sprzęt dodany.';
$notice_class = 'notice-success';
}
} else {
$message = 'Nazwa sprzętu nie może być pusta.';
$notice_class = 'notice-error';
}
}
// Handle GET requests (delete)
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && $_GET['action'] === 'delete' ) {
if ( wp_verify_nonce( $_GET['_wpnonce'], 'mystat_delete_equipment_' . $_GET['id'] ) ) {
$wpdb->delete( $table_equipment, [ 'id' => intval( $_GET['id'] ) ] );
$message = 'Sprzęt usunięty.';
$notice_class = 'notice-success';
}
}
// Prepare for form (for editing)
$item_to_edit = null;
if ( isset( $_GET['action'], $_GET['id'] ) && $_GET['action'] === 'edit' ) {
$item_to_edit = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_equipment WHERE id = %d", intval( $_GET['id'] ) ) );
}
$equipment_list = $wpdb->get_results( "SELECT * FROM $table_equipment ORDER BY name ASC" );
?>
Błąd
Nie podano ID aktywności do edycji.
';
return;
}
// Handle form submission for update
mystat_handle_activity_form_submission();
$table_activities = $wpdb->prefix . 'mystat_activities';
$activity = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_activities WHERE id = %d", $activity_id ) );
if ( ! $activity ) {
echo 'Błąd
Nie znaleziono aktywności o podanym ID.
';
return;
}
echo 'Edytuj Trening
';
mystat_render_add_form( $activity );
echo '';
}
function mystat_view_activity_page() {
global $wpdb;
$activity_id = isset( $_GET['id'] ) ? intval( $_GET['id'] ) : 0;
if ( $activity_id === 0 ) {
echo 'Błąd
Nie podano ID aktywności.
';
return;
}
$table_activities = $wpdb->prefix . 'mystat_activities';
$table_categories = $wpdb->prefix . 'mystat_categories';
$table_event_types = $wpdb->prefix . 'mystat_event_types';
$table_equipment = $wpdb->prefix . 'mystat_equipment';
$sql = $wpdb->prepare("
SELECT a.*, c.name as category_name, c.icon, c.color, et.name as event_type_name, eq.name as equipment_name
FROM $table_activities a
LEFT JOIN $table_categories c ON a.category_id = c.id
LEFT JOIN $table_event_types et ON a.event_type_id = et.id
LEFT JOIN $table_equipment eq ON a.equipment_id = eq.id
WHERE a.id = %d
", $activity_id);
$activity = $wpdb->get_row( $sql );
if ( ! $activity ) {
echo 'Błąd
Nie znaleziono aktywności o podanym ID.
';
return;
}
// Funkcja pomocnicza do wyświetlania wiersza, jeśli wartość istnieje
$render_row = function($label, $value, $unit = '') {
if ( ! is_null($value) && $value !== '' ) {
echo '';
echo '| ' . esc_html($label) . ' | ';
echo '' . esc_html($value) . ($unit ? ' ' . esc_html($unit) : '') . ' | ';
echo '
';
}
};
?>
0 ? 'mystat_edit_entry_' . $activity_id : 'mystat_add_entry';
// Weryfikacja bezpieczeństwa (Nonce)
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], $nonce_action ) ) {
echo 'Błąd weryfikacji bezpieczeństwa formularza.
';
return;
}
$table_activities = $wpdb->prefix . 'mystat_activities';
// Przygotowanie danych (zamiana przecinka na kropkę w dystansie)
$distance = isset($_POST['distance']) ? floatval( str_replace( ',', '.', $_POST['distance'] ) ) : 0;
// Funkcja pomocnicza do zamiany pustych wartości na NULL, aby poprawnie zapisać je w bazie
$null_if_empty = function($value) {
return $value !== '' ? $value : null;
};
$data = array(
'category_id' => intval( $_POST['category_id'] ),
'date' => sanitize_text_field( $_POST['date'] ),
'title' => sanitize_text_field( $_POST['title'] ),
'distance' => $distance,
'duration' => sanitize_text_field( $_POST['duration'] ),
'calories' => intval( $_POST['calories'] ),
'comment' => sanitize_textarea_field( $_POST['comment'] ),
'strava_url' => $null_if_empty( esc_url_raw( $_POST['strava_url'] ) ),
'avg_heart_rate' => $null_if_empty( intval( $_POST['avg_heart_rate'] ) ),
'max_heart_rate' => $null_if_empty( intval( $_POST['max_heart_rate'] ) ),
'avg_speed' => $null_if_empty( floatval( str_replace( ',', '.', $_POST['avg_speed'] ) ) ),
'max_speed' => $null_if_empty( floatval( str_replace( ',', '.', $_POST['max_speed'] ) ) ),
'avg_cadence' => $null_if_empty( intval( $_POST['avg_cadence'] ) ),
'max_cadence' => $null_if_empty( intval( $_POST['max_cadence'] ) ),
'total_elevation_gain' => $null_if_empty( intval( $_POST['total_elevation_gain'] ) ),
'total_elevation_loss' => $null_if_empty( intval( $_POST['total_elevation_loss'] ) ),
'min_altitude' => $null_if_empty( intval( $_POST['min_altitude'] ) ),
'max_altitude' => $null_if_empty( intval( $_POST['max_altitude'] ) ),
'equipment_id' => $null_if_empty( intval( $_POST['equipment_id'] ) ),
'gpx_url' => $null_if_empty( esc_url_raw( $_POST['gpx_url'] ) ),
'event_type_id' => $null_if_empty( intval( $_POST['event_type_id'] ) ),
);
// Format danych dla $wpdb->insert
$format = array(
'%d', '%s', '%s', '%f', '%s', '%d', '%s', // Pola podstawowe
'%s', '%d', '%d', '%f', '%f', '%d', '%d', // Tętno, prędkość, kadencja
'%d', '%d', '%d', '%d', '%d', '%s', '%d' // Wysokość, sprzęt, linki, typ wydarzenia
);
if ( $activity_id > 0 ) {
// UPDATE
$result = $wpdb->update( $table_activities, $data, [ 'id' => $activity_id ], $format, [ '%d' ] );
$message = 'Trening zaktualizowany pomyślnie!';
} else {
// INSERT
$result = $wpdb->insert( $table_activities, $data, $format );
$message = 'Trening dodany pomyślnie!';
}
if ( $result !== false ) {
echo '' . esc_html( $message ) . '
';
} else {
echo 'Wystąpił błąd podczas zapisu do bazy.
';
}
}
/**
* Renderowanie formularza HTML
*/
function mystat_render_add_form( $activity = null ) {
global $wpdb;
$table_categories = $wpdb->prefix . 'mystat_categories';
$table_event_types = $wpdb->prefix . 'mystat_event_types';
$table_equipment = $wpdb->prefix . 'mystat_equipment';
$categories = $wpdb->get_results( "SELECT * FROM $table_categories ORDER BY name ASC" );
$event_types = $wpdb->get_results( "SELECT * FROM $table_event_types ORDER BY name ASC" );
$equipment_list = $wpdb->get_results( "SELECT * FROM $table_equipment ORDER BY name ASC" );
$is_edit_mode = ! is_null( $activity );
$nonce_action = $is_edit_mode ? 'mystat_edit_entry_' . $activity->id : 'mystat_add_entry';
$form_title = $is_edit_mode ? 'Edytuj Aktywność' : 'Dodaj Nową Aktywność';
$button_text = $is_edit_mode ? 'Zaktualizuj Trening' : 'Zapisz Trening';
?>
prefix
// Jeśli tabele są "sztywne" (bez prefixu wp_), usuń $wpdb->prefix.
$table_activities = $wpdb->prefix . 'mystat_activities';
$table_categories = $wpdb->prefix . 'mystat_categories';
// --- 1. OBSŁUGA USUWANIA (DELETE) ---
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && $_GET['action'] === 'mystat_delete' ) {
$activity_id = intval( $_GET['id'] );
// Weryfikacja bezpieczeństwa (Nonce)
if ( wp_verify_nonce( $_GET['_wpnonce'], 'mystat_delete_' . $activity_id ) ) {
$result = $wpdb->delete(
$table_activities,
array( 'id' => $activity_id ),
array( '%d' )
);
if ( $result ) {
echo 'Aktywność została usunięta.
';
} else {
echo 'Wystąpił błąd podczas usuwania.
';
}
} else {
echo 'Błąd weryfikacji bezpieczeństwa (Nonce).
';
}
}
// --- 2. POBIERANIE DANYCH (SELECT) ---
// Pobieramy ostatnie 10 wpisów, łącząc z tabelą kategorii, aby mieć nazwę i ikonę
$sql = $wpdb->prepare("
SELECT a.*, c.name as category_name, c.icon, c.color, et.name as event_type_name, eq.name as equipment_name
FROM $table_activities a
LEFT JOIN $table_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
ORDER BY a.date DESC, a.id DESC
LIMIT %d
", 10);
$activities = $wpdb->get_results( $sql );
// --- 3. WIDOK TABELI (HTML) ---
?>
Ostatnie Aktywności
| Ikona |
Data |
Tytuł |
Kategoria |
Typ |
Sprzęt |
Dystans (km) |
Czas |
Śr. prędkość |
Akcja |
$_REQUEST['page'], // Zachowaj obecną stronę admina
'action' => 'mystat_delete',
'id' => $row->id,
'_wpnonce' => wp_create_nonce( 'mystat_delete_' . $row->id )
), admin_url( 'admin.php' ) );
$edit_url = add_query_arg( array(
'page' => 'mystat-edit-activity',
'id' => $row->id
), admin_url( 'admin.php' ) );
$details_url = add_query_arg( array(
'page' => 'mystat-view-activity',
'id' => $row->id
), admin_url( 'admin.php' ) );
?>
|
icon ) ) : ?>
|
date ); ?> |
title ? wp_trim_words( $row->title, 6 ) : '(bez tytułu)' ); ?> |
category_name ); ?> |
event_type_name ); ?> |
equipment_name ); ?> |
distance, 2, ',', ' ' ); ?> |
duration ); ?> |
avg_speed ? number_format( $row->avg_speed, 1, ',', ' ' ) . ' km/h' : '-'; ?> |
Edytuj
Szczegóły
Usuń
|
| Brak zarejestrowanych aktywności. Dodaj pierwszy trening powyżej! |
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();
?>
Podsumowanie miesiąca
| Całkowity dystans |
Całkowity czas |
| km |
|
Lista aktywności
| Data |
Tytuł |
Kategoria |
Dystans |
Czas |
Sprzęt |
| date ) ) ); ?> |
title ); ?> |
category_name ); ?> |
distance, 2, ',', ' ' ); ?> km |
duration ); ?> |
equipment_name ); ?> |
| Brak aktywności w tym miesiącu. |
0,
), $atts, 'moje_statystyki_wpis' );
$activity_id = intval( $atts['id'] );
if ( $activity_id === 0 ) {
return 'Błąd: Nie podano ID wpisu w shortcode.
';
}
// Pobieranie danych z bazy
$table_activities = $wpdb->prefix . 'mystat_activities';
$sql = $wpdb->prepare("
SELECT a.*, c.name as category_name, 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 'Błąd: Nie znaleziono wpisu o ID ' . esc_html($activity_id) . '.
';
}
// Funkcja pomocnicza do wyświetlania wiersza
$render_row = function($label, $value, $unit = '') {
if ( ! is_null($value) && $value !== '' && $value != 0) {
echo '';
echo '| ' . esc_html($label) . ' | ';
echo '' . esc_html($value) . ($unit ? ' ' . esc_html($unit) : '') . ' | ';
echo '
';
}
};
ob_start();
?>
title ); ?>
date ) ) ); ?>
distance, 2, ',', ' '), 'km'); ?>
duration); ?>
avg_speed, 1, ',', ' '), 'km/h'); ?>
total_elevation_gain, 'm'); ?>
category_name); ?>
equipment_name); ?>
strava_url ) ) : ?>
| Strava | Zobacz aktywność |