save_settings(); } // Handle email templates save. if ( isset( $_POST['yacht_booking_save_email_templates'] ) ) { check_admin_referer( 'yacht_booking_save_email_templates' ); $this->save_email_templates(); } // Handle reset email templates. if ( isset( $_POST['yacht_booking_reset_email_templates'] ) ) { check_admin_referer( 'yacht_booking_save_email_templates' ); Email_Templates::reset_templates(); wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=email-templates&templates_reset=1' ) ); exit; } // Handle global iCal settings save. if ( isset( $_POST['yacht_booking_save_global_ical'] ) ) { check_admin_referer( 'yacht_booking_save_global_ical' ); $import_url = isset( $_POST['global_ical_import_url'] ) ? esc_url_raw( wp_unslash( $_POST['global_ical_import_url'] ) ) : ''; update_option( 'yacht_booking_global_ical_import_url', $import_url ); $sync_mode = isset( $_POST['ical_sync_mode'] ) ? sanitize_text_field( wp_unslash( $_POST['ical_sync_mode'] ) ) : 'per_yacht'; if ( ! in_array( $sync_mode, array( 'per_yacht', 'global' ), true ) ) { $sync_mode = 'per_yacht'; } update_option( 'yacht_booking_ical_sync_mode', $sync_mode ); wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=google-calendar&global_ical_saved=1' ) ); exit; } // Handle global iCal token regeneration. if ( isset( $_POST['yacht_booking_regenerate_global_ical_token'] ) ) { check_admin_referer( 'yacht_booking_regenerate_global_ical_token' ); \YachtBooking\Integrations\ICal\ICal_Feed::regenerate_global_token(); wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=google-calendar&token_regenerated=1' ) ); exit; } // Handle global iCal manual import trigger. if ( isset( $_POST['yacht_booking_run_global_ical_import'] ) ) { check_admin_referer( 'yacht_booking_run_global_ical_import' ); $result = \YachtBooking\Integrations\ICal\ICal_Import::run_global_import(); $arg = $result ? 'global_import_done=1' : 'global_import_failed=1'; wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=google-calendar&' . $arg ) ); exit; } } /** * Display admin notices */ public function display_admin_notices() { // Only show on settings page if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'yacht-bookings-settings' ) { return; } // Success: Settings saved if ( isset( $_GET['saved'] ) ) { ?>

delete_yacht( $yacht_id ) ) { $deleted++; } } // Redirect with success message $redirect_url = add_query_arg( array( 'page' => 'yacht-bookings', 'deleted' => $deleted, ), admin_url( 'admin.php' ) ); wp_safe_redirect( $redirect_url ); exit; } } /** * Process single actions */ public function process_single_actions() { // Check for single yacht delete if ( isset( $_GET['action'] ) && $_GET['action'] === 'delete' && isset( $_GET['yacht'] ) ) { if ( ! current_user_can( 'yacht_booking_manage_yachts' ) ) { return; } $yacht_id = (int) $_GET['yacht']; check_admin_referer( 'delete_yacht_' . $yacht_id ); if ( $this->delete_yacht( $yacht_id ) ) { $redirect_url = add_query_arg( array( 'page' => 'yacht-bookings', 'deleted' => 1, ), admin_url( 'admin.php' ) ); wp_safe_redirect( $redirect_url ); exit; } } } /** * Delete yacht and cascade delete bookings * * @param int $yacht_id Yacht ID. * @return bool */ private function delete_yacht( $yacht_id ) { // Get all bookings for this yacht $bookings = get_posts( array( 'post_type' => 'yacht_booking', 'posts_per_page' => -1, 'meta_query' => array( array( 'key' => '_booking_yacht_id', 'value' => $yacht_id, ), ), 'fields' => 'ids', ) ); // Delete all bookings foreach ( $bookings as $booking_id ) { Availability::clear_booking_availability( $booking_id ); wp_delete_post( $booking_id, true ); } // Delete yacht wp_delete_post( $yacht_id, true ); return true; } /** * Save yacht * * @param array $data POST data. * @param int $yacht_id Yacht ID (0 for new). * @return int|false Yacht ID on success, false on failure. */ private function save_yacht( $data, $yacht_id = 0 ) { // Validate $title = isset( $data['yacht_title'] ) ? sanitize_text_field( wp_unslash( $data['yacht_title'] ) ) : ''; $content = isset( $data['yacht_description'] ) ? wp_kses_post( wp_unslash( $data['yacht_description'] ) ) : ''; if ( empty( $title ) ) { return false; } // Prepare post data $post_data = array( 'post_type' => 'yacht', 'post_title' => $title, 'post_content' => $content, 'post_status' => 'publish', ); if ( $yacht_id ) { $post_data['ID'] = $yacht_id; $saved_id = wp_update_post( $post_data ); } else { $saved_id = wp_insert_post( $post_data ); } if ( is_wp_error( $saved_id ) ) { return false; } // Save GCal alias for global iCal import matching. $gcal_alias = isset( $data['yacht_gcal_alias'] ) ? sanitize_text_field( wp_unslash( $data['yacht_gcal_alias'] ) ) : ''; Yacht::update_gcal_alias( $saved_id, $gcal_alias ); // Save admin-selected color for aggregated calendar. $yacht_color = isset( $data['yacht_color'] ) ? sanitize_text_field( wp_unslash( $data['yacht_color'] ) ) : ''; Yacht::update_color( $saved_id, $yacht_color ); return $saved_id; } /** * Render yachts list page */ public function render_yachts_page() { // Load Yacht List Table require_once YACHT_BOOKING_PLUGIN_DIR . 'admin/class-yacht-list-table.php'; $list_table = new Yacht_List_Table(); $list_table->prepare_items(); ?>

