first commit
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
/**
|
||||
* Availability Checker Class
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Availability class - handles yacht availability checking and caching
|
||||
*/
|
||||
class Availability {
|
||||
|
||||
/**
|
||||
* Check if yacht is available for given date range
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param string $start_date Start date (Y-m-d).
|
||||
* @param string $end_date End date (Y-m-d).
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_available( $yacht_id, $start_date, $end_date ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'yacht_availability';
|
||||
|
||||
// Get all dates in range that are not available
|
||||
$unavailable = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $table
|
||||
WHERE yacht_id = %d
|
||||
AND date >= %s
|
||||
AND date < %s
|
||||
AND status != 'available'",
|
||||
$yacht_id,
|
||||
$start_date,
|
||||
$end_date
|
||||
)
|
||||
);
|
||||
|
||||
return 0 === (int) $unavailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark dates as booked
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param string $start_date Start date (Y-m-d).
|
||||
* @param string $end_date End date (Y-m-d).
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function mark_as_booked( $yacht_id, $start_date, $end_date, $booking_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'yacht_availability';
|
||||
$dates = self::get_date_range( $start_date, $end_date );
|
||||
|
||||
foreach ( $dates as $date ) {
|
||||
$wpdb->replace(
|
||||
$table,
|
||||
array(
|
||||
'yacht_id' => $yacht_id,
|
||||
'date' => $date,
|
||||
'status' => 'booked',
|
||||
'booking_id' => $booking_id,
|
||||
'created_at' => current_time( 'mysql' ),
|
||||
'updated_at' => current_time( 'mysql' ),
|
||||
),
|
||||
array( '%d', '%s', '%s', '%d', '%s', '%s' )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark dates as blocked (from Google Calendar sync)
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param string $start_date Start date (Y-m-d).
|
||||
* @param string $end_date End date (Y-m-d).
|
||||
* @return bool
|
||||
*/
|
||||
public static function mark_as_blocked( $yacht_id, $start_date, $end_date ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'yacht_availability';
|
||||
$dates = self::get_date_range( $start_date, $end_date );
|
||||
|
||||
foreach ( $dates as $date ) {
|
||||
$wpdb->replace(
|
||||
$table,
|
||||
array(
|
||||
'yacht_id' => $yacht_id,
|
||||
'date' => $date,
|
||||
'status' => 'blocked',
|
||||
'booking_id' => null,
|
||||
'created_at' => current_time( 'mysql' ),
|
||||
'updated_at' => current_time( 'mysql' ),
|
||||
),
|
||||
array( '%d', '%s', '%s', '%d', '%s', '%s' )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark dates as available (when booking is cancelled)
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param string $start_date Start date (Y-m-d).
|
||||
* @param string $end_date End date (Y-m-d).
|
||||
* @return bool
|
||||
*/
|
||||
public static function mark_as_available( $yacht_id, $start_date, $end_date ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'yacht_availability';
|
||||
|
||||
$wpdb->delete(
|
||||
$table,
|
||||
array(
|
||||
'yacht_id' => $yacht_id,
|
||||
),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
// Alternative: update to available instead of delete
|
||||
// $dates = self::get_date_range( $start_date, $end_date );
|
||||
// foreach ( $dates as $date ) {
|
||||
// $wpdb->update(
|
||||
// $table,
|
||||
// array( 'status' => 'available', 'booking_id' => null ),
|
||||
// array( 'yacht_id' => $yacht_id, 'date' => $date ),
|
||||
// array( '%s', '%d' ),
|
||||
// array( '%d', '%s' )
|
||||
// );
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear availability for booking
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function clear_booking_availability( $booking_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'yacht_availability';
|
||||
|
||||
$wpdb->delete(
|
||||
$table,
|
||||
array( 'booking_id' => $booking_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get availability calendar for yacht and month
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param string $start Start date (Y-m-d).
|
||||
* @param string $end End date (Y-m-d).
|
||||
* @return array
|
||||
*/
|
||||
public static function get_availability_calendar( $yacht_id, $start, $end ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'yacht_availability';
|
||||
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT date, status, booking_id FROM $table
|
||||
WHERE yacht_id = %d
|
||||
AND date >= %s
|
||||
AND date <= %s
|
||||
ORDER BY date ASC",
|
||||
$yacht_id,
|
||||
$start,
|
||||
$end
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
// Create associative array with date as key
|
||||
$calendar = array();
|
||||
foreach ( $results as $row ) {
|
||||
$calendar[ $row['date'] ] = array(
|
||||
'status' => $row['status'],
|
||||
'booking_id' => $row['booking_id'],
|
||||
);
|
||||
}
|
||||
|
||||
// Fill in missing dates as available
|
||||
$all_dates = self::get_date_range( $start, $end );
|
||||
foreach ( $all_dates as $date ) {
|
||||
if ( ! isset( $calendar[ $date ] ) ) {
|
||||
$calendar[ $date ] = array(
|
||||
'status' => 'available',
|
||||
'booking_id' => null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $calendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count days between two dates
|
||||
*
|
||||
* @param string $start_date Start date (Y-m-d).
|
||||
* @param string $end_date End date (Y-m-d).
|
||||
* @return int
|
||||
*/
|
||||
public static function count_days( $start_date, $end_date ) {
|
||||
$start = new \DateTime( $start_date );
|
||||
$end = new \DateTime( $end_date );
|
||||
$diff = $start->diff( $end );
|
||||
|
||||
return (int) $diff->days;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of dates between start and end (exclusive of end date)
|
||||
*
|
||||
* @param string $start_date Start date (Y-m-d).
|
||||
* @param string $end_date End date (Y-m-d).
|
||||
* @return array
|
||||
*/
|
||||
private static function get_date_range( $start_date, $end_date ) {
|
||||
$dates = array();
|
||||
$start = new \DateTime( $start_date );
|
||||
$end = new \DateTime( $end_date );
|
||||
|
||||
while ( $start < $end ) {
|
||||
$dates[] = $start->format( 'Y-m-d' );
|
||||
$start->modify( '+1 day' );
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
/**
|
||||
* Booking Custom Post Type
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Booking CPT class
|
||||
*/
|
||||
class Booking {
|
||||
|
||||
/**
|
||||
* Register custom post type
|
||||
*/
|
||||
public static function register() {
|
||||
$labels = array(
|
||||
'name' => __( 'Rezerwacje', 'yacht-booking' ),
|
||||
'singular_name' => __( 'Rezerwacja', 'yacht-booking' ),
|
||||
'menu_name' => __( 'Rezerwacje', 'yacht-booking' ),
|
||||
'name_admin_bar' => __( 'Rezerwacja', 'yacht-booking' ),
|
||||
'add_new' => __( 'Dodaj nową', 'yacht-booking' ),
|
||||
'add_new_item' => __( 'Dodaj nową rezerwację', 'yacht-booking' ),
|
||||
'new_item' => __( 'Nowa rezerwacja', 'yacht-booking' ),
|
||||
'edit_item' => __( 'Edytuj rezerwację', 'yacht-booking' ),
|
||||
'view_item' => __( 'Zobacz rezerwację', 'yacht-booking' ),
|
||||
'all_items' => __( 'Wszystkie rezerwacje', 'yacht-booking' ),
|
||||
'search_items' => __( 'Szukaj rezerwacji', 'yacht-booking' ),
|
||||
'parent_item_colon' => __( 'Nadrzędna rezerwacja:', 'yacht-booking' ),
|
||||
'not_found' => __( 'Nie znaleziono rezerwacji', 'yacht-booking' ),
|
||||
'not_found_in_trash' => __( 'Nie znaleziono rezerwacji w koszu', 'yacht-booking' ),
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'labels' => $labels,
|
||||
'description' => __( 'Rezerwacje jachtów', 'yacht-booking' ),
|
||||
'public' => false,
|
||||
'publicly_queryable' => false,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => false, // Custom menu będzie w class-admin.php
|
||||
'show_in_rest' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => false,
|
||||
'capability_type' => 'post',
|
||||
'capabilities' => array(
|
||||
'edit_post' => 'yacht_booking_manage_bookings',
|
||||
'read_post' => 'yacht_booking_manage_bookings',
|
||||
'delete_post' => 'yacht_booking_manage_bookings',
|
||||
'edit_posts' => 'yacht_booking_manage_bookings',
|
||||
'edit_others_posts' => 'yacht_booking_manage_bookings',
|
||||
'publish_posts' => 'yacht_booking_manage_bookings',
|
||||
'read_private_posts' => 'yacht_booking_manage_bookings',
|
||||
'delete_posts' => 'yacht_booking_manage_bookings',
|
||||
),
|
||||
'has_archive' => false,
|
||||
'hierarchical' => false,
|
||||
'menu_position' => null,
|
||||
'supports' => array( 'title', 'custom-fields' ),
|
||||
);
|
||||
|
||||
register_post_type( 'yacht_booking', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get booking yacht ID
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return int
|
||||
*/
|
||||
public static function get_yacht_id( $booking_id ) {
|
||||
return (int) get_post_meta( $booking_id, '_booking_yacht_id', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get booking start date
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_start_date( $booking_id ) {
|
||||
return get_post_meta( $booking_id, '_booking_start_date', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get booking end date
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_end_date( $booking_id ) {
|
||||
return get_post_meta( $booking_id, '_booking_end_date', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get booking status
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_status( $booking_id ) {
|
||||
$status = get_post_meta( $booking_id, '_booking_status', true );
|
||||
return $status ? $status : 'pending';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customer name
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_customer_name( $booking_id ) {
|
||||
return get_post_meta( $booking_id, '_booking_customer_name', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customer email
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_customer_email( $booking_id ) {
|
||||
return get_post_meta( $booking_id, '_booking_customer_email', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customer phone
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_customer_phone( $booking_id ) {
|
||||
return get_post_meta( $booking_id, '_booking_customer_phone', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total price
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return float
|
||||
*/
|
||||
public static function get_total_price( $booking_id ) {
|
||||
return (float) get_post_meta( $booking_id, '_booking_total_price', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Google Calendar Event ID
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_gcal_event_id( $booking_id ) {
|
||||
return get_post_meta( $booking_id, '_booking_gcal_event_id', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get admin notes
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_notes( $booking_id ) {
|
||||
return get_post_meta( $booking_id, '_booking_notes', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update booking status
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @param string $status Status (pending, confirmed, cancelled).
|
||||
*/
|
||||
public static function update_status( $booking_id, $status ) {
|
||||
$valid_statuses = array( 'pending', 'confirmed', 'cancelled' );
|
||||
|
||||
if ( in_array( $status, $valid_statuses, true ) ) {
|
||||
update_post_meta( $booking_id, '_booking_status', $status );
|
||||
|
||||
// Trigger action hook
|
||||
do_action( 'yacht_booking_status_changed', $booking_id, $status );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new booking
|
||||
*
|
||||
* @param array $data Booking data.
|
||||
* @return int|false Booking ID on success, false on failure.
|
||||
*/
|
||||
public static function create( $data ) {
|
||||
// Validate required fields
|
||||
$required = array( 'yacht_id', 'start_date', 'end_date', 'customer_name', 'customer_email', 'customer_phone' );
|
||||
foreach ( $required as $field ) {
|
||||
if ( empty( $data[ $field ] ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get yacht title
|
||||
$yacht = get_post( $data['yacht_id'] );
|
||||
if ( ! $yacht || $yacht->post_type !== 'yacht' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create booking post
|
||||
$booking_id = wp_insert_post(
|
||||
array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'post_title' => sprintf(
|
||||
__( 'Rezerwacja #%s - %s', 'yacht-booking' ),
|
||||
time(),
|
||||
$yacht->post_title
|
||||
),
|
||||
'post_status' => 'publish',
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $booking_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save booking meta
|
||||
update_post_meta( $booking_id, '_booking_yacht_id', (int) $data['yacht_id'] );
|
||||
update_post_meta( $booking_id, '_booking_start_date', sanitize_text_field( $data['start_date'] ) );
|
||||
update_post_meta( $booking_id, '_booking_end_date', sanitize_text_field( $data['end_date'] ) );
|
||||
update_post_meta( $booking_id, '_booking_status', ! empty( $data['status'] ) ? $data['status'] : 'pending' );
|
||||
update_post_meta( $booking_id, '_booking_customer_name', sanitize_text_field( $data['customer_name'] ) );
|
||||
update_post_meta( $booking_id, '_booking_customer_email', sanitize_email( $data['customer_email'] ) );
|
||||
update_post_meta( $booking_id, '_booking_customer_phone', sanitize_text_field( $data['customer_phone'] ) );
|
||||
|
||||
if ( ! empty( $data['total_price'] ) ) {
|
||||
update_post_meta( $booking_id, '_booking_total_price', (float) $data['total_price'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $data['notes'] ) ) {
|
||||
update_post_meta( $booking_id, '_booking_notes', wp_kses_post( $data['notes'] ) );
|
||||
}
|
||||
|
||||
// Trigger action hook
|
||||
do_action( 'yacht_booking_created', $booking_id );
|
||||
|
||||
return $booking_id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
/**
|
||||
* Email templates helper.
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Email templates class.
|
||||
*/
|
||||
class Email_Templates {
|
||||
|
||||
/**
|
||||
* Option key.
|
||||
*/
|
||||
const OPTION_KEY = 'yacht_booking_email_templates';
|
||||
|
||||
/**
|
||||
* Template type: admin notification.
|
||||
*/
|
||||
const TYPE_ADMIN_NEW_BOOKING = 'admin_new_booking';
|
||||
|
||||
/**
|
||||
* Template type: customer confirmed.
|
||||
*/
|
||||
const TYPE_CUSTOMER_CONFIRMED = 'customer_confirmed';
|
||||
|
||||
/**
|
||||
* Template type: customer cancelled.
|
||||
*/
|
||||
const TYPE_CUSTOMER_CANCELLED = 'customer_cancelled';
|
||||
|
||||
/**
|
||||
* Get template labels.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_template_labels() {
|
||||
return array(
|
||||
self::TYPE_ADMIN_NEW_BOOKING => __( 'Nowa rezerwacja (email do admina)', 'yacht-booking' ),
|
||||
self::TYPE_CUSTOMER_CONFIRMED => __( 'Potwierdzenie rezerwacji (email do klienta)', 'yacht-booking' ),
|
||||
self::TYPE_CUSTOMER_CANCELLED => __( 'Anulowanie rezerwacji (email do klienta)', 'yacht-booking' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available tags for templates.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_available_tags() {
|
||||
return array(
|
||||
'{booking_id}' => __( 'ID rezerwacji', 'yacht-booking' ),
|
||||
'{yacht_name}' => __( 'Nazwa jachtu', 'yacht-booking' ),
|
||||
'{customer_name}' => __( 'Imię i nazwisko klienta', 'yacht-booking' ),
|
||||
'{customer_email}' => __( 'Email klienta', 'yacht-booking' ),
|
||||
'{customer_phone}' => __( 'Telefon klienta', 'yacht-booking' ),
|
||||
'{start_date}' => __( 'Data rozpoczęcia', 'yacht-booking' ),
|
||||
'{end_date}' => __( 'Data zakończenia', 'yacht-booking' ),
|
||||
'{days}' => __( 'Liczba dni', 'yacht-booking' ),
|
||||
'{total_price}' => __( 'Cena całkowita', 'yacht-booking' ),
|
||||
'{status}' => __( 'Status rezerwacji', 'yacht-booking' ),
|
||||
'{admin_link}' => __( 'Link do panelu admin', 'yacht-booking' ),
|
||||
'{site_name}' => __( 'Nazwa strony', 'yacht-booking' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default templates.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_default_templates() {
|
||||
return array(
|
||||
self::TYPE_ADMIN_NEW_BOOKING => array(
|
||||
'subject' => __( '[Nowa Rezerwacja] #{booking_id} - {site_name}', 'yacht-booking' ),
|
||||
'body' => __(
|
||||
'<p>Otrzymano nową rezerwację jachtu.</p>
|
||||
<p><strong>Szczegóły rezerwacji:</strong><br>
|
||||
Numer rezerwacji: #{booking_id}<br>
|
||||
Jacht: {yacht_name}<br>
|
||||
Data rozpoczęcia: {start_date}<br>
|
||||
Data zakończenia: {end_date}<br>
|
||||
Liczba dni: {days}<br>
|
||||
Cena całkowita: {total_price}<br>
|
||||
Status: {status}</p>
|
||||
<p><strong>Dane klienta:</strong><br>
|
||||
Imię i nazwisko: {customer_name}<br>
|
||||
Email: {customer_email}<br>
|
||||
Telefon: {customer_phone}</p>
|
||||
<p><a href="{admin_link}">Przejdź do zarządzania rezerwacją</a></p>',
|
||||
'yacht-booking'
|
||||
),
|
||||
),
|
||||
self::TYPE_CUSTOMER_CONFIRMED => array(
|
||||
'subject' => __( '[Potwierdzenie] Rezerwacja #{booking_id} - {site_name}', 'yacht-booking' ),
|
||||
'body' => __(
|
||||
'<p>Witaj {customer_name},</p>
|
||||
<p>Twoja rezerwacja jachtu została potwierdzona.</p>
|
||||
<p><strong>Szczegóły rezerwacji:</strong><br>
|
||||
Numer rezerwacji: #{booking_id}<br>
|
||||
Jacht: {yacht_name}<br>
|
||||
Data rozpoczęcia: {start_date}<br>
|
||||
Data zakończenia: {end_date}<br>
|
||||
Liczba dni: {days}<br>
|
||||
Cena całkowita: {total_price}</p>
|
||||
<p>Dziękujemy za wybranie naszych usług.</p>',
|
||||
'yacht-booking'
|
||||
),
|
||||
),
|
||||
self::TYPE_CUSTOMER_CANCELLED => array(
|
||||
'subject' => __( '[Anulowanie] Rezerwacja #{booking_id} - {site_name}', 'yacht-booking' ),
|
||||
'body' => __(
|
||||
'<p>Witaj {customer_name},</p>
|
||||
<p>Niestety Twoja rezerwacja jachtu została anulowana.</p>
|
||||
<p><strong>Szczegóły rezerwacji:</strong><br>
|
||||
Numer rezerwacji: #{booking_id}<br>
|
||||
Jacht: {yacht_name}<br>
|
||||
Data rozpoczęcia: {start_date}<br>
|
||||
Data zakończenia: {end_date}</p>
|
||||
<p>W razie pytań prosimy o kontakt.</p>',
|
||||
'yacht-booking'
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all templates.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_templates() {
|
||||
$defaults = self::get_default_templates();
|
||||
$saved = get_option( self::OPTION_KEY, array() );
|
||||
|
||||
if ( ! is_array( $saved ) ) {
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
$merged = array();
|
||||
foreach ( $defaults as $type => $template ) {
|
||||
$merged[ $type ] = array(
|
||||
'subject' => isset( $saved[ $type ]['subject'] ) && '' !== trim( (string) $saved[ $type ]['subject'] )
|
||||
? sanitize_text_field( $saved[ $type ]['subject'] )
|
||||
: $template['subject'],
|
||||
'body' => isset( $saved[ $type ]['body'] ) && '' !== trim( (string) $saved[ $type ]['body'] )
|
||||
? wp_kses_post( $saved[ $type ]['body'] )
|
||||
: $template['body'],
|
||||
);
|
||||
}
|
||||
|
||||
return $merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save templates.
|
||||
*
|
||||
* @param array $templates Templates.
|
||||
* @return bool
|
||||
*/
|
||||
public static function save_templates( $templates ) {
|
||||
$defaults = self::get_default_templates();
|
||||
$normalized = array();
|
||||
|
||||
foreach ( $defaults as $type => $default_template ) {
|
||||
$subject = isset( $templates[ $type ]['subject'] ) ? sanitize_text_field( wp_unslash( $templates[ $type ]['subject'] ) ) : '';
|
||||
$body = isset( $templates[ $type ]['body'] ) ? wp_kses_post( wp_unslash( $templates[ $type ]['body'] ) ) : '';
|
||||
|
||||
$normalized[ $type ] = array(
|
||||
'subject' => '' !== trim( $subject ) ? $subject : $default_template['subject'],
|
||||
'body' => '' !== trim( $body ) ? $body : $default_template['body'],
|
||||
);
|
||||
}
|
||||
|
||||
return update_option( self::OPTION_KEY, $normalized );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to default templates.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function reset_templates() {
|
||||
return delete_option( self::OPTION_KEY );
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile template with replacement tags.
|
||||
*
|
||||
* @param string $type Template type.
|
||||
* @param array $data Data for replacements.
|
||||
* @return array
|
||||
*/
|
||||
public static function compile( $type, $data = array() ) {
|
||||
$templates = self::get_templates();
|
||||
|
||||
if ( ! isset( $templates[ $type ] ) ) {
|
||||
return array(
|
||||
'subject' => '',
|
||||
'body' => '',
|
||||
);
|
||||
}
|
||||
|
||||
$replacements = self::build_replacements( $data );
|
||||
|
||||
return array(
|
||||
'subject' => strtr( $templates[ $type ]['subject'], $replacements ),
|
||||
'body' => strtr( $templates[ $type ]['body'], $replacements ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build replacement map.
|
||||
*
|
||||
* @param array $data Raw data.
|
||||
* @return array
|
||||
*/
|
||||
private static function build_replacements( $data ) {
|
||||
$site_name = get_bloginfo( 'name' );
|
||||
|
||||
return array(
|
||||
'{booking_id}' => isset( $data['booking_id'] ) ? (string) $data['booking_id'] : '',
|
||||
'{yacht_name}' => isset( $data['yacht_name'] ) ? (string) $data['yacht_name'] : '',
|
||||
'{customer_name}' => isset( $data['customer_name'] ) ? (string) $data['customer_name'] : '',
|
||||
'{customer_email}' => isset( $data['customer_email'] ) ? (string) $data['customer_email'] : '',
|
||||
'{customer_phone}' => isset( $data['customer_phone'] ) ? (string) $data['customer_phone'] : '',
|
||||
'{start_date}' => isset( $data['start_date'] ) ? (string) $data['start_date'] : '',
|
||||
'{end_date}' => isset( $data['end_date'] ) ? (string) $data['end_date'] : '',
|
||||
'{days}' => isset( $data['days'] ) ? (string) $data['days'] : '',
|
||||
'{total_price}' => isset( $data['total_price'] ) ? (string) $data['total_price'] : '',
|
||||
'{status}' => isset( $data['status'] ) ? (string) $data['status'] : '',
|
||||
'{admin_link}' => isset( $data['admin_link'] ) ? (string) $data['admin_link'] : '',
|
||||
'{site_name}' => $site_name,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build booking data for template replacements.
|
||||
*
|
||||
* @param int $booking_id Booking ID.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_booking_template_data( $booking_id ) {
|
||||
$booking_id = absint( $booking_id );
|
||||
$yacht_id = Booking::get_yacht_id( $booking_id );
|
||||
$yacht = get_post( $yacht_id );
|
||||
|
||||
$start_date = Booking::get_start_date( $booking_id );
|
||||
$end_date = Booking::get_end_date( $booking_id );
|
||||
$status = Booking::get_status( $booking_id );
|
||||
$days = Availability::count_days( $start_date, $end_date );
|
||||
|
||||
return array(
|
||||
'booking_id' => $booking_id,
|
||||
'yacht_name' => $yacht ? $yacht->post_title : __( 'Nieznany', 'yacht-booking' ),
|
||||
'customer_name' => Booking::get_customer_name( $booking_id ),
|
||||
'customer_email' => Booking::get_customer_email( $booking_id ),
|
||||
'customer_phone' => Booking::get_customer_phone( $booking_id ),
|
||||
'start_date' => Settings::format_date( $start_date ),
|
||||
'end_date' => Settings::format_date( $end_date ),
|
||||
'days' => $days,
|
||||
'total_price' => Settings::format_price( Booking::get_total_price( $booking_id ) ),
|
||||
'status' => self::get_status_label( $status ),
|
||||
'admin_link' => admin_url( 'admin.php?page=yacht-bookings-list&booking_id=' . $booking_id ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized status label.
|
||||
*
|
||||
* @param string $status Status.
|
||||
* @return string
|
||||
*/
|
||||
private static function get_status_label( $status ) {
|
||||
$labels = array(
|
||||
'pending' => __( 'Oczekująca', 'yacht-booking' ),
|
||||
'confirmed' => __( 'Potwierdzona', 'yacht-booking' ),
|
||||
'cancelled' => __( 'Anulowana', 'yacht-booking' ),
|
||||
);
|
||||
|
||||
return isset( $labels[ $status ] ) ? $labels[ $status ] : $status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
/**
|
||||
* Inquiry Custom Post Type
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inquiry CPT class
|
||||
*/
|
||||
class Inquiry {
|
||||
|
||||
/**
|
||||
* Register custom post type
|
||||
*/
|
||||
public static function register() {
|
||||
register_post_type(
|
||||
'yacht_inquiry',
|
||||
array(
|
||||
'labels' => array(
|
||||
'name' => __( 'Zapytania', 'yacht-booking' ),
|
||||
'singular_name' => __( 'Zapytanie', 'yacht-booking' ),
|
||||
),
|
||||
'public' => false,
|
||||
'publicly_queryable' => false,
|
||||
'show_ui' => false,
|
||||
'show_in_menu' => false,
|
||||
'show_in_rest' => false,
|
||||
'capability_type' => 'post',
|
||||
'capabilities' => array(
|
||||
'edit_post' => 'yacht_booking_manage_bookings',
|
||||
'read_post' => 'yacht_booking_manage_bookings',
|
||||
'delete_post' => 'yacht_booking_manage_bookings',
|
||||
'edit_posts' => 'yacht_booking_manage_bookings',
|
||||
'edit_others_posts' => 'yacht_booking_manage_bookings',
|
||||
'publish_posts' => 'yacht_booking_manage_bookings',
|
||||
'read_private_posts' => 'yacht_booking_manage_bookings',
|
||||
'delete_posts' => 'yacht_booking_manage_bookings',
|
||||
),
|
||||
'has_archive' => false,
|
||||
'supports' => array( 'title', 'custom-fields' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get yacht ID.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry post ID.
|
||||
* @return int
|
||||
*/
|
||||
public static function get_yacht_id( $inquiry_id ) {
|
||||
return (int) get_post_meta( $inquiry_id, '_inquiry_yacht_id', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customer name.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_customer_name( $inquiry_id ) {
|
||||
return get_post_meta( $inquiry_id, '_inquiry_customer_name', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customer email.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_customer_email( $inquiry_id ) {
|
||||
return get_post_meta( $inquiry_id, '_inquiry_customer_email', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customer phone.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_customer_phone( $inquiry_id ) {
|
||||
return get_post_meta( $inquiry_id, '_inquiry_customer_phone', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_message( $inquiry_id ) {
|
||||
return get_post_meta( $inquiry_id, '_inquiry_message', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get preferred dates.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_preferred_dates( $inquiry_id ) {
|
||||
return get_post_meta( $inquiry_id, '_inquiry_preferred_dates', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email sent to admin body.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_admin_email_body( $inquiry_id ) {
|
||||
return get_post_meta( $inquiry_id, '_inquiry_admin_email_body', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email sent to customer body.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_customer_email_body( $inquiry_id ) {
|
||||
return get_post_meta( $inquiry_id, '_inquiry_customer_email_body', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new inquiry.
|
||||
*
|
||||
* @param array $data Inquiry data.
|
||||
* @return int|false Inquiry ID on success, false on failure.
|
||||
*/
|
||||
public static function create( $data ) {
|
||||
$required = array( 'yacht_id', 'customer_name', 'customer_email', 'customer_phone' );
|
||||
foreach ( $required as $field ) {
|
||||
if ( empty( $data[ $field ] ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$yacht = get_post( $data['yacht_id'] );
|
||||
if ( ! $yacht || 'yacht' !== $yacht->post_type ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$inquiry_id = wp_insert_post(
|
||||
array(
|
||||
'post_type' => 'yacht_inquiry',
|
||||
'post_title' => sprintf(
|
||||
__( 'Zapytanie #%s - %s', 'yacht-booking' ),
|
||||
time(),
|
||||
$yacht->post_title
|
||||
),
|
||||
'post_status' => 'publish',
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $inquiry_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
update_post_meta( $inquiry_id, '_inquiry_yacht_id', (int) $data['yacht_id'] );
|
||||
update_post_meta( $inquiry_id, '_inquiry_customer_name', sanitize_text_field( $data['customer_name'] ) );
|
||||
update_post_meta( $inquiry_id, '_inquiry_customer_email', sanitize_email( $data['customer_email'] ) );
|
||||
update_post_meta( $inquiry_id, '_inquiry_customer_phone', sanitize_text_field( $data['customer_phone'] ) );
|
||||
|
||||
if ( ! empty( $data['message'] ) ) {
|
||||
update_post_meta( $inquiry_id, '_inquiry_message', sanitize_textarea_field( $data['message'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $data['preferred_dates'] ) ) {
|
||||
update_post_meta( $inquiry_id, '_inquiry_preferred_dates', sanitize_text_field( $data['preferred_dates'] ) );
|
||||
}
|
||||
|
||||
return $inquiry_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send inquiry emails (admin + customer) and store copies.
|
||||
*
|
||||
* @param int $inquiry_id Inquiry ID.
|
||||
*/
|
||||
public static function send_emails( $inquiry_id ) {
|
||||
$inquiry_id = absint( $inquiry_id );
|
||||
$yacht_id = self::get_yacht_id( $inquiry_id );
|
||||
$yacht = get_post( $yacht_id );
|
||||
$yacht_name = $yacht ? $yacht->post_title : __( 'Nieznany', 'yacht-booking' );
|
||||
|
||||
$customer_name = self::get_customer_name( $inquiry_id );
|
||||
$customer_email = self::get_customer_email( $inquiry_id );
|
||||
$customer_phone = self::get_customer_phone( $inquiry_id );
|
||||
$message = self::get_message( $inquiry_id );
|
||||
$preferred_dates = self::get_preferred_dates( $inquiry_id );
|
||||
$site_name = get_bloginfo( 'name' );
|
||||
$from_name = Settings::get_email_from_name();
|
||||
$from_address = Settings::get_email_from_address();
|
||||
|
||||
$headers = array(
|
||||
'Content-Type: text/html; charset=UTF-8',
|
||||
'From: ' . $from_name . ' <' . $from_address . '>',
|
||||
);
|
||||
|
||||
// --- Admin email ---
|
||||
$admin_subject = sprintf(
|
||||
/* translators: 1: inquiry ID, 2: site name */
|
||||
__( '[Zapytanie o rezerwację] #%1$d - %2$s', 'yacht-booking' ),
|
||||
$inquiry_id,
|
||||
$site_name
|
||||
);
|
||||
|
||||
$admin_body = '<p>' . __( 'Otrzymano nowe zapytanie o rezerwację jachtu.', 'yacht-booking' ) . '</p>';
|
||||
$admin_body .= '<p><strong>' . __( 'Jacht:', 'yacht-booking' ) . '</strong> ' . esc_html( $yacht_name ) . '</p>';
|
||||
if ( $preferred_dates ) {
|
||||
$admin_body .= '<p><strong>' . __( 'Preferowane terminy:', 'yacht-booking' ) . '</strong> ' . esc_html( $preferred_dates ) . '</p>';
|
||||
}
|
||||
$admin_body .= '<p><strong>' . __( 'Dane klienta:', 'yacht-booking' ) . '</strong><br>';
|
||||
$admin_body .= esc_html__( 'Imię i nazwisko:', 'yacht-booking' ) . ' ' . esc_html( $customer_name ) . '<br>';
|
||||
$admin_body .= esc_html__( 'Email:', 'yacht-booking' ) . ' ' . esc_html( $customer_email ) . '<br>';
|
||||
$admin_body .= esc_html__( 'Telefon:', 'yacht-booking' ) . ' ' . esc_html( $customer_phone ) . '</p>';
|
||||
if ( $message ) {
|
||||
$admin_body .= '<p><strong>' . __( 'Wiadomość:', 'yacht-booking' ) . '</strong><br>' . nl2br( esc_html( $message ) ) . '</p>';
|
||||
}
|
||||
$admin_body .= '<p><a href="' . esc_url( admin_url( 'admin.php?page=yacht-inquiries' ) ) . '">'
|
||||
. __( 'Przejdź do listy zapytań', 'yacht-booking' ) . '</a></p>';
|
||||
|
||||
$admin_headers = $headers;
|
||||
$admin_headers[] = 'Reply-To: ' . $customer_name . ' <' . $customer_email . '>';
|
||||
|
||||
wp_mail( get_option( 'admin_email' ), $admin_subject, $admin_body, $admin_headers );
|
||||
update_post_meta( $inquiry_id, '_inquiry_admin_email_body', $admin_body );
|
||||
|
||||
// --- Customer confirmation email ---
|
||||
$customer_subject = sprintf(
|
||||
/* translators: 1: site name */
|
||||
__( 'Potwierdzenie zapytania o rezerwację - %s', 'yacht-booking' ),
|
||||
$site_name
|
||||
);
|
||||
|
||||
$customer_body = '<p>' . sprintf(
|
||||
/* translators: %s: customer name */
|
||||
__( 'Witaj %s,', 'yacht-booking' ),
|
||||
esc_html( $customer_name )
|
||||
) . '</p>';
|
||||
$customer_body .= '<p>' . __( 'Dziękujemy za przesłanie zapytania o rezerwację jachtu. Otrzymaliśmy Twoje zgłoszenie i skontaktujemy się z Tobą najszybciej jak to możliwe.', 'yacht-booking' ) . '</p>';
|
||||
$customer_body .= '<p><strong>' . __( 'Szczegóły zapytania:', 'yacht-booking' ) . '</strong><br>';
|
||||
$customer_body .= esc_html__( 'Jacht:', 'yacht-booking' ) . ' ' . esc_html( $yacht_name ) . '</p>';
|
||||
if ( $preferred_dates ) {
|
||||
$customer_body .= '<p>' . esc_html__( 'Preferowane terminy:', 'yacht-booking' ) . ' ' . esc_html( $preferred_dates ) . '</p>';
|
||||
}
|
||||
if ( $message ) {
|
||||
$customer_body .= '<p>' . esc_html__( 'Twoja wiadomość:', 'yacht-booking' ) . '<br>' . nl2br( esc_html( $message ) ) . '</p>';
|
||||
}
|
||||
$customer_body .= '<p>' . __( 'Pozdrawiamy,', 'yacht-booking' ) . '<br>' . esc_html( $site_name ) . '</p>';
|
||||
|
||||
wp_mail( $customer_email, $customer_subject, $customer_body, $headers );
|
||||
update_post_meta( $inquiry_id, '_inquiry_customer_email_body', $customer_body );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Installer Class
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installer class - handles plugin installation and database setup
|
||||
*/
|
||||
class Installer {
|
||||
|
||||
/**
|
||||
* Run installation
|
||||
*/
|
||||
public function install() {
|
||||
$this->create_tables();
|
||||
$this->create_options();
|
||||
$this->set_version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create custom database tables
|
||||
*/
|
||||
private function create_tables() {
|
||||
global $wpdb;
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
$table_name = $wpdb->prefix . 'yacht_availability';
|
||||
|
||||
$sql = "CREATE TABLE $table_name (
|
||||
id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
yacht_id bigint(20) UNSIGNED NOT NULL,
|
||||
date date NOT NULL,
|
||||
status varchar(20) NOT NULL DEFAULT 'available',
|
||||
booking_id bigint(20) UNSIGNED NULL,
|
||||
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY yacht_date (yacht_id, date),
|
||||
KEY yacht_id (yacht_id),
|
||||
KEY date (date),
|
||||
KEY status (status),
|
||||
KEY booking_id (booking_id)
|
||||
) $charset_collate;";
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
dbDelta( $sql );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default plugin options
|
||||
*/
|
||||
private function create_options() {
|
||||
$options = array(
|
||||
'yacht_booking_default_status' => 'pending',
|
||||
'yacht_booking_email_from_name' => get_bloginfo( 'name' ),
|
||||
'yacht_booking_email_from' => get_bloginfo( 'admin_email' ),
|
||||
'yacht_booking_date_format' => 'Y-m-d',
|
||||
'yacht_booking_currency_symbol' => 'zł',
|
||||
'yacht_booking_terms_page_id' => 0,
|
||||
'yacht_booking_enable_notifications' => 'yes',
|
||||
'yacht_booking_gcal_sync_enabled' => 'no',
|
||||
);
|
||||
|
||||
foreach ( $options as $key => $value ) {
|
||||
if ( false === get_option( $key ) ) {
|
||||
add_option( $key, $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plugin version
|
||||
*/
|
||||
private function set_version() {
|
||||
update_option( 'yacht_booking_version', YACHT_BOOKING_VERSION );
|
||||
update_option( 'yacht_booking_installed_at', current_time( 'mysql' ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin settings helper.
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking;
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings helper class.
|
||||
*/
|
||||
class Settings {
|
||||
|
||||
/**
|
||||
* Check if online booking is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_booking_enabled() {
|
||||
return '1' === get_option( 'yacht_booking_enabled', '1' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default booking status.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_default_status() {
|
||||
$status = get_option( 'yacht_booking_default_status', 'pending' );
|
||||
return in_array( $status, array( 'pending', 'confirmed' ), true ) ? $status : 'pending';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email from name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_email_from_name() {
|
||||
$value = get_option( 'yacht_booking_email_from_name', get_bloginfo( 'name' ) );
|
||||
return is_string( $value ) && '' !== $value ? $value : get_bloginfo( 'name' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email from address.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_email_from_address() {
|
||||
$value = get_option( 'yacht_booking_email_from', '' );
|
||||
|
||||
// Backward compatibility with older option name.
|
||||
if ( empty( $value ) ) {
|
||||
$value = get_option( 'yacht_booking_email_from_address', get_option( 'admin_email' ) );
|
||||
}
|
||||
|
||||
return is_email( $value ) ? $value : get_option( 'admin_email' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supported date formats.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_supported_date_formats() {
|
||||
return array( 'Y-m-d', 'd/m/Y', 'm/d/Y', 'd.m.Y' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_date_format() {
|
||||
$format = get_option( 'yacht_booking_date_format', 'Y-m-d' );
|
||||
return in_array( $format, self::get_supported_date_formats(), true ) ? $format : 'Y-m-d';
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date using plugin settings.
|
||||
*
|
||||
* @param string $date Date string.
|
||||
* @return string
|
||||
*/
|
||||
public static function format_date( $date ) {
|
||||
$timestamp = strtotime( (string) $date );
|
||||
return false !== $timestamp ? date_i18n( self::get_date_format(), $timestamp ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currency symbol.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_currency_symbol() {
|
||||
$symbol = get_option( 'yacht_booking_currency_symbol', 'zł' );
|
||||
$symbol = is_string( $symbol ) ? trim( $symbol ) : '';
|
||||
return '' !== $symbol ? $symbol : 'zł';
|
||||
}
|
||||
|
||||
/**
|
||||
* Format price using plugin settings.
|
||||
*
|
||||
* @param float $amount Amount.
|
||||
* @return string
|
||||
*/
|
||||
public static function format_price( $amount ) {
|
||||
return trim( number_format_i18n( (float) $amount, 2 ) . ' ' . self::get_currency_symbol() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get terms page ID.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function get_terms_page_id() {
|
||||
return absint( get_option( 'yacht_booking_terms_page_id', 0 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get terms page URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_terms_page_url() {
|
||||
$page_id = self::get_terms_page_id();
|
||||
if ( ! $page_id ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$url = get_permalink( $page_id );
|
||||
return $url ? $url : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
/**
|
||||
* Main Plugin Class
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Yacht Booking class - Singleton pattern
|
||||
*/
|
||||
class Yacht_Booking {
|
||||
|
||||
/**
|
||||
* Single instance of the class
|
||||
*
|
||||
* @var Yacht_Booking
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return Yacht_Booking
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->init_hooks();
|
||||
$this->load_dependencies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize WordPress hooks
|
||||
*/
|
||||
private function init_hooks() {
|
||||
// Register Custom Post Types
|
||||
add_action( 'init', array( $this, 'register_post_types' ), 10 );
|
||||
|
||||
// Enqueue scripts and styles
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ), 10 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ), 10 );
|
||||
|
||||
// Register REST API routes
|
||||
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ), 10 );
|
||||
|
||||
// Register Elementor widgets
|
||||
add_action( 'elementor/widgets/register', array( $this, 'register_elementor_widgets' ), 10 );
|
||||
|
||||
// Register shortcodes
|
||||
add_action( 'init', array( $this, 'register_shortcodes' ), 15 );
|
||||
|
||||
// Add custom capabilities
|
||||
add_action( 'admin_init', array( $this, 'add_custom_capabilities' ), 10 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugin dependencies
|
||||
*/
|
||||
private function load_dependencies() {
|
||||
// Load CPT handlers
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'includes/class-yacht.php';
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'includes/class-booking.php';
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'includes/class-availability.php';
|
||||
|
||||
// Load admin classes
|
||||
if ( is_admin() ) {
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'admin/class-admin.php';
|
||||
Admin::get_instance();
|
||||
}
|
||||
|
||||
// Load Google Calendar integration
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-oauth-handler.php';
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-gcal-service.php';
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-sync-controller.php';
|
||||
|
||||
// Initialize sync controller
|
||||
\YachtBooking\Integrations\GoogleCalendar\Sync_Controller::get_instance();
|
||||
|
||||
// Register cron actions
|
||||
\YachtBooking\Integrations\GoogleCalendar\Sync_Controller::register_cron_actions();
|
||||
|
||||
// Load iCal integration
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/ical/class-ical-feed.php';
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/ical/class-ical-import.php';
|
||||
|
||||
\YachtBooking\Integrations\ICal\ICal_Feed::register();
|
||||
\YachtBooking\Integrations\ICal\ICal_Import::register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Custom Post Types
|
||||
*/
|
||||
public function register_post_types() {
|
||||
// Register Yacht CPT
|
||||
Yacht::register();
|
||||
|
||||
// Register Booking CPT
|
||||
Booking::register();
|
||||
|
||||
// Register Inquiry CPT
|
||||
Inquiry::register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend assets
|
||||
*/
|
||||
public function enqueue_frontend_assets() {
|
||||
// Only load on pages with yacht calendar
|
||||
if ( ! $this->should_load_frontend_assets() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FullCalendar CSS
|
||||
wp_enqueue_style(
|
||||
'fullcalendar',
|
||||
'https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.css',
|
||||
array(),
|
||||
'6.1.10'
|
||||
);
|
||||
|
||||
// Plugin CSS
|
||||
wp_enqueue_style(
|
||||
'yacht-booking-calendar',
|
||||
YACHT_BOOKING_PLUGIN_URL . 'frontend/assets/css/calendar.css',
|
||||
array( 'fullcalendar' ),
|
||||
YACHT_BOOKING_VERSION
|
||||
);
|
||||
|
||||
// FullCalendar JS
|
||||
wp_enqueue_script(
|
||||
'fullcalendar',
|
||||
'https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.js',
|
||||
array(),
|
||||
'6.1.10',
|
||||
true
|
||||
);
|
||||
|
||||
// FullCalendar Polish locale
|
||||
wp_enqueue_script(
|
||||
'fullcalendar-pl',
|
||||
'https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.10/locales/pl.global.min.js',
|
||||
array( 'fullcalendar' ),
|
||||
'6.1.10',
|
||||
true
|
||||
);
|
||||
|
||||
// Plugin JS
|
||||
wp_enqueue_script(
|
||||
'yacht-booking-calendar',
|
||||
YACHT_BOOKING_PLUGIN_URL . 'frontend/assets/js/calendar.js',
|
||||
array( 'jquery', 'fullcalendar', 'fullcalendar-pl' ),
|
||||
YACHT_BOOKING_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize script
|
||||
wp_localize_script(
|
||||
'yacht-booking-calendar',
|
||||
'yachtBookingData',
|
||||
array(
|
||||
'apiUrl' => esc_url_raw( rest_url( 'yacht-booking/v1' ) ),
|
||||
'nonce' => wp_create_nonce( 'wp_rest' ),
|
||||
'bookingEnabled' => Settings::is_booking_enabled(),
|
||||
'i18n' => array(
|
||||
'loading' => __( 'Ładowanie...', 'yacht-booking' ),
|
||||
'submitBooking' => __( 'Wyślij rezerwację', 'yacht-booking' ),
|
||||
'submitting' => __( 'Wysyłanie...', 'yacht-booking' ),
|
||||
'successTitle' => __( 'Sukces!', 'yacht-booking' ),
|
||||
'successMessage' => __( 'Twoja rezerwacja została wysłana. Skontaktujemy się z Tobą wkrótce.', 'yacht-booking' ),
|
||||
'errorTitle' => __( 'Błąd!', 'yacht-booking' ),
|
||||
'errorMessage' => __( 'Wystąpił błąd podczas wysyłania rezerwacji. Spróbuj ponownie.', 'yacht-booking' ),
|
||||
'selectDates' => __( 'Wybierz daty na kalendarzu', 'yacht-booking' ),
|
||||
'invalidDateRange' => __( 'Nieprawidłowy zakres dat', 'yacht-booking' ),
|
||||
'unavailableDatesSelected' => __( 'Wybrane daty zawierają niedostępne terminy. Proszę wybrać inne daty.', 'yacht-booking' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin assets
|
||||
*/
|
||||
public function enqueue_admin_assets( $hook ) {
|
||||
// Only load on yacht booking pages
|
||||
if ( strpos( $hook, 'yacht-bookings' ) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'yacht-booking-admin',
|
||||
YACHT_BOOKING_PLUGIN_URL . 'admin/assets/css/admin.css',
|
||||
array(),
|
||||
YACHT_BOOKING_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'yacht-booking-admin',
|
||||
YACHT_BOOKING_PLUGIN_URL . 'admin/assets/js/admin.js',
|
||||
array( 'jquery' ),
|
||||
YACHT_BOOKING_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'yacht-booking-admin',
|
||||
'yachtBookingAdmin',
|
||||
array(
|
||||
'apiUrl' => esc_url_raw( rest_url( 'yacht-booking/v1' ) ),
|
||||
'nonce' => wp_create_nonce( 'wp_rest' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if frontend assets should be loaded
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function should_load_frontend_assets() {
|
||||
global $post;
|
||||
|
||||
// Always load if Elementor is in edit mode
|
||||
if ( class_exists( '\Elementor\Plugin' ) && \Elementor\Plugin::$instance->preview->is_preview_mode() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if post contains yacht calendar shortcode or widget
|
||||
if ( $post && ( has_shortcode( $post->post_content, 'yacht_calendar' ) || $this->has_yacht_calendar_widget( $post->ID ) ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if post has yacht calendar Elementor widget
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return bool
|
||||
*/
|
||||
private function has_yacht_calendar_widget( $post_id ) {
|
||||
if ( ! class_exists( '\Elementor\Plugin' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$document = \Elementor\Plugin::$instance->documents->get( $post_id );
|
||||
if ( ! $document ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $document->get_elements_data();
|
||||
return $this->find_widget_recursive( $data, 'yacht-calendar' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Find widget recursively in Elementor data
|
||||
*
|
||||
* @param array $elements Elements data.
|
||||
* @param string $widget_name Widget name to find.
|
||||
* @return bool
|
||||
*/
|
||||
private function find_widget_recursive( $elements, $widget_name ) {
|
||||
foreach ( $elements as $element ) {
|
||||
if ( isset( $element['widgetType'] ) && $element['widgetType'] === $widget_name ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! empty( $element['elements'] ) ) {
|
||||
if ( $this->find_widget_recursive( $element['elements'], $widget_name ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST API routes
|
||||
*/
|
||||
public function register_rest_routes() {
|
||||
// Load REST controllers
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'api/class-rest-controller.php';
|
||||
|
||||
$controller = new Rest_Controller();
|
||||
$controller->register_routes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Elementor widgets
|
||||
*
|
||||
* @param object $widgets_manager Elementor widgets manager.
|
||||
*/
|
||||
public function register_elementor_widgets( $widgets_manager ) {
|
||||
// Load widget class
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'frontend/class-calendar-widget.php';
|
||||
|
||||
// Register widget
|
||||
$widgets_manager->register( new Calendar_Widget() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register shortcodes
|
||||
*/
|
||||
public function register_shortcodes() {
|
||||
// Load shortcode class
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'frontend/class-shortcode.php';
|
||||
|
||||
// Initialize shortcode handler
|
||||
Shortcode::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom capabilities
|
||||
*/
|
||||
public function add_custom_capabilities() {
|
||||
// Only run once
|
||||
if ( get_option( 'yacht_booking_capabilities_added' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$admin = get_role( 'administrator' );
|
||||
|
||||
if ( $admin ) {
|
||||
$capabilities = array(
|
||||
'yacht_booking_manage_yachts',
|
||||
'yacht_booking_manage_bookings',
|
||||
'yacht_booking_manage_settings',
|
||||
);
|
||||
|
||||
foreach ( $capabilities as $cap ) {
|
||||
$admin->add_cap( $cap );
|
||||
}
|
||||
|
||||
update_option( 'yacht_booking_capabilities_added', true );
|
||||
}
|
||||
}
|
||||
}
|
||||
155
wp-content/plugins/yacht-booking-system/includes/class-yacht.php
Normal file
155
wp-content/plugins/yacht-booking-system/includes/class-yacht.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* Yacht Custom Post Type
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Yacht CPT class
|
||||
*/
|
||||
class Yacht {
|
||||
|
||||
/**
|
||||
* Register custom post type
|
||||
*/
|
||||
public static function register() {
|
||||
$labels = array(
|
||||
'name' => __( 'Jachty', 'yacht-booking' ),
|
||||
'singular_name' => __( 'Jacht', 'yacht-booking' ),
|
||||
'menu_name' => __( 'Jachty', 'yacht-booking' ),
|
||||
'name_admin_bar' => __( 'Jacht', 'yacht-booking' ),
|
||||
'add_new' => __( 'Dodaj nowy', 'yacht-booking' ),
|
||||
'add_new_item' => __( 'Dodaj nowy jacht', 'yacht-booking' ),
|
||||
'new_item' => __( 'Nowy jacht', 'yacht-booking' ),
|
||||
'edit_item' => __( 'Edytuj jacht', 'yacht-booking' ),
|
||||
'view_item' => __( 'Zobacz jacht', 'yacht-booking' ),
|
||||
'all_items' => __( 'Wszystkie jachty', 'yacht-booking' ),
|
||||
'search_items' => __( 'Szukaj jachtów', 'yacht-booking' ),
|
||||
'parent_item_colon' => __( 'Nadrzędny jacht:', 'yacht-booking' ),
|
||||
'not_found' => __( 'Nie znaleziono jachtów', 'yacht-booking' ),
|
||||
'not_found_in_trash' => __( 'Nie znaleziono jachtów w koszu', 'yacht-booking' ),
|
||||
'featured_image' => __( 'Zdjęcie jachtu', 'yacht-booking' ),
|
||||
'set_featured_image' => __( 'Ustaw zdjęcie jachtu', 'yacht-booking' ),
|
||||
'remove_featured_image' => __( 'Usuń zdjęcie jachtu', 'yacht-booking' ),
|
||||
'use_featured_image' => __( 'Użyj jako zdjęcie jachtu', 'yacht-booking' ),
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'labels' => $labels,
|
||||
'description' => __( 'Jachty dostępne do rezerwacji', 'yacht-booking' ),
|
||||
'public' => false,
|
||||
'publicly_queryable' => false,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => false, // Custom menu będzie w class-admin.php
|
||||
'show_in_rest' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => false,
|
||||
'capability_type' => 'post',
|
||||
'capabilities' => array(
|
||||
'edit_post' => 'yacht_booking_manage_yachts',
|
||||
'read_post' => 'yacht_booking_manage_yachts',
|
||||
'delete_post' => 'yacht_booking_manage_yachts',
|
||||
'edit_posts' => 'yacht_booking_manage_yachts',
|
||||
'edit_others_posts' => 'yacht_booking_manage_yachts',
|
||||
'publish_posts' => 'yacht_booking_manage_yachts',
|
||||
'read_private_posts' => 'yacht_booking_manage_yachts',
|
||||
'delete_posts' => 'yacht_booking_manage_yachts',
|
||||
),
|
||||
'has_archive' => false,
|
||||
'hierarchical' => false,
|
||||
'menu_position' => null,
|
||||
'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
|
||||
);
|
||||
|
||||
register_post_type( 'yacht', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get yacht capacity
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @return int
|
||||
*/
|
||||
public static function get_capacity( $yacht_id ) {
|
||||
return (int) get_post_meta( $yacht_id, '_yacht_capacity', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get yacht price per day
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @return float
|
||||
*/
|
||||
public static function get_price_per_day( $yacht_id ) {
|
||||
return (float) get_post_meta( $yacht_id, '_yacht_price_per_day', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get yacht Google Calendar ID
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_gcal_id( $yacht_id ) {
|
||||
return get_post_meta( $yacht_id, '_yacht_gcal_id', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get yacht features
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_features( $yacht_id ) {
|
||||
$features = get_post_meta( $yacht_id, '_yacht_features', true );
|
||||
return is_array( $features ) ? $features : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update yacht capacity
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param int $capacity Capacity.
|
||||
*/
|
||||
public static function update_capacity( $yacht_id, $capacity ) {
|
||||
update_post_meta( $yacht_id, '_yacht_capacity', (int) $capacity );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update yacht price per day
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param float $price Price per day.
|
||||
*/
|
||||
public static function update_price_per_day( $yacht_id, $price ) {
|
||||
update_post_meta( $yacht_id, '_yacht_price_per_day', (float) $price );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update yacht Google Calendar ID
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param string $gcal_id Google Calendar ID.
|
||||
*/
|
||||
public static function update_gcal_id( $yacht_id, $gcal_id ) {
|
||||
update_post_meta( $yacht_id, '_yacht_gcal_id', sanitize_text_field( $gcal_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update yacht features
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param array $features Features array.
|
||||
*/
|
||||
public static function update_features( $yacht_id, $features ) {
|
||||
update_post_meta( $yacht_id, '_yacht_features', is_array( $features ) ? $features : array() );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user