Update repo

This commit is contained in:
2026-04-22 12:51:16 +02:00
parent d303a55638
commit d31591e287
24 changed files with 3994 additions and 3501 deletions
+41
View File
@@ -58,3 +58,44 @@
padding-top: 5px; padding-top: 5px;
white-space: nowrap; white-space: nowrap;
} }
/* Form layout improvements */
.statpress-form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-top: 1em;
}
.statpress-form-group {
background: #fdfdfd;
border: 1px solid #e5e5e5;
padding: 15px;
border-radius: 4px;
}
.statpress-form-group h3 {
margin-top: 0;
font-size: 1em;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
margin-bottom: 15px;
}
.statpress-form-group p {
margin: 0 0 1em;
}
.statpress-form-group label {
display: block;
margin-bottom: 5px;
}
/* GPX Parser Status */
#gpx_parse_status {
vertical-align: middle;
color: #50575e;
}
#gpx_parse_status .spinner {
margin-top: -2px;
}
+23 -23
View File
@@ -1,34 +1,34 @@
/* Styles for WordPress Activity Stats Plugin - Frontend Shortcodes */ /* Styles for WordPress Activity Stats Plugin - Frontend Shortcodes */
/* Shortcode [moje_statystyki] */ /* Shortcode [statpress_summary] */
.mystats-shortcode-container table { .statpress-shortcode-container table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
margin-bottom: 2em; margin-bottom: 2em;
} }
.mystats-shortcode-container th, .statpress-shortcode-container th,
.mystats-shortcode-container td { .statpress-shortcode-container td {
padding: 8px 12px; padding: 8px 12px;
border: 1px solid #ddd; border: 1px solid #ddd;
text-align: left; text-align: left;
} }
.mystats-shortcode-container th { .statpress-shortcode-container th {
background-color: #f4f4f4; background-color: #f4f4f4;
} }
.mystats-activity-table td:nth-child(4), .statpress-activity-table td:nth-child(4),
.mystats-activity-table td:nth-child(5) { .statpress-activity-table td:nth-child(5) {
text-align: right; text-align: right;
} }
/* Styles for the nested details table */ /* Styles for the nested details table */
.mystats-activity-details-row > .mystats-activity-details-cell { .statpress-activity-details-row > .statpress-activity-details-cell {
padding: 0 !important; padding: 0 !important;
border-top: none; border-top: none;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
} }
.mystats-nested-details-table { .statpress-nested-details-table {
width: 100%; width: 100%;
margin: 0; margin: 0;
background-color: #fdfdfd; background-color: #fdfdfd;
@@ -36,8 +36,8 @@
border-collapse: collapse; border-collapse: collapse;
} }
.mystats-nested-details-table th, .statpress-nested-details-table th,
.mystats-nested-details-table td { .statpress-nested-details-table td {
border: none !important; border: none !important;
padding: 6px 12px; padding: 6px 12px;
text-align: center; text-align: center;
@@ -45,7 +45,7 @@
width: 33.33%; width: 33.33%;
} }
.mystats-nested-details-table th { .statpress-nested-details-table th {
background-color: transparent; background-color: transparent;
font-weight: normal; font-weight: normal;
color: #777; color: #777;
@@ -53,49 +53,49 @@
padding-bottom: 2px; padding-bottom: 2px;
} }
.mystats-nested-details-table td { .statpress-nested-details-table td {
font-weight: bold; font-weight: bold;
padding-top: 0; padding-top: 0;
padding-bottom: 8px; padding-bottom: 8px;
} }
/* Shortcode [moje_statystyki_wpis] */ /* Shortcode [statpress_activity] */
.mystat-single-activity-shortcode { .statpress-single-activity-shortcode {
border: 1px solid #eee; border: 1px solid #eee;
padding: 15px; padding: 15px;
margin-bottom: 1.5em; margin-bottom: 1.5em;
border-radius: 5px; border-radius: 5px;
background: #f9f9f9; background: #f9f9f9;
} }
.mystat-single-activity-shortcode h4 { .statpress-single-activity-shortcode h4 {
margin-top: 0; margin-top: 0;
} }
.mystat-single-activity-shortcode p em { .statpress-single-activity-shortcode p em {
color: #777; color: #777;
font-size: 0.9em; font-size: 0.9em;
} }
.mystat-single-columns-container { .statpress-single-columns-container {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 30px; gap: 30px;
margin-bottom: 15px; margin-bottom: 15px;
} }
.mystat-single-col { .statpress-single-col {
flex: 1; flex: 1;
min-width: 240px; min-width: 240px;
} }
.mystat-single-summary-table { .statpress-single-summary-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
} }
.mystat-single-summary-table th, .statpress-single-summary-table th,
.mystat-single-summary-table td { .statpress-single-summary-table td {
padding: 4px 0; padding: 4px 0;
border: none; border: none;
text-align: left; text-align: left;
vertical-align: top; vertical-align: top;
} }
.mystat-single-summary-table th { .statpress-single-summary-table th {
font-weight: bold; font-weight: bold;
padding-right: 1em; padding-right: 1em;
white-space: nowrap; white-space: nowrap;
+7 -7
View File
@@ -14,16 +14,16 @@ if ( ! defined( 'ABSPATH' ) ) {
* *
* @return void * @return void
*/ */
function mystat_activate() { function statpress_activate() {
global $wpdb; global $wpdb;
$charset_collate = $wpdb->get_charset_collate(); $charset_collate = $wpdb->get_charset_collate();
$table_categories = $wpdb->prefix . 'mystat_categories'; $table_categories = $wpdb->prefix . 'statpress_categories';
$table_activities = $wpdb->prefix . 'mystat_activities'; $table_activities = $wpdb->prefix . 'statpress_activities';
$table_event_types = $wpdb->prefix . 'mystat_event_types'; $table_event_types = $wpdb->prefix . 'statpress_event_types';
$table_equipment = $wpdb->prefix . 'mystat_equipment'; $table_equipment = $wpdb->prefix . 'statpress_equipment';
$table_equipment_log = $wpdb->prefix . 'mystat_equipment_log'; $table_equipment_log = $wpdb->prefix . 'statpress_equipment_log';
$table_goals = $wpdb->prefix . 'mystat_goals'; $table_goals = $wpdb->prefix . 'statpress_goals';
// SQL dla Kategorii // SQL dla Kategorii
$sql_cat = "CREATE TABLE $table_categories ( $sql_cat = "CREATE TABLE $table_categories (
+8 -8
View File
@@ -6,10 +6,10 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* Set up admin-specific hooks. * Set up admin-specific hooks.
*/ */
function mystat_admin_init_setup() { function statpress_admin_init_setup() {
add_filter( 'upload_mimes', 'mystat_add_gpx_mime_type' ); add_filter( 'upload_mimes', 'statpress_add_gpx_mime_type' );
add_filter( 'wp_check_filetype_and_ext', 'mystat_fix_gpx_upload_permission', 10, 4 ); add_filter( 'wp_check_filetype_and_ext', 'statpress_fix_gpx_upload_permission', 10, 4 );
mystat_register_settings(); statpress_register_settings();
} }
/** /**
@@ -17,11 +17,11 @@ function mystat_admin_init_setup() {
* *
* @param string $hook The current admin page hook. * @param string $hook The current admin page hook.
*/ */
function mystat_enqueue_admin_styles( $hook ) { function statpress_enqueue_admin_styles( $hook ) {
global $mystat_plugin_hooks; global $statpress_plugin_hooks;
if ( in_array( $hook, $mystat_plugin_hooks, true ) ) { if ( is_array( $statpress_plugin_hooks ) && in_array( $hook, $statpress_plugin_hooks, true ) ) {
$plugin_version = '1.0'; // You can use filemtime() for cache-busting in development $plugin_version = '1.0'; // You can use filemtime() for cache-busting in development
wp_enqueue_style( 'mystat-admin-styles', MYSTAT_PLUGIN_URL . 'assets/css/admin.css', array(), $plugin_version ); wp_enqueue_style( 'statpress-admin-styles', STATPRESS_PLUGIN_URL . 'assets/css/admin.css', array(), $plugin_version );
} }
} }
+47 -47
View File
@@ -14,110 +14,110 @@ if ( ! defined( 'ABSPATH' ) ) {
* *
* @return void * @return void
*/ */
function mystat_add_admin_menu() { function statpress_add_admin_menu() {
global $mystat_plugin_hooks; global $statpress_plugin_hooks;
$mystat_plugin_hooks[] = add_menu_page( $statpress_plugin_hooks[] = add_menu_page(
'Moje Statystyki', // Tytuł strony 'StatPress Dashboard', // Tytuł strony
'Statystyki', // Tytuł w menu 'StatPress', // Tytuł w menu
'manage_options', // Wymagane uprawnienia 'manage_options', // Wymagane uprawnienia
'moje-statystyki', // Slug menu 'statpress-dashboard', // Slug menu
'mystat_dashboard_page', // Funkcja renderująca stronę główną (dashboard) 'statpress_dashboard_page', // Funkcja renderująca stronę główną (dashboard)
'dashicons-chart-line', // Ikona 'dashicons-chart-line', // Ikona
6 // Pozycja 6 // Pozycja
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
'moje-statystyki', // Slug rodzica 'statpress-dashboard', // Slug rodzica
'Dodaj Nowy Trening', // Tytuł strony 'Dodaj Nowy Trening', // Tytuł strony
'Nowy trening', // Tytuł w podmenu 'Nowy trening', // Tytuł w podmenu
'manage_options', // Wymagane uprawnienia 'manage_options', // Wymagane uprawnienia
'mystat-nowy-trening', // Slug podmenu 'statpress-add-new', // Slug podmenu
'mystat_add_new_page' // Funkcja renderująca stronę dodawania 'statpress_add_new_page' // Funkcja renderująca stronę dodawania
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
'moje-statystyki', 'statpress-dashboard',
'Typy Wydarzeń', 'Typy Wydarzeń',
'Typy wydarzeń', 'Typy wydarzeń',
'manage_options', 'manage_options',
'mystat-event-types', 'statpress-event-types',
'mystat_event_types_page' 'statpress_event_types_page'
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
'moje-statystyki', 'statpress-dashboard',
'Sprzęt', 'Sprzęt',
'Sprzęt', 'Sprzęt',
'manage_options', 'manage_options',
'mystat-equipment', 'statpress-equipment',
'mystat_equipment_page' 'statpress_equipment_page'
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
'moje-statystyki', 'statpress-dashboard',
'Cele', 'Cele',
'Cele', 'Cele',
'manage_options', 'manage_options',
'mystat-goals', 'statpress-goals',
'mystat_goals_page' 'statpress_goals_page'
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
null, 'Dziennik Serwisowy', 'Dziennik Serwisowy', 'manage_options', 'mystat-equipment-details', 'mystat_equipment_details_page' null, 'Dziennik Serwisowy', 'Dziennik Serwisowy', 'manage_options', 'statpress-equipment-details', 'statpress_equipment_details_page'
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
null, // Ukryta strona, nie pojawia się w menu null, // Ukryta strona, nie pojawia się w menu
'Szczegóły Treningu', // Tytuł strony 'Szczegóły Treningu', // Tytuł strony
'Szczegóły Treningu', // Tytuł w menu (nieistotny) 'Szczegóły Treningu', // Tytuł w menu (nieistotny)
'manage_options', // Wymagane uprawnienia 'manage_options', // Wymagane uprawnienia
'mystat-view-activity', // Slug podmenu 'statpress-view-activity', // Slug podmenu
'mystat_view_activity_page' // Funkcja renderująca 'statpress_view_activity_page' // Funkcja renderująca
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
null, // Ukryta strona null, // Ukryta strona
'Edytuj Trening', // Tytuł strony 'Edytuj Trening', // Tytuł strony
'Edytuj Trening', // Tytuł w menu (nieistotny) 'Edytuj Trening', // Tytuł w menu (nieistotny)
'manage_options', // Wymagane uprawnienia 'manage_options', // Wymagane uprawnienia
'mystat-edit-activity', // Slug podmenu 'statpress-edit-activity', // Slug podmenu
'mystat_edit_activity_page' // Funkcja renderująca 'statpress_edit_activity_page' // Funkcja renderująca
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
'moje-statystyki', // Slug rodzica 'statpress-dashboard', // Slug rodzica
'Podsumowanie Roczne', // Tytuł strony 'Podsumowanie Roczne', // Tytuł strony
'Podsumowanie Roczne', // Tytuł w podmenu 'Podsumowanie Roczne', // Tytuł w podmenu
'manage_options', // Wymagane uprawnienia 'manage_options', // Wymagane uprawnienia
'mystat-yearly-summary', // Slug podmenu 'statpress-yearly-summary', // Slug podmenu
'mystat_yearly_summary_page'// Funkcja renderująca 'statpress_yearly_summary_page'// Funkcja renderująca
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
'moje-statystyki', // Slug rodzica 'statpress-dashboard', // Slug rodzica
'Infografika', // Tytuł strony 'Infografika', // Tytuł strony
'Infografika', // Tytuł w podmenu 'Infografika', // Tytuł w podmenu
'manage_options', // Wymagane uprawnienia 'manage_options', // Wymagane uprawnienia
'mystat-infographic', // Slug podmenu 'statpress-infographic', // Slug podmenu
'mystat_infographic_page' // Funkcja renderująca 'statpress_infographic_page' // Funkcja renderująca
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
'moje-statystyki', // Slug rodzica 'statpress-dashboard', // Slug rodzica
'Import CSV', // Tytuł strony 'Import CSV', // Tytuł strony
'Import CSV', // Tytuł w podmenu 'Import CSV', // Tytuł w podmenu
'manage_options', // Wymagane uprawnienia 'manage_options', // Wymagane uprawnienia
'mystat-import-csv', // Slug podmenu 'statpress-import-csv', // Slug podmenu
'mystat_import_csv_page' // Funkcja renderująca 'statpress_import_csv_page' // Funkcja renderująca
); );
$mystat_plugin_hooks[] = add_submenu_page( $statpress_plugin_hooks[] = add_submenu_page(
'moje-statystyki', // Slug rodzica 'statpress-dashboard', // Slug rodzica
'Ustawienia', // Tytuł strony 'Ustawienia', // Tytuł strony
'Ustawienia', // Tytuł w podmenu 'Ustawienia', // Tytuł w podmenu
'manage_options', // Wymagane uprawnienia 'manage_options', // Wymagane uprawnienia
'mystat-settings', // Slug podmenu 'statpress-settings', // Slug podmenu
'mystat_settings_page' // Funkcja renderująca 'statpress_settings_page' // Funkcja renderująca
); );
} }
+111 -51
View File
@@ -106,6 +106,18 @@ function statpress_render_add_form( $activity = null ) {
<th scope="row"><label for="title">Tytuł</label></th> <th scope="row"><label for="title">Tytuł</label></th>
<td><input type="text" name="title" id="title" class="large-text" placeholder="np. Poranny trening w lesie" value="<?php echo $is_edit_mode ? esc_attr( $activity->title ) : ''; ?>" required></td> <td><input type="text" name="title" id="title" class="large-text" placeholder="np. Poranny trening w lesie" value="<?php echo $is_edit_mode ? esc_attr( $activity->title ) : ''; ?>" required></td>
</tr> </tr>
<tr>
<th scope="row"><label for="gpx_url">Link do pliku GPX</label></th>
<td>
<input type="text" name="gpx_url" id="gpx_url" class="large-text" placeholder="Wklej URL lub wgraj plik..." value="<?php echo $is_edit_mode ? esc_attr( $activity->gpx_url ) : ''; ?>" style="width: calc(100% - 150px); margin-right: 10px;">
<span id="gpx_parse_status"></span>
<button type="button" class="button" id="upload_gpx_button" style="margin-top: 5px;">Wgraj lub wybierz plik</button>
<p class="description">Wklej link lub wgraj plik GPX, aby automatycznie uzupełnić pola poniżej.</p>
</td>
</tr>
<tr>
<td colspan="2" style="padding: 15px 0 5px;"><hr></td>
</tr>
<tr> <tr>
<th scope="row"><label for="category_id">Kategoria</label></th> <th scope="row"><label for="category_id">Kategoria</label></th>
<td> <td>
@@ -155,67 +167,45 @@ function statpress_render_add_form( $activity = null ) {
</select> </select>
</td> </td>
</tr> </tr>
<tr class="form-field">
<td colspan="2"><hr><h4>Dane szczegółowe (opcjonalne)</h4></td>
</tr>
<tr> <tr>
<th scope="row"><label for="avg_speed">Śr. prędkość (km/h)</label></th> <td colspan="2" style="padding: 20px 0;">
<td><input type="text" name="avg_speed" id="avg_speed" class="small-text" placeholder="0,0" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->avg_speed, 2, ',', '' ) ) : ''; ?>"></td> <hr>
</tr> <h3 style="font-size: 1.2em; margin: 1em 0;">Dane szczegółowe (opcjonalne)</h3>
<tr> <div class="statpress-form-grid">
<th scope="row"><label for="max_speed">Maks. prędkość (km/h)</label></th> <div class="statpress-form-group">
<td><input type="text" name="max_speed" id="max_speed" class="small-text" placeholder="0,0" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->max_speed, 2, ',', '' ) ) : ''; ?>"></td> <h3><span class="dashicons dashicons-dashboard" style="vertical-align: middle;"></span> Prędkość</h3>
</tr> <p><label for="avg_speed">Średnia (km/h)</label><input type="text" name="avg_speed" id="avg_speed" placeholder="0,0" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->avg_speed, 2, ',', '' ) ) : ''; ?>"></p>
<tr> <p><label for="max_speed">Maksymalna (km/h)</label><input type="text" name="max_speed" id="max_speed" placeholder="0,0" value="<?php echo $is_edit_mode ? esc_attr( number_format( (float) $activity->max_speed, 2, ',', '' ) ) : ''; ?>"></p>
<th scope="row"><label for="avg_heart_rate">Śr. tętno</label></th> </div>
<td><input type="number" name="avg_heart_rate" id="avg_heart_rate" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->avg_heart_rate ) : ''; ?>"></td> <div class="statpress-form-group">
</tr> <h3><span class="dashicons dashicons-heart" style="vertical-align: middle;"></span> Tętno</h3>
<tr> <p><label for="avg_heart_rate">Średnie (bpm)</label><input type="number" name="avg_heart_rate" id="avg_heart_rate" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->avg_heart_rate ) : ''; ?>"></p>
<th scope="row"><label for="max_heart_rate">Maks. tętno</label></th> <p><label for="max_heart_rate">Maksymalne (bpm)</label><input type="number" name="max_heart_rate" id="max_heart_rate" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_heart_rate ) : ''; ?>"></p>
<td><input type="number" name="max_heart_rate" id="max_heart_rate" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_heart_rate ) : ''; ?>"></td> </div>
</tr> <div class="statpress-form-group">
<tr> <h3><span class="dashicons dashicons-update" style="vertical-align: middle;"></span> Rytm</h3>
<th scope="row"><label for="avg_cadence">Śr. rytm pedałowania</label></th> <p><label for="avg_cadence">Średni (rpm)</label><input type="number" name="avg_cadence" id="avg_cadence" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->avg_cadence ) : ''; ?>"></p>
<td><input type="number" name="avg_cadence" id="avg_cadence" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->avg_cadence ) : ''; ?>"></td> <p><label for="max_cadence">Maksymalny (rpm)</label><input type="number" name="max_cadence" id="max_cadence" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_cadence ) : ''; ?>"></p>
</tr> </div>
<tr> <div class="statpress-form-group">
<th scope="row"><label for="max_cadence">Maks. rytm pedałowania</label></th> <h3><span class="dashicons dashicons-chart-area" style="vertical-align: middle;"></span> Wysokość</h3>
<td><input type="number" name="max_cadence" id="max_cadence" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_cadence ) : ''; ?>"></td> <p><label for="total_elevation_gain">Suma wzniosów (m)</label><input type="number" name="total_elevation_gain" id="total_elevation_gain" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->total_elevation_gain ) : ''; ?>"></p>
</tr> <p><label for="total_elevation_loss">Suma spadków (m)</label><input type="number" name="total_elevation_loss" id="total_elevation_loss" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->total_elevation_loss ) : ''; ?>"></p>
<tr> <p><label for="min_altitude">Min. wysokość (m n.p.m.)</label><input type="number" name="min_altitude" id="min_altitude" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->min_altitude ) : ''; ?>"></p>
<th scope="row"><label for="total_elevation_gain">Całkowity wznios (m)</label></th> <p><label for="max_altitude">Maks. wysokość (m n.p.m.)</label><input type="number" name="max_altitude" id="max_altitude" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_altitude ) : ''; ?>"></p>
<td><input type="number" name="total_elevation_gain" id="total_elevation_gain" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->total_elevation_gain ) : ''; ?>"></td> </div>
</tr> </div>
<tr> <hr style="margin-top: 2em;">
<th scope="row"><label for="total_elevation_loss">Całkowity spadek (m)</label></th> </td>
<td><input type="number" name="total_elevation_loss" id="total_elevation_loss" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->total_elevation_loss ) : ''; ?>"></td>
</tr>
<tr>
<th scope="row"><label for="min_altitude">Min. wysokość (m)</labe></th>
<td><input type="number" name="min_altitude" id="min_altitude" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->min_altitude ) : ''; ?>"></td>
</tr>
<tr>
<th scope="row"><label for="max_altitude">Maks. wysokość (m)</label></th>
<td><input type="number" name="max_altitude" id="max_altitude" class="small-text" placeholder="0" value="<?php echo $is_edit_mode ? esc_attr( $activity->max_altitude ) : ''; ?>"></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><label for="comment">Komentarz</label></th> <th scope="row"><label for="comment">Komentarz</label></th>
<td><textarea name="comment" id="comment" rows="3" class="large-text"><?php echo $is_edit_mode ? esc_textarea( $activity->comment ) : ''; ?></textarea></td> <td><textarea name="comment" id="comment" rows="3" class="large-text"><?php echo $is_edit_mode ? esc_textarea( $activity->comment ) : ''; ?></textarea></td>
</tr> </tr>
<tr class="form-field">
<td colspan="2"><hr><h4>Linki zewnętrzne (opcjonalne)</h4></td>
</tr>
<tr> <tr>
<th scope="row"><label for="strava_url">Link do Strava</label></th> <th scope="row"><label for="strava_url">Link do Strava</label></th>
<td><input type="url" name="strava_url" id="strava_url" class="large-text" placeholder="https://www.strava.com/activities/..." value="<?php echo $is_edit_mode ? esc_attr( $activity->strava_url ) : ''; ?>"></td> <td><input type="url" name="strava_url" id="strava_url" class="large-text" placeholder="https://www.strava.com/activities/..." value="<?php echo $is_edit_mode ? esc_attr( $activity->strava_url ) : ''; ?>"></td>
</tr> </tr>
<tr>
<th scope="row"><label for="gpx_url">Link do pliku GPX</label></th>
<td>
<input type="text" name="gpx_url" id="gpx_url" class="large-text" placeholder="Wklej URL lub wgraj plik..." value="<?php echo $is_edit_mode ? esc_attr( $activity->gpx_url ) : ''; ?>">
<button type="button" class="button" id="upload_gpx_button" style="margin-top: 5px;">Wgraj lub wybierz plik</button>
</td>
</tr>
</table> </table>
<p class="submit"> <p class="submit">
<input type="submit" name="statpress_submit_activity" id="submit" class="button button-primary" value="<?php echo esc_attr( $button_text ); ?>"> <input type="submit" name="statpress_submit_activity" id="submit" class="button button-primary" value="<?php echo esc_attr( $button_text ); ?>">
@@ -236,6 +226,76 @@ function statpress_render_add_form( $activity = null ) {
$('#gpx_url').val(attachment.url).trigger('change'); $('#gpx_url').val(attachment.url).trigger('change');
}).open(); }).open();
}); });
// --- GPX Auto-fill Feature ---
const gpxUrlInput = $('#gpx_url');
const statusEl = $('#gpx_parse_status');
let debounceTimer;
gpxUrlInput.on('input change', function() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(function() {
const url = gpxUrlInput.val();
if (url && url.toLowerCase().endsWith('.gpx')) {
parseGpx(url);
}
}, 500); // 500ms delay to avoid firing on every keystroke
});
function parseGpx(url) {
statusEl.html('<span class="spinner is-active" style="float:none; vertical-align: middle;"></span> Analizuję plik...');
wp.apiFetch({
path: '/statpress/v1/gpx/parse-summary',
method: 'POST',
data: { gpx_url: url }
})
.then(function(data) {
statusEl.html('<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> Dane wczytane!');
const fieldsToCheck = [
'#distance', '#duration', '#calories', '#avg_speed', '#max_speed',
'#avg_heart_rate', '#max_heart_rate', '#avg_cadence', '#max_cadence',
'#total_elevation_gain', '#total_elevation_loss', '#min_altitude', '#max_altitude'
];
let hasExistingData = false;
fieldsToCheck.forEach(function(selector) {
const field = $(selector);
if (field.val() && field.val() !== '00:00:00' && field.val() !== '0' && field.val() !== '0,00') {
hasExistingData = true;
}
});
let proceed = true;
if (hasExistingData) {
proceed = confirm('Niektóre pola formularza zawierają już dane. Czy na pewno chcesz je nadpisać danymi z pliku GPX?');
}
if (proceed) {
const formatNum = (num) => String(num).replace('.', ',');
if (data.distance) $('#distance').val(formatNum(data.distance));
if (data.duration) $('#duration').val(data.duration);
if (data.avg_speed) $('#avg_speed').val(formatNum(data.avg_speed));
if (data.max_speed) $('#max_speed').val(formatNum(data.max_speed));
if (data.avg_heart_rate) $('#avg_heart_rate').val(data.avg_heart_rate);
if (data.max_heart_rate) $('#max_heart_rate').val(data.max_heart_rate);
if (data.avg_cadence) $('#avg_cadence').val(data.avg_cadence);
if (data.max_cadence) $('#max_cadence').val(data.max_cadence);
if (data.total_elevation_gain) $('#total_elevation_gain').val(data.total_elevation_gain);
if (data.total_elevation_loss) $('#total_elevation_loss').val(data.total_elevation_loss);
if (data.min_altitude) $('#min_altitude').val(data.min_altitude);
if (data.max_altitude) $('#max_altitude').val(data.max_altitude);
}
setTimeout(() => statusEl.html(''), 4000);
})
.catch(function(error) {
const errorMsg = error.message || 'Nieznany błąd.';
statusEl.html('<span class="dashicons dashicons-warning" style="color: #d63638;"></span> Błąd: ' + errorMsg);
});
}
}); });
</script> </script>
</div> </div>
+38
View File
@@ -5,6 +5,44 @@ if ( ! defined( 'ABSPATH' ) ) {
function statpress_dashboard_page() { function statpress_dashboard_page() {
echo '<div class="wrap"><h1>StatPress Dashboard</h1>'; echo '<div class="wrap"><h1>StatPress Dashboard</h1>';
// --- MIGRATION NOTICE ---
// Show the migration button if it hasn't been completed yet.
if ( ! get_option( 'statpress_migration_complete' ) ) {
$migration_url = wp_nonce_url(
admin_url( 'admin.php?page=statpress-dashboard&action=statpress_migrate_data' ),
'statpress_migration_nonce'
);
echo '<div class="notice notice-warning is-dismissible" style="padding-bottom: 10px;">';
echo '<h4>Migracja danych StatPress</h4>';
echo '<p>Wygląda na to, że istnieją dane w starych tabelach (<code>mystat_*</code>), które można przenieść. Kliknij przycisk poniżej, aby rozpocząć proces.</p>';
echo '<p><strong>Ważne:</strong> Ta operacja jest jednorazowa i powinna być wykonana tylko raz, na pustej instalacji wtyczki StatPress.</p>';
echo '<a href="' . esc_url( $migration_url ) . '" class="button button-primary">Rozpocznij migrację danych</a>';
echo '</div>';
}
// Show the results of the migration after it's done.
$migration_results = get_transient( 'statpress_migration_results' );
if ( $migration_results ) {
echo '<div class="notice notice-success is-dismissible">';
echo '<h4>Migracja zakończona!</h4>';
echo '<ul>';
foreach ( $migration_results as $table => $result ) {
if ( 'success' === $result['status'] ) {
echo '<li>Tabela <strong>' . esc_html( $table ) . '</strong>: Przeniesiono <strong>' . esc_html( $result['count'] ) . '</strong> wierszy.</li>';
} elseif ( 'skipped' === $result['status'] ) {
echo '<li>Tabela <strong>' . esc_html( $table ) . '</strong>: Pominięto, ponieważ nowa tabela zawiera już dane (' . esc_html( $result['count'] ) . ' wierszy).</li>';
} elseif ( 'failure' === $result['status'] ) {
echo '<li>Tabela <strong>' . esc_html( $table ) . '</strong>: <strong style="color:red;">Migracja nieudana.</strong> Błąd bazy danych: <pre style="display:inline;white-space:pre-wrap;">' . esc_html( $result['error'] ) . '</pre></li>';
}
}
echo '</ul>';
echo '<p>Twoje dane powinny być teraz widoczne. Stare tabele (<code>' . esc_html( $GLOBALS['wpdb']->prefix ) . 'mystat_*</code>) wciąż istnieją w bazie danych, ale nie są już używane. Możesz je usunąć ręcznie (np. przez phpMyAdmin), jeśli wszystko działa poprawnie.</p>';
echo '</div>';
delete_transient( 'statpress_migration_results' );
}
// --- END MIGRATION NOTICE ---
statpress_render_history_table(); statpress_render_history_table();
echo '</div>'; echo '</div>';
} }
+42 -19
View File
@@ -6,39 +6,68 @@ if ( ! defined( 'ABSPATH' ) ) {
function statpress_settings_page() { function statpress_settings_page() {
?> ?>
<div class="wrap"> <div class="wrap">
<?php
if ( get_transient( 'statpress_migration_reset_notice' ) ) {
echo '<div class="notice notice-success is-dismissible"><p>Status migracji został zresetowany. Wróć do <a href="' . esc_url( admin_url( 'admin.php?page=statpress-dashboard' ) ) . '">głównego panelu</a>, aby ponownie uruchomić migrację.</p></div>';
delete_transient( 'statpress_migration_reset_notice' );
}
?>
<h1>Ustawienia Wtyczki Statystyk</h1> <h1>Ustawienia Wtyczki Statystyk</h1>
<form method="post" action="options.php"> <form method="post" action="options.php">
<?php <?php
settings_fields( 'statpress_privacy_settings' ); settings_fields( 'statpress_settings_group' );
do_settings_sections( 'statpress-privacy-section' ); do_settings_sections( 'statpress_settings_page' );
settings_fields( 'statpress_api_settings' );
do_settings_sections( 'statpress-api-section' );
submit_button(); submit_button();
?> ?>
</form> </form>
<hr>
<h2>Narzędzia deweloperskie</h2>
<div class="postbox">
<div class="postbox-header"><h3 class="hndle">Resetowanie migracji</h3></div>
<div class="inside">
<p>Jeśli migracja danych nie powiodła się, a przycisk do jej ponownego uruchomienia zniknął, możesz użyć tego narzędzia, aby zresetować status migracji. Po kliknięciu, przycisk w głównym panelu wtyczki pojawi się ponownie.</p>
<form method="post">
<input type="hidden" name="statpress_action" value="reset_migration"><?php wp_nonce_field( 'statpress_reset_migration_nonce', '_wpnonce' ); ?><?php submit_button( 'Zresetuj status migracji', 'delete' ); ?></form>
</div>
</div>
</div> </div>
<?php <?php
} }
function statpress_register_settings() { function statpress_register_settings() {
// Define a single group and page for all settings on this form.
$option_group = 'statpress_settings_group';
$page_slug = 'statpress_settings_page';
// Register Privacy settings under the main group.
register_setting( register_setting(
'statpress_privacy_settings', $option_group,
'statpress_privacy_options', 'statpress_privacy_options',
'statpress_sanitize_privacy_options' 'statpress_sanitize_privacy_options'
); );
// Register API settings under the same main group.
register_setting(
$option_group,
'statpress_api_options',
'statpress_sanitize_api_options'
);
// Add the Privacy section to the main page.
add_settings_section( add_settings_section(
'statpress_privacy_zone_section', 'statpress_privacy_zone_section',
'Strefa Prywatności GPX', 'Strefa Prywatności GPX',
'statpress_privacy_section_callback', 'statpress_privacy_section_callback',
'statpress-privacy-section' $page_slug
); );
add_settings_field( add_settings_field(
'statpress_privacy_latitude', 'statpress_privacy_latitude',
'Szerokość geograficzna (Latitude)', 'Szerokość geograficzna (Latitude)',
'statpress_render_lat_field', 'statpress_render_lat_field',
'statpress-privacy-section', $page_slug,
'statpress_privacy_zone_section' 'statpress_privacy_zone_section'
); );
@@ -46,7 +75,7 @@ function statpress_register_settings() {
'statpress_privacy_longitude', 'statpress_privacy_longitude',
'Długość geograficzna (Longitude)', 'Długość geograficzna (Longitude)',
'statpress_render_lon_field', 'statpress_render_lon_field',
'statpress-privacy-section', $page_slug,
'statpress_privacy_zone_section' 'statpress_privacy_zone_section'
); );
@@ -54,30 +83,24 @@ function statpress_register_settings() {
'statpress_privacy_radius', 'statpress_privacy_radius',
'Promień strefy (w metrach)', 'Promień strefy (w metrach)',
'statpress_render_radius_field', 'statpress_render_radius_field',
'statpress-privacy-section', $page_slug,
'statpress_privacy_zone_section' 'statpress_privacy_zone_section'
); );
// API Settings // Add the API section to the same main page.
register_setting(
'statpress_api_settings',
'statpress_api_options',
'statpress_sanitize_api_options'
);
add_settings_section( add_settings_section(
'statpress_api_section', 'statpress_api_section',
'Ustawienia API', 'Ustawienia API',
'statpress_api_section_callback', 'statpress_api_section_callback',
'statpress-api-section' $page_slug
); );
add_settings_field( add_settings_field(
'statpress_enable_api', 'statpress_enable_api',
'REST API', 'REST API',
'statpress_render_enable_api_field', 'statpress_render_enable_api_field',
'statpress-privacy-section', $page_slug,
'statpress_privacy_zone_section' 'statpress_api_section'
); );
} }
+64 -3
View File
@@ -63,6 +63,23 @@ function statpress_register_rest_routes() {
), ),
) )
); );
// Route for parsing GPX file summary
register_rest_route(
$namespace,
'/gpx/parse-summary',
array(
'methods' => WP_REST_Server::CREATABLE, // Use POST to send URL in the body
'callback' => 'statpress_parse_gpx_summary_api',
'permission_callback' => 'statpress_api_permissions_check',
'args' => array(
'gpx_url' => array(
'required' => true,
'validate_callback' => 'esc_url_raw',
),
),
)
);
} }
/** /**
@@ -88,6 +105,10 @@ function statpress_get_activities_api( WP_REST_Request $request ) {
$page = $request->get_param( 'page' ) ? (int) $request->get_param( 'page' ) : 1; $page = $request->get_param( 'page' ) ? (int) $request->get_param( 'page' ) : 1;
$offset = ( $page - 1 ) * $per_page; $offset = ( $page - 1 ) * $per_page;
// Get total items for pagination headers
$total_items = (int) $wpdb->get_var( "SELECT COUNT(id) FROM $table_activities" );
$total_pages = ceil( $total_items / $per_page );
$sql = $wpdb->prepare( $sql = $wpdb->prepare(
"SELECT a.*, c.name as category_name, et.name as event_type_name, eq.name as equipment_name "SELECT a.*, c.name as category_name, et.name as event_type_name, eq.name as equipment_name
FROM $table_activities a FROM $table_activities a
@@ -102,7 +123,11 @@ function statpress_get_activities_api( WP_REST_Request $request ) {
$results = $wpdb->get_results( $sql ); $results = $wpdb->get_results( $sql );
return new WP_REST_Response( $results, 200 ); $response = new WP_REST_Response( $results, 200 );
$response->header( 'X-WP-Total', $total_items );
$response->header( 'X-WP-TotalPages', $total_pages );
return $response;
} }
/** /**
@@ -115,7 +140,16 @@ function statpress_get_activity_api( WP_REST_Request $request ) {
global $wpdb; global $wpdb;
$id = (int) $request['id']; $id = (int) $request['id'];
$sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}statpress_activities WHERE id = %d", $id ); // Use the same rich query as the collection endpoint for consistency.
$sql = $wpdb->prepare(
"SELECT a.*, c.name as category_name, et.name as event_type_name, eq.name as equipment_name
FROM {$wpdb->prefix}statpress_activities a
LEFT JOIN {$wpdb->prefix}statpress_categories c ON a.category_id = c.id
LEFT JOIN {$wpdb->prefix}statpress_event_types et ON a.event_type_id = et.id
LEFT JOIN {$wpdb->prefix}statpress_equipment eq ON a.equipment_id = eq.id
WHERE a.id = %d",
$id
);
$activity = $wpdb->get_row( $sql ); $activity = $wpdb->get_row( $sql );
if ( ! $activity ) { if ( ! $activity ) {
@@ -139,7 +173,10 @@ function statpress_create_activity_api( WP_REST_Request $request ) {
return new WP_Error( 'cant-create', 'Error creating activity', array( 'status' => 500 ) ); return new WP_Error( 'cant-create', 'Error creating activity', array( 'status' => 500 ) );
} }
$response = statpress_get_activity_api( new WP_REST_Request( 'GET', "/statpress/v1/activities/{$activity_id}" ) ); // Create a new request object to fetch the newly created activity.
$new_request = new WP_REST_Request();
$new_request->set_param( 'id', $activity_id );
$response = statpress_get_activity_api( $new_request );
$response->set_status( 201 ); // 201 Created $response->set_status( 201 ); // 201 Created
return $response; return $response;
} }
@@ -180,3 +217,27 @@ function statpress_delete_activity_api( WP_REST_Request $request ) {
return new WP_REST_Response( array( 'message' => 'Activity deleted successfully.' ), 200 ); return new WP_REST_Response( array( 'message' => 'Activity deleted successfully.' ), 200 );
} }
/**
* API callback to parse a GPX file and return its summary data.
*
* @param WP_REST_Request $request The request object.
* @return WP_REST_Response|WP_Error A response object with summary data or an error.
*/
function statpress_parse_gpx_summary_api( WP_REST_Request $request ) {
$params = $request->get_json_params();
$gpx_url = $params['gpx_url'] ?? '';
if ( empty( $gpx_url ) ) {
return new WP_Error( 'no_url_provided', 'Nie podano adresu URL do pliku GPX.', array( 'status' => 400 ) );
}
// This new function will do the heavy lifting of calculation.
$summary = statpress_calculate_gpx_summary( $gpx_url );
if ( empty( $summary ) || ! $summary['distance'] > 0 ) {
return new WP_Error( 'gpx_parse_error', 'Nie udało się przetworzyć pliku GPX. Sprawdź, czy plik jest poprawny i zawiera dane trasy.', array( 'status' => 400 ) );
}
return new WP_REST_Response( $summary, 200 );
}
+158
View File
@@ -137,3 +137,161 @@ function statpress_parse_gpx_data( $gpx_url ) {
'profiles' => $profiles, 'profiles' => $profiles,
); );
} }
/**
* Fetches and calculates a full summary from a GPX file.
* This function parses the entire file to get accurate summary stats, ignoring privacy zones.
*
* @param string $gpx_url The URL of the GPX file.
* @return array An associative array with summary statistics.
*/
function statpress_calculate_gpx_summary( $gpx_url ) {
if ( empty( $gpx_url ) ) {
return array();
}
$response = wp_remote_get( $gpx_url, array( 'timeout' => 20 ) );
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
return array();
}
$gpx_content = wp_remote_retrieve_body( $response );
if ( empty( $gpx_content ) ) {
return array();
}
libxml_use_internal_errors( true );
$gpx = simplexml_load_string( $gpx_content );
libxml_clear_errors();
if ( false === $gpx ) {
return array();
}
$gpx->registerXPathNamespace( 'gpx', 'http://www.topografix.com/GPX/1/1' );
$trackpoints = $gpx->xpath( '//gpx:trkpt' );
if ( empty( $trackpoints ) ) {
$trackpoints = $gpx->xpath( '//trkpt' );
}
if ( empty( $trackpoints ) ) {
return array();
}
$haversine = function( $lat1, $lon1, $lat2, $lon2 ) {
$earth_radius = 6371; // km
$dLat = deg2rad( $lat2 - $lat1 );
$dLon = deg2rad( $lon2 - $lon1 );
$a = sin( $dLat / 2 ) * sin( $dLat / 2 ) + cos( deg2rad( $lat1 ) ) * cos( deg2rad( $lat2 ) ) * sin( $dLon / 2 ) * sin( $dLon / 2 );
$c = 2 * atan2( sqrt( $a ), sqrt( 1 - $a ) );
return $earth_radius * $c;
};
$stats = array(
'distance' => 0,
'total_elevation_gain' => 0,
'total_elevation_loss' => 0,
'min_altitude' => null,
'max_altitude' => null,
'max_speed' => 0,
'max_heart_rate' => 0,
'max_cadence' => 0,
'hr_sum' => 0,
'hr_count' => 0,
'cad_sum' => 0,
'cad_count' => 0,
'speed_sum' => 0,
'speed_count' => 0,
'start_time' => null,
'end_time' => null,
);
$prev_point = null;
foreach ( $trackpoints as $trkpt ) {
$point = array(
'lat' => (float) $trkpt['lat'],
'lon' => (float) $trkpt['lon'],
'ele' => isset( $trkpt->ele ) ? (float) $trkpt->ele : null,
'time' => isset( $trkpt->time ) ? strtotime( (string) $trkpt->time ) : null,
);
if ( is_null( $stats['start_time'] ) && ! is_null( $point['time'] ) ) {
$stats['start_time'] = $point['time'];
}
$stats['end_time'] = $point['time'];
if ( ! is_null( $point['ele'] ) ) {
if ( is_null( $stats['min_altitude'] ) || $point['ele'] < $stats['min_altitude'] ) {
$stats['min_altitude'] = $point['ele'];
}
if ( is_null( $stats['max_altitude'] ) || $point['ele'] > $stats['max_altitude'] ) {
$stats['max_altitude'] = $point['ele'];
}
}
$extensions = $trkpt->extensions ? $trkpt->extensions->children( 'gpxtpx', true ) : null;
if ( $extensions ) {
if ( isset( $extensions->TrackPointExtension->hr ) ) {
$hr = (int) $extensions->TrackPointExtension->hr;
$stats['hr_sum'] += $hr;
$stats['hr_count']++;
if ( $hr > $stats['max_heart_rate'] ) {
$stats['max_heart_rate'] = $hr;
}
}
if ( isset( $extensions->TrackPointExtension->cad ) ) {
$cad = (int) $extensions->TrackPointExtension->cad;
$stats['cad_sum'] += $cad;
$stats['cad_count']++;
if ( $cad > $stats['max_cadence'] ) {
$stats['max_cadence'] = $cad;
}
}
}
if ( $prev_point ) {
$distance_delta = $haversine( $prev_point['lat'], $prev_point['lon'], $point['lat'], $point['lon'] );
$stats['distance'] += $distance_delta;
if ( ! is_null( $point['ele'] ) && ! is_null( $prev_point['ele'] ) ) {
$ele_delta = $point['ele'] - $prev_point['ele'];
if ( $ele_delta > 0 ) {
$stats['total_elevation_gain'] += $ele_delta;
} else {
$stats['total_elevation_loss'] += abs( $ele_delta );
}
}
if ( ! is_null( $point['time'] ) && ! is_null( $prev_point['time'] ) ) {
$time_delta = $point['time'] - $prev_point['time'];
if ( $time_delta > 0 ) {
$speed = ( $distance_delta * 3600 ) / $time_delta; // km/h
$stats['speed_sum'] += $speed;
$stats['speed_count']++;
if ( $speed > $stats['max_speed'] ) {
$stats['max_speed'] = $speed;
}
}
}
}
$prev_point = $point;
}
$duration_sec = ( ! is_null( $stats['end_time'] ) && ! is_null( $stats['start_time'] ) ) ? ( $stats['end_time'] - $stats['start_time'] ) : 0;
return array(
'distance' => round( $stats['distance'], 2 ),
'duration' => gmdate( 'H:i:s', $duration_sec ),
'avg_speed' => ( $stats['distance'] > 0 && $duration_sec > 0 ) ? round( ( $stats['distance'] / ( $duration_sec / 3600 ) ), 1 ) : 0,
'max_speed' => round( $stats['max_speed'], 1 ),
'total_elevation_gain' => round( $stats['total_elevation_gain'] ),
'total_elevation_loss' => round( $stats['total_elevation_loss'] ),
'min_altitude' => ! is_null( $stats['min_altitude'] ) ? round( $stats['min_altitude'] ) : null,
'max_altitude' => ! is_null( $stats['max_altitude'] ) ? round( $stats['max_altitude'] ) : null,
'avg_heart_rate' => $stats['hr_count'] > 0 ? round( $stats['hr_sum'] / $stats['hr_count'] ) : 0,
'max_heart_rate' => $stats['max_heart_rate'],
'avg_cadence' => $stats['cad_count'] > 0 ? round( $stats['cad_sum'] / $stats['cad_count'] ) : 0,
'max_cadence' => $stats['max_cadence'],
);
}
+3 -3
View File
@@ -6,12 +6,12 @@ if ( ! defined( 'ABSPATH' ) ) {
/** /**
* Registers styles for frontend and enqueues them when shortcodes are used. * Registers styles for frontend and enqueues them when shortcodes are used.
*/ */
function mystat_enqueue_frontend_assets() { function statpress_enqueue_frontend_assets() {
// Register the stylesheet. It will be enqueued by the shortcodes when needed. // Register the stylesheet. It will be enqueued by the shortcodes when needed.
$plugin_version = '1.0'; $plugin_version = '1.0';
wp_register_style( wp_register_style(
'mystat-frontend-styles', 'statpress-frontend-styles',
MYSTAT_PLUGIN_URL . 'assets/css/frontend.css', STATPRESS_PLUGIN_URL . 'assets/css/frontend.css',
array(), array(),
$plugin_version $plugin_version
); );
+45 -45
View File
@@ -4,11 +4,11 @@ if ( ! defined( 'ABSPATH' ) ) {
} }
/** /**
* Rejestruje shortcode [moje_statystyki]. * Rejestruje shortcode [statpress_summary] and [statpress_activity].
*/ */
function mystat_register_shortcode() { function statpress_register_shortcode() {
add_shortcode( 'moje_statystyki', 'mystat_shortcode_handler' ); add_shortcode( 'statpress_summary', 'statpress_shortcode_handler' );
add_shortcode( 'moje_statystyki_wpis', 'mystat_single_activity_shortcode_handler' ); add_shortcode( 'statpress_activity', 'statpress_single_activity_shortcode_handler' );
} }
/** /**
@@ -16,8 +16,8 @@ function mystat_register_shortcode() {
* @param array $atts Atrybuty shortcode'u (np. year, month). * @param array $atts Atrybuty shortcode'u (np. year, month).
* @return string HTML do wyświetlenia. * @return string HTML do wyświetlenia.
*/ */
function mystat_shortcode_handler( $atts ) { function statpress_shortcode_handler( $atts ) {
wp_enqueue_style( 'mystat-frontend-styles' ); wp_enqueue_style( 'statpress-frontend-styles' );
global $wpdb; global $wpdb;
@@ -28,20 +28,20 @@ function mystat_shortcode_handler( $atts ) {
'month' => current_time( 'n' ), 'month' => current_time( 'n' ),
), ),
$atts, $atts,
'moje_statystyki' 'statpress_summary'
); );
$year = intval( $atts['year'] ); $year = intval( $atts['year'] );
$month = intval( $atts['month'] ); $month = intval( $atts['month'] );
// Pobieranie danych z bazy // Pobieranie danych z bazy
$table_activities = $wpdb->prefix . 'mystat_activities'; $table_activities = $wpdb->prefix . 'statpress_activities';
$sql = $wpdb->prepare( $sql = $wpdb->prepare(
" "
SELECT a.*, c.name as category_name, eq.name as equipment_name SELECT a.*, c.name as category_name, eq.name as equipment_name
FROM $table_activities a FROM $table_activities a
LEFT JOIN {$wpdb->prefix}mystat_categories c ON a.category_id = c.id LEFT JOIN {$wpdb->prefix}statpress_categories c ON a.category_id = c.id
LEFT JOIN {$wpdb->prefix}mystat_equipment eq ON a.equipment_id = eq.id LEFT JOIN {$wpdb->prefix}statpress_equipment eq ON a.equipment_id = eq.id
WHERE YEAR(a.date) = %d AND MONTH(a.date) = %d WHERE YEAR(a.date) = %d AND MONTH(a.date) = %d
ORDER BY a.date ASC ORDER BY a.date ASC
", ",
@@ -68,10 +68,10 @@ function mystat_shortcode_handler( $atts ) {
// Rozpoczęcie buforowania wyjścia // Rozpoczęcie buforowania wyjścia
ob_start(); ob_start();
?> ?>
<div class="mystats-shortcode-container"> <div class="statpress-shortcode-container">
<h3>Podsumowanie miesiąca</h3> <h3>Podsumowanie miesiąca</h3>
<table class="mystats-summary-table"> <table class="statpress-summary-table">
<thead> <thead>
<tr> <tr>
<th>Całkowity dystans</th> <th>Całkowity dystans</th>
@@ -87,7 +87,7 @@ function mystat_shortcode_handler( $atts ) {
</table> </table>
<h3>Lista aktywności</h3> <h3>Lista aktywności</h3>
<table class="mystats-activity-table"> <table class="statpress-activity-table">
<thead> <thead>
<tr> <tr>
<th>Data</th> <th>Data</th>
@@ -111,9 +111,9 @@ function mystat_shortcode_handler( $atts ) {
<td><?php echo esc_html( $row->equipment_name ); ?></td> <td><?php echo esc_html( $row->equipment_name ); ?></td>
</tr> </tr>
<?php if ( $row->avg_speed || $row->avg_heart_rate || $row->avg_cadence ) : ?> <?php if ( $row->avg_speed || $row->avg_heart_rate || $row->avg_cadence ) : ?>
<tr class="mystats-activity-details-row"> <tr class="statpress-activity-details-row">
<td colspan="6" class="mystats-activity-details-cell"> <td colspan="6" class="statpress-activity-details-cell">
<table class="mystats-nested-details-table"> <table class="statpress-nested-details-table">
<thead> <thead>
<tr> <tr>
<?php if ( $row->avg_speed ) { echo '<th>Śr. prędkość</th>'; } ?> <?php if ( $row->avg_speed ) { echo '<th>Śr. prędkość</th>'; } ?>
@@ -146,8 +146,8 @@ function mystat_shortcode_handler( $atts ) {
return ob_get_clean(); return ob_get_clean();
} }
function mystat_single_activity_shortcode_handler( $atts ) { function statpress_single_activity_shortcode_handler( $atts ) {
wp_enqueue_style( 'mystat-frontend-styles' ); wp_enqueue_style( 'statpress-frontend-styles' );
global $wpdb; global $wpdb;
@@ -156,7 +156,7 @@ function mystat_single_activity_shortcode_handler( $atts ) {
'id' => 0, 'id' => 0,
), ),
$atts, $atts,
'moje_statystyki_wpis' 'statpress_activity'
); );
$activity_id = intval( $atts['id'] ); $activity_id = intval( $atts['id'] );
@@ -166,14 +166,14 @@ function mystat_single_activity_shortcode_handler( $atts ) {
} }
// Pobieranie danych z bazy // Pobieranie danych z bazy
$table_activities = $wpdb->prefix . 'mystat_activities'; $table_activities = $wpdb->prefix . 'statpress_activities';
$sql = $wpdb->prepare( $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 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 FROM $table_activities a
LEFT JOIN {$wpdb->prefix}mystat_categories c ON a.category_id = c.id LEFT JOIN {$wpdb->prefix}statpress_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}statpress_event_types et ON a.event_type_id = et.id
LEFT JOIN {$wpdb->prefix}mystat_equipment eq ON a.equipment_id = eq.id LEFT JOIN {$wpdb->prefix}statpress_equipment eq ON a.equipment_id = eq.id
WHERE a.id = %d WHERE a.id = %d
", ",
$activity_id $activity_id
@@ -201,11 +201,11 @@ function mystat_single_activity_shortcode_handler( $atts ) {
$gpx_data = array(); $gpx_data = array();
$has_gpx_data = false; $has_gpx_data = false;
if ( ! empty( $activity->gpx_url ) ) { if ( ! empty( $activity->gpx_url ) ) {
$gpx_data = mystat_parse_gpx_data( $activity->gpx_url ); $gpx_data = statpress_parse_gpx_data( $activity->gpx_url );
$has_gpx_data = ! empty( $gpx_data['points'] ); $has_gpx_data = ! empty( $gpx_data['points'] );
} }
$unique_id = 'mystat-activity-' . esc_attr( $activity->id ); $unique_id = 'statpress-activity-' . esc_attr( $activity->id );
if ( $has_gpx_data ) { if ( $has_gpx_data ) {
wp_enqueue_style( 'leaflet-css', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' ); wp_enqueue_style( 'leaflet-css', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' );
@@ -227,8 +227,8 @@ function mystat_single_activity_shortcode_handler( $atts ) {
$has_time_data = ! empty( array_filter( $gpx_data['profiles']['time'], fn( $t ) => ! is_null( $t ) ) ); $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_register_script( 'statpress-shortcode-loader-' . $activity->id, false );
wp_enqueue_script( 'mystat-shortcode-loader-' . $activity->id ); wp_enqueue_script( 'statpress-shortcode-loader-' . $activity->id );
$js_script = ' $js_script = '
(function() { (function() {
@@ -268,7 +268,7 @@ function mystat_single_activity_shortcode_handler( $atts ) {
function renderChart() { function renderChart() {
if (activeChart) activeChart.destroy(); if (activeChart) activeChart.destroy();
const activeTab = containerEl.querySelector(".mystat-chart-tab.active"); const activeTab = containerEl.querySelector(".statpress-chart-tab.active");
if (!activeTab) return; if (!activeTab) return;
const chartType = activeTab.dataset.type; const chartType = activeTab.dataset.type;
const xAxisRadio = containerEl.querySelector(\'input[name="xaxis-\' + uniqueId + \'"]:checked\'); const xAxisRadio = containerEl.querySelector(\'input[name="xaxis-\' + uniqueId + \'"]:checked\');
@@ -309,27 +309,27 @@ function mystat_single_activity_shortcode_handler( $atts ) {
}); });
} }
containerEl.querySelectorAll(".mystat-chart-tab").forEach(t => t.addEventListener("click", e => { containerEl.querySelectorAll(".statpress-chart-tab").forEach(t => t.addEventListener("click", e => {
e.preventDefault(); e.preventDefault();
containerEl.querySelector(".mystat-chart-tab.active").classList.remove("active"); containerEl.querySelector(".statpress-chart-tab.active").classList.remove("active");
e.currentTarget.classList.add("active"); e.currentTarget.classList.add("active");
renderChart(); renderChart();
})); }));
containerEl.querySelectorAll(\'input[name="xaxis-\' + uniqueId + \'"]\').forEach(r => r.addEventListener("change", renderChart)); containerEl.querySelectorAll(\'input[name="xaxis-\' + uniqueId + \'"]\').forEach(r => r.addEventListener("change", renderChart));
if (containerEl.querySelector(".mystat-chart-tab")) renderChart(); if (containerEl.querySelector(".statpress-chart-tab")) renderChart();
}); });
})();'; })();';
wp_add_inline_script( 'mystat-shortcode-loader-' . $activity->id, $js_script ); wp_add_inline_script( 'statpress-shortcode-loader-' . $activity->id, $js_script );
} }
?> ?>
<div class="mystat-single-activity-shortcode" id="<?php echo esc_attr( $unique_id ); ?>"> <div class="statpress-single-activity-shortcode" id="<?php echo esc_attr( $unique_id ); ?>">
<h4><?php echo esc_html( $activity->title ); ?></h4> <h4><?php echo esc_html( $activity->title ); ?></h4>
<p><em><?php echo esc_html( date_i18n( 'j F Y', strtotime( $activity->date ) ) ); ?></em></p> <p><em><?php echo esc_html( date_i18n( 'j F Y', strtotime( $activity->date ) ) ); ?></em></p>
<div class="mystat-single-columns-container"> <div class="statpress-single-columns-container">
<div class="mystat-single-col"> <div class="statpress-single-col">
<table class="mystat-single-summary-table"> <table class="statpress-single-summary-table">
<tbody> <tbody>
<?php $render_row( 'Dystans', number_format( $activity->distance, 2, ',', ' ' ), 'km' ); ?> <?php $render_row( 'Dystans', number_format( $activity->distance, 2, ',', ' ' ), 'km' ); ?>
<?php $render_row( 'Czas trwania', $activity->duration ); ?> <?php $render_row( 'Czas trwania', $activity->duration ); ?>
@@ -338,8 +338,8 @@ function mystat_single_activity_shortcode_handler( $atts ) {
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="mystat-single-col"> <div class="statpress-single-col">
<table class="mystat-single-summary-table"> <table class="statpress-single-summary-table">
<tbody> <tbody>
<?php $render_row( 'Kategoria', $activity->category_name ); ?> <?php $render_row( 'Kategoria', $activity->category_name ); ?>
<?php $render_row( 'Sprzęt', $activity->equipment_name ); ?> <?php $render_row( 'Sprzęt', $activity->equipment_name ); ?>
@@ -352,17 +352,17 @@ function mystat_single_activity_shortcode_handler( $atts ) {
</div> </div>
<?php if ( $has_gpx_data ) : ?> <?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> <div id="<?php echo esc_attr( $map_id ); ?>" class="statpress-single-map" style="height: 350px; width: 100%; margin-top: 15px; border-radius: 5px; margin-bottom: 20px;"></div>
<?php if ( ! empty( $available_profiles ) ) : ?> <?php if ( ! empty( $available_profiles ) ) : ?>
<div class="mystat-charts-container"> <div class="statpress-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="statpress-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;"> <div class="statpress-chart-tabs" style="display: flex; flex-wrap: wrap; gap: 5px;">
<?php <?php
$is_first = true; $is_first = true;
foreach ( $available_profiles as $key => $label ) : foreach ( $available_profiles as $key => $label ) :
?> ?>
<button data-type="<?php echo esc_attr( $key ); ?>" class="mystat-chart-tab <?php <button data-type="<?php echo esc_attr( $key ); ?>" class="statpress-chart-tab <?php
if ( $is_first ) { if ( $is_first ) {
echo 'active'; echo 'active';
$is_first = false; } $is_first = false; }
@@ -371,7 +371,7 @@ function mystat_single_activity_shortcode_handler( $atts ) {
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php if ( $has_time_data ) : ?> <?php if ( $has_time_data ) : ?>
<div class="mystat-xaxis-switcher" style="padding-top: 5px; font-size: 0.9em; white-space: nowrap;"> <div class="statpress-xaxis-switcher" style="padding-top: 5px; font-size: 0.9em; white-space: nowrap;">
<strong>Oś X:</strong>&nbsp; <strong>Oś X:</strong>&nbsp;
<label><input type="radio" name="xaxis-<?php echo esc_attr( $unique_id ); ?>" value="distance" checked> Dystans</label> <label><input type="radio" name="xaxis-<?php echo esc_attr( $unique_id ); ?>" value="distance" checked> Dystans</label>
&nbsp; &nbsp;
@@ -381,7 +381,7 @@ function mystat_single_activity_shortcode_handler( $atts ) {
<input type="hidden" name="xaxis-<?php echo esc_attr( $unique_id ); ?>" value="distance" checked> <input type="hidden" name="xaxis-<?php echo esc_attr( $unique_id ); ?>" value="distance" checked>
<?php endif; ?> <?php endif; ?>
</div> </div>
<div class="mystat-chart-wrapper" style="position: relative; height:250px; width:100%;"> <div class="statpress-chart-wrapper" style="position: relative; height:250px; width:100%;">
<canvas id="<?php echo esc_attr( $chart_id ); ?>"></canvas> <canvas id="<?php echo esc_attr( $chart_id ); ?>"></canvas>
</div> </div>
</div> </div>
+112
View File
@@ -36,6 +36,9 @@ require_once STATPRESS_PLUGIN_DIR . 'includes/admin/pages/page-yearly-summary.ph
require_once STATPRESS_PLUGIN_DIR . 'includes/admin/pages/page-infographic.php'; require_once STATPRESS_PLUGIN_DIR . 'includes/admin/pages/page-infographic.php';
require_once STATPRESS_PLUGIN_DIR . 'includes/admin/pages/page-import-csv.php'; require_once STATPRESS_PLUGIN_DIR . 'includes/admin/pages/page-import-csv.php';
global $statpress_plugin_hooks;
$statpress_plugin_hooks = array();
add_action( 'admin_menu', 'statpress_add_admin_menu' ); add_action( 'admin_menu', 'statpress_add_admin_menu' );
add_action( 'admin_init', 'statpress_admin_init_setup' ); add_action( 'admin_init', 'statpress_admin_init_setup' );
add_action( 'admin_enqueue_scripts', 'statpress_enqueue_admin_styles' ); add_action( 'admin_enqueue_scripts', 'statpress_enqueue_admin_styles' );
@@ -53,3 +56,112 @@ require_once STATPRESS_PLUGIN_DIR . 'includes/frontend/shortcodes.php';
add_action( 'wp_enqueue_scripts', 'statpress_enqueue_frontend_assets' ); add_action( 'wp_enqueue_scripts', 'statpress_enqueue_frontend_assets' );
add_action( 'init', 'statpress_register_shortcode' ); add_action( 'init', 'statpress_register_shortcode' );
// --- 5. MIGRACJA DANYCH (jednorazowa) ---
add_action( 'admin_init', 'statpress_handle_data_migration' );
add_action( 'admin_init', 'statpress_handle_admin_tools' );
/**
* Handles the one-time data migration from 'mystat_' tables to 'statpress_' tables.
*/
function statpress_handle_data_migration() {
// Check if the migration action is triggered, nonce is valid, and user has permissions.
if ( ! isset( $_GET['action'] ) || 'statpress_migrate_data' !== $_GET['action'] ) {
return;
}
if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'statpress_migration_nonce' ) ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
global $wpdb;
// Define table columns to ensure robust migration, even if schemas differ.
$table_columns = array(
'activities' => 'id, category_id, date, title, distance, duration, calories, comment, strava_url, avg_heart_rate, max_heart_rate, avg_speed, max_speed, avg_cadence, max_cadence, total_elevation_gain, total_elevation_loss, min_altitude, max_altitude, equipment_id, gpx_url, event_type_id',
'categories' => 'id, name, icon, color',
'equipment' => 'id, name, type, purchase_date, initial_cost, status, notes',
'equipment_log' => 'id, equipment_id, log_date, log_type, description, cost, mileage',
'event_types' => 'id, name',
'goals' => 'id, name, goal_type, target_value, year, month, category_id',
);
$results = array();
$all_successful = true;
foreach ( $table_columns as $table_suffix => $columns ) {
$old_table = $wpdb->prefix . 'mystat_' . $table_suffix;
$new_table = $wpdb->prefix . 'statpress_' . $table_suffix;
// Check if old table exists and new table is empty
if ( $wpdb->get_var( "SHOW TABLES LIKE '{$old_table}'" ) === $old_table ) {
// Check if there's anything to migrate
$old_table_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$old_table}" );
$new_table_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$new_table}" );
// Only migrate if the new table is empty but the old one has data.
if ( $old_table_count > 0 && 0 === $new_table_count ) {
// Use explicit column list for a robust query
$query = "INSERT INTO {$new_table} ({$columns}) SELECT {$columns} FROM {$old_table}";
$copied_rows = $wpdb->query( $query );
if ( false === $copied_rows ) {
$all_successful = false;
$results[ $table_suffix ] = array(
'status' => 'failure',
'error' => $wpdb->last_error,
);
} else {
$results[ $table_suffix ] = array(
'status' => 'success',
'count' => $copied_rows,
);
}
} else {
// If the new table is not empty, skip it.
$results[ $table_suffix ] = array(
'status' => 'skipped',
'count' => $new_table_count,
);
}
}
}
// Store the results in a transient to display a notice
set_transient( 'statpress_migration_results', $results, 60 );
// Mark migration as complete only if everything was successful or skipped.
if ( $all_successful ) {
update_option( 'statpress_migration_complete', true );
}
// Redirect to the main dashboard to show the notice and remove query args
wp_safe_redirect( admin_url( 'admin.php?page=statpress-dashboard' ) );
exit;
}
/**
* Handles various admin tool actions, like resetting the migration flag.
*/
function statpress_handle_admin_tools() {
// Check if the reset migration action is triggered
if ( isset( $_POST['statpress_action'] ) && 'reset_migration' === $_POST['statpress_action'] ) {
// Verify nonce and permissions
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'statpress_reset_migration_nonce' ) ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Delete the option that hides the migration button
delete_option( 'statpress_migration_complete' );
// Set a transient to show a success notice on the settings page
set_transient( 'statpress_migration_reset_notice', true, 60 );
wp_safe_redirect( admin_url( 'admin.php?page=statpress-settings' ) );
exit;
}
}