%s

', esc_html( sprintf( _n( 'Usunięto %d jacht.', 'Usunięto %d jachtów.', $count, 'yacht-booking' ), $count ) ) ); } ?>
search_box( __( 'Szukaj jachtów', 'yacht-booking' ), 'yacht' ); ?>
display(); ?>
save_yacht( $_POST, $yacht_id ); if ( ! $saved_id ) { return; } $redirect_url = add_query_arg( array( 'page' => 'yacht-bookings-add-yacht', 'yacht_id' => $saved_id, 'saved' => 1, ), admin_url( 'admin.php' ) ); wp_safe_redirect( $redirect_url ); exit; } /** * Render bookings list page */ public function render_bookings_page() { // Load Booking List Table require_once YACHT_BOOKING_PLUGIN_DIR . 'admin/class-booking-list-table.php'; $list_table = new Booking_List_Table(); $list_table->prepare_items(); ?>

%s

', esc_html( sprintf( _n( 'Zatwierdzono %d rezerwację.', 'Zatwierdzono %d rezerwacji.', $count, 'yacht-booking' ), $count ) ) ); } if ( isset( $_GET['cancelled'] ) ) { $count = (int) $_GET['cancelled']; printf( '

%s

', esc_html( sprintf( _n( 'Anulowano %d rezerwację.', 'Anulowano %d rezerwacji.', $count, 'yacht-booking' ), $count ) ) ); } if ( isset( $_GET['deleted'] ) ) { $count = (int) $_GET['deleted']; printf( '

%s

', esc_html( sprintf( _n( 'Usunięto %d rezerwację.', 'Usunięto %d rezerwacji.', $count, 'yacht-booking' ), $count ) ) ); } ?>
search_box( __( 'Szukaj rezerwacji', 'yacht-booking' ), 'booking' ); $list_table->views(); ?>
display(); ?>
get_export_filters(); $yachts = get_posts( array( 'post_type' => 'yacht', 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC', ) ); ?>

get_export_filters(); $args = $this->get_export_query_args( $filters ); $bookings = get_posts( $args ); nocache_headers(); $filename = 'yacht-bookings-' . gmdate( 'Ymd-His' ) . '.csv'; header( 'Content-Type: text/csv; charset=UTF-8' ); header( 'Content-Disposition: attachment; filename=' . $filename ); $output = fopen( 'php://output', 'w' ); if ( false === $output ) { exit; } // UTF-8 BOM for Excel compatibility. fwrite( $output, chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF ) ); fputcsv( $output, array( 'booking_id', 'yacht', 'customer_name', 'customer_email', 'customer_phone', 'start_date', 'end_date', 'status', 'total_price', 'created_at', ), ';' ); $status_labels = array( 'pending' => __( 'Oczekująca', 'yacht-booking' ), 'confirmed' => __( 'Potwierdzona', 'yacht-booking' ), 'cancelled' => __( 'Anulowana', 'yacht-booking' ), ); foreach ( $bookings as $booking ) { $booking_id = $booking->ID; $yacht_id = Booking::get_yacht_id( $booking_id ); $yacht = get_post( $yacht_id ); $status = Booking::get_status( $booking_id ); fputcsv( $output, array( $booking_id, $yacht ? $yacht->post_title : '', Booking::get_customer_name( $booking_id ), Booking::get_customer_email( $booking_id ), Booking::get_customer_phone( $booking_id ), Settings::format_date( Booking::get_start_date( $booking_id ) ), Settings::format_date( Booking::get_end_date( $booking_id ) ), isset( $status_labels[ $status ] ) ? $status_labels[ $status ] : $status, Settings::format_price( Booking::get_total_price( $booking_id ) ), get_the_date( Settings::get_date_format() . ' H:i', $booking ), ), ';' ); } fclose( $output ); exit; } /** * Get sanitized export filters from query string. * * @return array */ private function get_export_filters() { $status_filter = isset( $_GET['status_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['status_filter'] ) ) : 'all'; $yacht_filter = isset( $_GET['yacht_filter'] ) ? absint( $_GET['yacht_filter'] ) : 0; $date_from = isset( $_GET['date_from'] ) ? sanitize_text_field( wp_unslash( $_GET['date_from'] ) ) : ''; $date_to = isset( $_GET['date_to'] ) ? sanitize_text_field( wp_unslash( $_GET['date_to'] ) ) : ''; if ( ! in_array( $status_filter, array( 'all', 'pending', 'confirmed', 'cancelled' ), true ) ) { $status_filter = 'all'; } if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date_from ) ) { $date_from = ''; } if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date_to ) ) { $date_to = ''; } return array( 'status_filter' => $status_filter, 'yacht_filter' => $yacht_filter, 'date_from' => $date_from, 'date_to' => $date_to, ); } /** * Build WP_Query args for export. * * @param array $filters Export filters. * @return array */ private function get_export_query_args( $filters ) { $args = array( 'post_type' => 'yacht_booking', 'post_status' => 'publish', 'posts_per_page' => -1, 'orderby' => 'date', 'order' => 'DESC', ); $meta_query = array(); if ( 'all' !== $filters['status_filter'] ) { $meta_query[] = array( 'key' => '_booking_status', 'value' => $filters['status_filter'], ); } if ( ! empty( $filters['yacht_filter'] ) ) { $meta_query[] = array( 'key' => '_booking_yacht_id', 'value' => $filters['yacht_filter'], ); } if ( ! empty( $filters['date_from'] ) ) { $meta_query[] = array( 'key' => '_booking_end_date', 'value' => $filters['date_from'], 'compare' => '>=', 'type' => 'DATE', ); } if ( ! empty( $filters['date_to'] ) ) { $meta_query[] = array( 'key' => '_booking_start_date', 'value' => $filters['date_to'], 'compare' => '<=', 'type' => 'DATE', ); } if ( ! empty( $meta_query ) ) { $args['meta_query'] = array_merge( array( 'relation' => 'AND' ), $meta_query ); } return $args; } /** * Render settings page */ /** * Render inquiries admin page */ public function render_inquiries_page() { if ( ! current_user_can( 'yacht_booking_manage_bookings' ) ) { wp_die( esc_html__( 'Brak uprawnień.', 'yacht-booking' ) ); } // Handle view email action if ( isset( $_GET['action'] ) && 'view_email' === $_GET['action'] && isset( $_GET['inquiry'] ) ) { $inquiry_id = absint( $_GET['inquiry'] ); check_admin_referer( 'view_email_' . $inquiry_id ); $type = isset( $_GET['type'] ) && 'customer' === $_GET['type'] ? 'customer' : 'admin'; $body = 'admin' === $type ? Inquiry::get_admin_email_body( $inquiry_id ) : Inquiry::get_customer_email_body( $inquiry_id ); $label = 'admin' === $type ? __( 'Email wysłany do administratora', 'yacht-booking' ) : __( 'Email wysłany do klienta', 'yacht-booking' ); echo '
'; echo '

' . esc_html( $label ) . ' — #' . esc_html( $inquiry_id ) . '

'; echo '

← ' . esc_html__( 'Wróć do listy zapytań', 'yacht-booking' ) . '

'; echo '
'; echo $body ? wp_kses_post( $body ) : '

' . esc_html__( 'Brak treści emaila.', 'yacht-booking' ) . '

'; echo '
'; return; } // Handle delete action if ( isset( $_GET['action'] ) && 'delete' === $_GET['action'] && isset( $_GET['inquiry'] ) ) { $inquiry_id = absint( $_GET['inquiry'] ); check_admin_referer( 'delete_inquiry_' . $inquiry_id ); wp_delete_post( $inquiry_id, true ); wp_safe_redirect( admin_url( 'admin.php?page=yacht-inquiries&deleted=1' ) ); exit; } // Handle bulk delete if ( isset( $_POST['action'] ) && 'delete' === $_POST['action'] && ! empty( $_POST['inquiry'] ) ) { check_admin_referer( 'bulk-inquiries' ); $ids = array_map( 'absint', (array) $_POST['inquiry'] ); foreach ( $ids as $id ) { wp_delete_post( $id, true ); } wp_safe_redirect( admin_url( 'admin.php?page=yacht-inquiries&deleted=' . count( $ids ) ) ); exit; } require_once YACHT_BOOKING_PLUGIN_DIR . 'admin/class-inquiry-list-table.php'; $table = new Inquiry_List_Table(); $table->prepare_items(); ?>

search_box( __( 'Szukaj', 'yacht-booking' ), 'inquiry-search' ); $table->display(); ?>

render_general_settings(); } elseif ( $active_tab === 'email-templates' ) { $this->render_email_templates_settings(); } elseif ( $active_tab === 'google-calendar' ) { $this->render_google_calendar_settings(); } ?>

'terms_page_id', 'id' => 'terms_page_id', 'selected' => $terms_page_id, 'show_option_none' => __( '— Wybierz stronę —', 'yacht-booking' ), 'option_none_value' => 0, ) ); ?>

