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();
?>
',
esc_html( sprintf( _n( 'Usunięto %d jacht.', 'Usunięto %d jachtów.', $count, 'yacht-booking' ), $count ) )
);
}
?>
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();
?>
',
esc_html( sprintf( _n( 'Zatwierdzono %d rezerwację.', 'Zatwierdzono %d rezerwacji.', $count, 'yacht-booking' ), $count ) )
);
}
if ( isset( $_GET['cancelled'] ) ) {
$count = (int) $_GET['cancelled'];
printf(
'',
esc_html( sprintf( _n( 'Anulowano %d rezerwację.', 'Anulowano %d rezerwacji.', $count, 'yacht-booking' ), $count ) )
);
}
if ( isset( $_GET['deleted'] ) ) {
$count = (int) $_GET['deleted'];
printf(
'',
esc_html( sprintf( _n( 'Usunięto %d rezerwację.', 'Usunięto %d rezerwacji.', $count, 'yacht-booking' ), $count ) )
);
}
?>
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 '';
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();
?>
render_general_settings();
} elseif ( $active_tab === 'email-templates' ) {
$this->render_email_templates_settings();
} elseif ( $active_tab === 'google-calendar' ) {
$this->render_google_calendar_settings();
}
?>
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' ),
);
?>
$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 );
}
}