diff --git a/includes/admin/pages/page-activity-form.php b/includes/admin/pages/page-activity-form.php
index 006464b..df9c971 100644
--- a/includes/admin/pages/page-activity-form.php
+++ b/includes/admin/pages/page-activity-form.php
@@ -57,76 +57,17 @@ function mystat_handle_activity_form_submission() {
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
- );
+ // Use the refactored function to save data.
+ // We can pass $_POST directly as the function will sanitize it.
+ $result = mystat_save_activity_data( $_POST, $activity_id );
if ( $activity_id > 0 ) {
- // UPDATE
- $result = $wpdb->update( $table_activities, $data, array( 'id' => $activity_id ), $format, array( '%d' ) );
$message = 'Trening zaktualizowany pomyślnie!';
} else {
- // INSERT
- $result = $wpdb->insert( $table_activities, $data, $format );
$message = 'Trening dodany pomyślnie!';
}
- if ( false !== $result ) {
+ if ( $result ) {
echo '
' . esc_html( $message ) . '
';
} else {
echo 'Wystąpił błąd podczas zapisu do bazy.
';
diff --git a/includes/admin/pages/routes.php b/includes/admin/pages/routes.php
new file mode 100644
index 0000000..b752225
--- /dev/null
+++ b/includes/admin/pages/routes.php
@@ -0,0 +1,182 @@
+ WP_REST_Server::READABLE,
+ 'callback' => 'mystat_get_activities_api',
+ 'permission_callback' => 'mystat_api_permissions_check',
+ 'args' => array(
+ 'page' => array(
+ 'validate_callback' => 'is_numeric',
+ ),
+ 'per_page' => array(
+ 'validate_callback' => 'is_numeric',
+ ),
+ ),
+ ),
+ array(
+ 'methods' => WP_REST_Server::CREATABLE,
+ 'callback' => 'mystat_create_activity_api',
+ 'permission_callback' => 'mystat_api_permissions_check',
+ ),
+ )
+ );
+
+ // Route for a single activity
+ register_rest_route(
+ $namespace,
+ '/activities/(?P[\d]+)',
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => 'mystat_get_activity_api',
+ 'permission_callback' => 'mystat_api_permissions_check',
+ ),
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => 'mystat_update_activity_api',
+ 'permission_callback' => 'mystat_api_permissions_check',
+ ),
+ array(
+ 'methods' => WP_REST_Server::DELETABLE,
+ 'callback' => 'mystat_delete_activity_api',
+ 'permission_callback' => 'mystat_api_permissions_check',
+ ),
+ )
+ );
+}
+
+/**
+ * Permission check for API endpoints.
+ *
+ * @return bool
+ */
+function mystat_api_permissions_check() {
+ return current_user_can( 'manage_options' );
+}
+
+/**
+ * Get a collection of activities.
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error
+ */
+function mystat_get_activities_api( WP_REST_Request $request ) {
+ global $wpdb;
+ $table_activities = $wpdb->prefix . 'mystat_activities';
+
+ $per_page = $request->get_param( 'per_page' ) ? (int) $request->get_param( 'per_page' ) : 20;
+ $page = $request->get_param( 'page' ) ? (int) $request->get_param( 'page' ) : 1;
+ $offset = ( $page - 1 ) * $per_page;
+
+ $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
+ ORDER BY a.date DESC, a.id DESC
+ LIMIT %d OFFSET %d",
+ $per_page,
+ $offset
+ );
+
+ $results = $wpdb->get_results( $sql );
+
+ return new WP_REST_Response( $results, 200 );
+}
+
+/**
+ * Get a single activity.
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error
+ */
+function mystat_get_activity_api( WP_REST_Request $request ) {
+ global $wpdb;
+ $id = (int) $request['id'];
+
+ $sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}mystat_activities WHERE id = %d", $id );
+ $activity = $wpdb->get_row( $sql );
+
+ if ( ! $activity ) {
+ return new WP_Error( 'not_found', 'Activity not found', array( 'status' => 404 ) );
+ }
+
+ return new WP_REST_Response( $activity, 200 );
+}
+
+/**
+ * Create a new activity.
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error
+ */
+function mystat_create_activity_api( WP_REST_Request $request ) {
+ $params = $request->get_json_params();
+ $activity_id = mystat_save_activity_data( $params );
+
+ if ( ! $activity_id ) {
+ return new WP_Error( 'cant-create', 'Error creating activity', array( 'status' => 500 ) );
+ }
+
+ $response = mystat_get_activity_api( new WP_REST_Request( 'GET', "/mystat/v1/activities/{$activity_id}" ) );
+ $response->set_status( 201 ); // 201 Created
+ return $response;
+}
+
+/**
+ * Update an existing activity.
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error
+ */
+function mystat_update_activity_api( WP_REST_Request $request ) {
+ $id = (int) $request['id'];
+ $params = $request->get_json_params();
+ $activity_id = mystat_save_activity_data( $params, $id );
+
+ if ( ! $activity_id ) {
+ return new WP_Error( 'cant-update', 'Error updating activity', array( 'status' => 500 ) );
+ }
+
+ return mystat_get_activity_api( $request );
+}
+
+/**
+ * Delete an activity.
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error
+ */
+function mystat_delete_activity_api( WP_REST_Request $request ) {
+ global $wpdb;
+ $id = (int) $request['id'];
+
+ $result = $wpdb->delete( $wpdb->prefix . 'mystat_activities', array( 'id' => $id ), array( '%d' ) );
+
+ if ( ! $result ) {
+ return new WP_Error( 'cant-delete', 'Error deleting activity', array( 'status' => 500 ) );
+ }
+
+ return new WP_REST_Response( array( 'message' => 'Activity deleted successfully.' ), 200 );
+}
\ No newline at end of file
diff --git a/includes/core/crud-activity.php b/includes/core/crud-activity.php
new file mode 100644
index 0000000..2f25ef2
--- /dev/null
+++ b/includes/core/crud-activity.php
@@ -0,0 +1,95 @@
+prefix . 'mystat_activities';
+
+ // Helper to convert empty strings to NULL for the database.
+ $null_if_empty = function( $value ) {
+ // Also check for 0 as we don't want to nullify it for numeric fields.
+ return ( '' !== $value && ! is_null( $value ) ) ? $value : null;
+ };
+
+ // Sanitize and prepare data.
+ $prepared_data = array(
+ 'category_id' => isset( $data['category_id'] ) ? intval( $data['category_id'] ) : null,
+ 'date' => isset( $data['date'] ) ? sanitize_text_field( $data['date'] ) : current_time( 'Y-m-d' ),
+ 'title' => isset( $data['title'] ) ? sanitize_text_field( $data['title'] ) : '',
+ 'distance' => isset( $data['distance'] ) ? floatval( str_replace( ',', '.', $data['distance'] ) ) : 0,
+ 'duration' => isset( $data['duration'] ) ? sanitize_text_field( $data['duration'] ) : '00:00:00',
+ 'calories' => isset( $data['calories'] ) ? intval( $data['calories'] ) : null,
+ 'comment' => isset( $data['comment'] ) ? sanitize_textarea_field( $data['comment'] ) : null,
+ 'strava_url' => isset( $data['strava_url'] ) ? $null_if_empty( esc_url_raw( $data['strava_url'] ) ) : null,
+ 'avg_heart_rate' => isset( $data['avg_heart_rate'] ) ? $null_if_empty( intval( $data['avg_heart_rate'] ) ) : null,
+ 'max_heart_rate' => isset( $data['max_heart_rate'] ) ? $null_if_empty( intval( $data['max_heart_rate'] ) ) : null,
+ 'avg_speed' => isset( $data['avg_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $data['avg_speed'] ) ) ) : null,
+ 'max_speed' => isset( $data['max_speed'] ) ? $null_if_empty( floatval( str_replace( ',', '.', $data['max_speed'] ) ) ) : null,
+ 'avg_cadence' => isset( $data['avg_cadence'] ) ? $null_if_empty( intval( $data['avg_cadence'] ) ) : null,
+ 'max_cadence' => isset( $data['max_cadence'] ) ? $null_if_empty( intval( $data['max_cadence'] ) ) : null,
+ 'total_elevation_gain' => isset( $data['total_elevation_gain'] ) ? $null_if_empty( intval( $data['total_elevation_gain'] ) ) : null,
+ 'total_elevation_loss' => isset( $data['total_elevation_loss'] ) ? $null_if_empty( intval( $data['total_elevation_loss'] ) ) : null,
+ 'min_altitude' => isset( $data['min_altitude'] ) ? $null_if_empty( intval( $data['min_altitude'] ) ) : null,
+ 'max_altitude' => isset( $data['max_altitude'] ) ? $null_if_empty( intval( $data['max_altitude'] ) ) : null,
+ 'equipment_id' => isset( $data['equipment_id'] ) ? $null_if_empty( intval( $data['equipment_id'] ) ) : null,
+ 'gpx_url' => isset( $data['gpx_url'] ) ? $null_if_empty( esc_url_raw( $data['gpx_url'] ) ) : null,
+ 'event_type_id' => isset( $data['event_type_id'] ) ? $null_if_empty( intval( $data['event_type_id'] ) ) : null,
+ );
+
+ // Data formats for $wpdb.
+ $format = array(
+ '%d', // category_id
+ '%s', // date
+ '%s', // title
+ '%f', // distance
+ '%s', // duration
+ '%d', // calories
+ '%s', // comment
+ '%s', // strava_url
+ '%d', // avg_heart_rate
+ '%d', // max_heart_rate
+ '%f', // avg_speed
+ '%f', // max_speed
+ '%d', // avg_cadence
+ '%d', // max_cadence
+ '%d', // total_elevation_gain
+ '%d', // total_elevation_loss
+ '%d', // min_altitude
+ '%d', // max_altitude
+ '%d', // equipment_id
+ '%s', // gpx_url
+ '%d', // event_type_id
+ );
+
+ if ( $activity_id > 0 ) {
+ // UPDATE
+ $result = $wpdb->update( $table_activities, $prepared_data, array( 'id' => $activity_id ), $format, array( '%d' ) );
+ if ( false !== $result ) {
+ return $activity_id;
+ }
+ } else {
+ // INSERT
+ $result = $wpdb->insert( $table_activities, $prepared_data, $format );
+ if ( $result ) {
+ return $wpdb->insert_id;
+ }
+ }
+
+ return false;
+}
\ No newline at end of file
diff --git a/moje-statystyki.php b/moje-statystyki.php
index c8b7135..0f4e124 100644
--- a/moje-statystyki.php
+++ b/moje-statystyki.php
@@ -17,6 +17,7 @@ define( 'MYSTAT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
// --- 1. PLIKI RDZENNE I AKTYWACJA ---
require_once MYSTAT_PLUGIN_DIR . 'includes/activation.php';
require_once MYSTAT_PLUGIN_DIR . 'includes/core/gpx-parser.php';
+require_once MYSTAT_PLUGIN_DIR . 'includes/core/crud-activity.php';
require_once MYSTAT_PLUGIN_DIR . 'includes/core/gpx-upload.php';
register_activation_hook( __FILE__, 'mystat_activate' );
@@ -42,6 +43,11 @@ add_action( 'admin_menu', 'mystat_add_admin_menu' );
add_action( 'admin_init', 'mystat_admin_init_setup' );
add_action( 'admin_enqueue_scripts', 'mystat_enqueue_admin_styles' );
+// --- 3. REST API ---
+require_once MYSTAT_PLUGIN_DIR . 'includes/api/routes.php';
+add_action( 'rest_api_init', 'mystat_register_rest_routes' );
+
+
// --- 3. SHORTCODE DO WYŚWIETLANIA NA FRONCIE ---
require_once MYSTAT_PLUGIN_DIR . 'includes/frontend/assets.php';
require_once MYSTAT_PLUGIN_DIR . 'includes/frontend/shortcodes.php';