1234, 'yacht_name' => __( 'Oceanic 42', 'yacht-booking' ), 'customer_name' => __( 'Jan Kowalski', 'yacht-booking' ), 'customer_email' => 'jan.kowalski@example.com', 'customer_phone' => '+48 500 600 700', 'start_date' => Settings::format_date( gmdate( 'Y-m-d', strtotime( '+7 days' ) ) ), 'end_date' => Settings::format_date( gmdate( 'Y-m-d', strtotime( '+10 days' ) ) ), 'days' => 4, 'total_price' => Settings::format_price( 2499.00 ), 'status' => __( 'Potwierdzona', 'yacht-booking' ), 'admin_link' => admin_url( 'admin.php?page=yacht-bookings-list' ), ); ?>

$label ) : ?> -

$label ) : ?> '', 'body' => '' ); $preview = Email_Templates::compile( $type, $preview_data ); ?>

'template_body[' . $type . ']', 'textarea_rows' => 8, 'media_buttons' => false, 'teeny' => true, ) ); ?>

$default_template ) { $templates[ $type ] = array( 'subject' => isset( $subjects[ $type ] ) ? $subjects[ $type ] : '', 'body' => isset( $bodies[ $type ] ) ? $bodies[ $type ] : '', ); } Email_Templates::save_templates( $templates ); wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=email-templates&templates_saved=1' ) ); exit; } /** * Render Google Calendar settings tab */ private function render_google_calendar_settings() { ?>

'yacht-bookings-list', 'approved' => 1, ), admin_url( 'admin.php' ) ) ); exit; } // Single action: cancel if ( isset( $_GET['action'] ) && $_GET['action'] === 'cancel' && isset( $_GET['booking'] ) ) { $booking_id = (int) $_GET['booking']; check_admin_referer( 'cancel_booking_' . $booking_id ); // Update status to cancelled Booking::update_status( $booking_id, 'cancelled' ); // Clear availability cache Availability::clear_booking_availability( $booking_id ); wp_safe_redirect( add_query_arg( array( 'page' => 'yacht-bookings-list', 'cancelled' => 1, ), admin_url( 'admin.php' ) ) ); exit; } // Single action: delete if ( isset( $_GET['action'] ) && $_GET['action'] === 'delete' && isset( $_GET['booking'] ) ) { $booking_id = (int) $_GET['booking']; check_admin_referer( 'delete_booking_' . $booking_id ); // Clear availability cache Availability::clear_booking_availability( $booking_id ); // Delete booking wp_delete_post( $booking_id, true ); wp_safe_redirect( add_query_arg( array( 'page' => 'yacht-bookings-list', 'deleted' => 1, ), admin_url( 'admin.php' ) ) ); exit; } // Bulk actions if ( isset( $_POST['action'] ) && isset( $_POST['booking'] ) ) { check_admin_referer( 'bulk-bookings' ); $booking_ids = array_map( 'intval', $_POST['booking'] ); $action = sanitize_text_field( wp_unslash( $_POST['action'] ) ); $count = 0; switch ( $action ) { case 'approve': foreach ( $booking_ids as $booking_id ) { $current_status = Booking::get_status( $booking_id ); if ( 'pending' === $current_status ) { Booking::update_status( $booking_id, 'confirmed' ); $count++; } } wp_safe_redirect( add_query_arg( array( 'page' => 'yacht-bookings-list', 'approved' => $count, ), admin_url( 'admin.php' ) ) ); exit; case 'cancel': foreach ( $booking_ids as $booking_id ) { $current_status = Booking::get_status( $booking_id ); if ( in_array( $current_status, array( 'pending', 'confirmed' ), true ) ) { Booking::update_status( $booking_id, 'cancelled' ); Availability::clear_booking_availability( $booking_id ); $count++; } } wp_safe_redirect( add_query_arg( array( 'page' => 'yacht-bookings-list', 'cancelled' => $count, ), admin_url( 'admin.php' ) ) ); exit; case 'delete': foreach ( $booking_ids as $booking_id ) { Availability::clear_booking_availability( $booking_id ); wp_delete_post( $booking_id, true ); $count++; } wp_safe_redirect( add_query_arg( array( 'page' => 'yacht-bookings-list', 'deleted' => $count, ), admin_url( 'admin.php' ) ) ); exit; } } } /** * Send email notification to customer when booking status changes * * @param int $booking_id Booking post ID. * @param string $new_status New status. */ public function send_customer_notification( $booking_id, $new_status ) { // Only send emails for confirmed and cancelled statuses if ( ! in_array( $new_status, array( 'confirmed', 'cancelled' ), true ) ) { return; } $template_type = 'confirmed' === $new_status ? Email_Templates::TYPE_CUSTOMER_CONFIRMED : Email_Templates::TYPE_CUSTOMER_CANCELLED; $template_data = Email_Templates::get_booking_template_data( $booking_id ); $customer_email = Booking::get_customer_email( $booking_id ); $template = Email_Templates::compile( $template_type, $template_data ); if ( empty( $customer_email ) || empty( $template['subject'] ) ) { return; } // Email headers $headers = array( 'Content-Type: text/html; charset=UTF-8', 'From: ' . Settings::get_email_from_name() . ' <' . Settings::get_email_from_address() . '>', ); // Send email wp_mail( $customer_email, $template['subject'], $template['body'], $headers ); // Allow other plugins to hook in do_action( 'yacht_booking_customer_notification_sent', $booking_id, $new_status, $customer_email ); } }