update
This commit is contained in:
@@ -64,27 +64,6 @@ class Admin {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load Google Calendar classes if needed
|
||||
if ( file_exists( YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-oauth-handler.php' ) ) {
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-oauth-handler.php';
|
||||
}
|
||||
if ( file_exists( YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-gcal-service.php' ) ) {
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-gcal-service.php';
|
||||
}
|
||||
|
||||
// Handle OAuth callback
|
||||
if ( isset( $_GET['gcal_callback'] ) && isset( $_GET['code'] ) ) {
|
||||
$this->handle_oauth_callback();
|
||||
}
|
||||
|
||||
// Handle disconnect
|
||||
if ( isset( $_POST['yacht_booking_disconnect_gcal'] ) ) {
|
||||
check_admin_referer( 'yacht_booking_disconnect_gcal' );
|
||||
\YachtBooking\Integrations\GoogleCalendar\OAuth_Handler::disconnect();
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=google-calendar&disconnected=1' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
// Handle general settings save
|
||||
if ( isset( $_POST['yacht_booking_save_settings'] ) ) {
|
||||
check_admin_referer( 'yacht_booking_save_settings' );
|
||||
@@ -105,10 +84,30 @@ class Admin {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Handle credentials save
|
||||
if ( isset( $_POST['yacht_booking_save_gcal_credentials'] ) ) {
|
||||
check_admin_referer( 'yacht_booking_save_gcal_credentials' );
|
||||
$this->save_gcal_credentials();
|
||||
// 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 );
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,24 +147,6 @@ class Admin {
|
||||
<?php
|
||||
}
|
||||
|
||||
// Success: Connected to Google Calendar
|
||||
if ( isset( $_GET['connected'] ) ) {
|
||||
?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><strong><?php esc_html_e( 'Połączono z Google Calendar pomyślnie!', 'yacht-booking' ); ?></strong></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Success: Disconnected from Google Calendar
|
||||
if ( isset( $_GET['disconnected'] ) ) {
|
||||
?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><strong><?php esc_html_e( 'Rozłączono z Google Calendar.', 'yacht-booking' ); ?></strong></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Error
|
||||
if ( isset( $_GET['error'] ) ) {
|
||||
?>
|
||||
@@ -174,6 +155,38 @@ class Admin {
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
if ( isset( $_GET['global_ical_saved'] ) ) {
|
||||
?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><strong><?php esc_html_e( 'Globalne ustawienia iCal zostały zapisane.', 'yacht-booking' ); ?></strong></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
if ( isset( $_GET['token_regenerated'] ) ) {
|
||||
?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><strong><?php esc_html_e( 'Wygenerowano nowy token globalnego feed iCal. Poprzedni URL został unieważniony — zaktualizuj subskrypcję w Google Calendar.', 'yacht-booking' ); ?></strong></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
if ( isset( $_GET['global_import_done'] ) ) {
|
||||
?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><strong><?php esc_html_e( 'Globalny import iCal wykonany. Sprawdź listę rezerwacji.', 'yacht-booking' ); ?></strong></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
if ( isset( $_GET['global_import_failed'] ) ) {
|
||||
?>
|
||||
<div class="notice notice-error is-dismissible">
|
||||
<p><strong><?php esc_html_e( 'Globalny import iCal nie powiódł się. Sprawdź czy URL jest poprawny i logi serwera.', 'yacht-booking' ); ?></strong></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,7 +377,6 @@ class Admin {
|
||||
// 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'] ) ) : '';
|
||||
$gcal_id = isset( $data['yacht_gcal_id'] ) ? sanitize_text_field( wp_unslash( $data['yacht_gcal_id'] ) ) : '';
|
||||
|
||||
if ( empty( $title ) ) {
|
||||
return false;
|
||||
@@ -389,12 +401,9 @@ class Admin {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save meta
|
||||
Yacht::update_gcal_id( $saved_id, $gcal_id );
|
||||
|
||||
// Save iCal import URL
|
||||
$ical_import_url = isset( $data['yacht_ical_import_url'] ) ? esc_url_raw( wp_unslash( $data['yacht_ical_import_url'] ) ) : '';
|
||||
\YachtBooking\Integrations\ICal\ICal_Import::set_import_url( $saved_id, $ical_import_url );
|
||||
// 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 );
|
||||
|
||||
return $saved_id;
|
||||
}
|
||||
@@ -1221,181 +1230,115 @@ class Admin {
|
||||
* Render Google Calendar settings tab
|
||||
*/
|
||||
private function render_google_calendar_settings() {
|
||||
// Check if classes exist
|
||||
if ( ! class_exists( '\YachtBooking\Integrations\GoogleCalendar\OAuth_Handler' ) ||
|
||||
! class_exists( '\YachtBooking\Integrations\GoogleCalendar\GCal_Service' ) ) {
|
||||
?>
|
||||
<div class="notice notice-error">
|
||||
<p><?php esc_html_e( 'Błąd: Klasy Google Calendar nie zostały załadowane. Skontaktuj się z administratorem.', 'yacht-booking' ); ?></p>
|
||||
</div>
|
||||
<?php
|
||||
return;
|
||||
}
|
||||
|
||||
$is_connected = \YachtBooking\Integrations\GoogleCalendar\OAuth_Handler::is_connected();
|
||||
$connected_email = $is_connected ? \YachtBooking\Integrations\GoogleCalendar\OAuth_Handler::get_connected_email() : false;
|
||||
$credentials = \YachtBooking\Integrations\GoogleCalendar\OAuth_Handler::get_credentials();
|
||||
$calendar_id = \YachtBooking\Integrations\GoogleCalendar\GCal_Service::get_calendar_id();
|
||||
|
||||
?>
|
||||
<h2><?php esc_html_e( 'Konfiguracja Google Calendar', 'yacht-booking' ); ?></h2>
|
||||
<h2><?php esc_html_e( 'Synchronizacja z Google Calendar (iCal)', 'yacht-booking' ); ?></h2>
|
||||
|
||||
<?php if ( ! $is_connected ) : ?>
|
||||
<!-- Step 1: Enter credentials -->
|
||||
<div class="card">
|
||||
<h3><?php esc_html_e( 'Krok 1: Dodaj dane OAuth', 'yacht-booking' ); ?></h3>
|
||||
<p><?php esc_html_e( 'Aby połączyć się z Google Calendar, musisz utworzyć projekt w Google Cloud Console i pobrać Client ID oraz Client Secret.', 'yacht-booking' ); ?></p>
|
||||
|
||||
<ol>
|
||||
<li><?php esc_html_e( 'Przejdź do', 'yacht-booking' ); ?> <a href="https://console.cloud.google.com/" target="_blank">Google Cloud Console</a></li>
|
||||
<li><?php esc_html_e( 'Utwórz nowy projekt lub wybierz istniejący', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Włącz Google Calendar API', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Utwórz OAuth 2.0 Client ID (typ: Web application)', 'yacht-booking' ); ?></li>
|
||||
<li>
|
||||
<?php esc_html_e( 'Dodaj Authorized redirect URI:', 'yacht-booking' ); ?>
|
||||
<br>
|
||||
<code style="background: #f0f0f0; padding: 5px 10px; display: inline-block; margin: 5px 0;">https://jachty.pagedev.pl/wp-admin/admin.php?page=yacht-bookings-settings&tab=google-calendar&gcal_callback=1</code>
|
||||
<br>
|
||||
<button type="button" class="button button-small" onclick="navigator.clipboard.writeText('https://jachty.pagedev.pl/wp-admin/admin.php?page=yacht-bookings-settings&tab=google-calendar&gcal_callback=1'); this.textContent='Skopiowano!';">
|
||||
<?php esc_html_e( 'Kopiuj', 'yacht-booking' ); ?>
|
||||
</button>
|
||||
</li>
|
||||
<li><?php esc_html_e( 'Skopiuj Client ID i Client Secret', 'yacht-booking' ); ?></li>
|
||||
</ol>
|
||||
|
||||
<form method="post" action="">
|
||||
<?php wp_nonce_field( 'yacht_booking_save_gcal_credentials' ); ?>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="gcal_client_id"><?php esc_html_e( 'Client ID', 'yacht-booking' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" name="gcal_client_id" id="gcal_client_id" class="regular-text" value="<?php echo esc_attr( isset( $credentials['client_id'] ) ? $credentials['client_id'] : '' ); ?>" required>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="gcal_client_secret"><?php esc_html_e( 'Client Secret', 'yacht-booking' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" name="gcal_client_secret" id="gcal_client_secret" class="regular-text" value="<?php echo esc_attr( isset( $credentials['client_secret'] ) ? $credentials['client_secret'] : '' ); ?>" required>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="submit">
|
||||
<input type="submit" name="yacht_booking_save_gcal_credentials" class="button-primary" value="<?php esc_attr_e( 'Zapisz i przejdź do autoryzacji', 'yacht-booking' ); ?>">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if ( $credentials ) : ?>
|
||||
<!-- Step 2: Authorize -->
|
||||
<div class="card" style="margin-top: 20px;">
|
||||
<h3><?php esc_html_e( 'Krok 2: Autoryzuj połączenie', 'yacht-booking' ); ?></h3>
|
||||
<p><?php esc_html_e( 'Kliknij poniższy przycisk aby autoryzować dostęp do Google Calendar.', 'yacht-booking' ); ?></p>
|
||||
|
||||
<?php
|
||||
$auth_url = \YachtBooking\Integrations\GoogleCalendar\OAuth_Handler::get_auth_url();
|
||||
if ( $auth_url ) :
|
||||
// Parse URL to extract redirect_uri for debugging
|
||||
$parsed_url = wp_parse_url( $auth_url );
|
||||
parse_str( isset( $parsed_url['query'] ) ? $parsed_url['query'] : '', $query_params );
|
||||
$redirect_uri_sent = isset( $query_params['redirect_uri'] ) ? $query_params['redirect_uri'] : '';
|
||||
?>
|
||||
<p>
|
||||
<a href="<?php echo esc_url( $auth_url ); ?>" class="button button-primary button-hero">
|
||||
<?php esc_html_e( 'Połącz z Google Calendar', 'yacht-booking' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<!-- DEBUG: Show exact redirect_uri being sent -->
|
||||
<div style="margin-top: 15px; padding: 10px; background: #fff3cd; border-left: 4px solid #ffc107;">
|
||||
<strong>DEBUG - Redirect URI wysyłany do Google:</strong>
|
||||
<br>
|
||||
<code style="display: block; margin-top: 5px; padding: 5px; background: white; word-break: break-all;">
|
||||
<?php echo esc_html( $redirect_uri_sent ); ?>
|
||||
</code>
|
||||
<small style="color: #856404;">Porównaj dokładnie z tym co masz w Google Cloud Console (character-by-character)</small>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<p class="description" style="color: #d63638;">
|
||||
<?php esc_html_e( 'Błąd generowania URL autoryzacji. Sprawdź Client ID i Secret.', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php else : ?>
|
||||
<!-- Connected status -->
|
||||
<div class="card">
|
||||
<h3><?php esc_html_e( 'Status połączenia', 'yacht-booking' ); ?></h3>
|
||||
<p>
|
||||
<span class="dashicons dashicons-yes-alt" style="color: #46b450; font-size: 20px;"></span>
|
||||
<strong><?php esc_html_e( 'Połączono z Google Calendar', 'yacht-booking' ); ?></strong>
|
||||
</p>
|
||||
<?php if ( $connected_email ) : ?>
|
||||
<p><?php esc_html_e( 'Konto:', 'yacht-booking' ); ?> <code><?php echo esc_html( $connected_email ); ?></code></p>
|
||||
<?php endif; ?>
|
||||
<p><?php esc_html_e( 'Kalendarz:', 'yacht-booking' ); ?> <code><?php echo esc_html( $calendar_id ); ?></code></p>
|
||||
|
||||
<form method="post" action="" onsubmit="return confirm('<?php esc_attr_e( 'Czy na pewno chcesz rozłączyć Google Calendar?', 'yacht-booking' ); ?>')">
|
||||
<?php wp_nonce_field( 'yacht_booking_disconnect_gcal' ); ?>
|
||||
<p class="submit">
|
||||
<input type="submit" name="yacht_booking_disconnect_gcal" class="button button-secondary" value="<?php esc_attr_e( 'Rozłącz', 'yacht-booking' ); ?>">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Manual sync -->
|
||||
<div class="card" style="margin-top: 20px;">
|
||||
<h3><?php esc_html_e( 'Synchronizacja ręczna', 'yacht-booking' ); ?></h3>
|
||||
<p><?php esc_html_e( 'Synchronizacja odbywa się automatycznie, ale możesz wymusić synchronizację ręcznie.', 'yacht-booking' ); ?></p>
|
||||
<p>
|
||||
<button type="button" id="yacht-booking-manual-sync" class="button" data-nonce="<?php echo esc_attr( wp_create_nonce( 'yacht_booking_manual_sync' ) ); ?>">
|
||||
<?php esc_html_e( 'Synchronizuj teraz', 'yacht-booking' ); ?>
|
||||
</button>
|
||||
<span id="yacht-booking-sync-status" style="margin-left: 10px;"></span>
|
||||
</p>
|
||||
<div id="yacht-booking-sync-result" style="margin-top: 10px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top: 20px;">
|
||||
<h3><?php esc_html_e( 'Instrukcja: dodawanie rezerwacji po stronie Google Calendar', 'yacht-booking' ); ?></h3>
|
||||
<p><?php esc_html_e( 'Poniższe kroki pozwalają dodać wydarzenie w Google Calendar tak, aby po synchronizacji zostało zablokowane w kalendarzu WordPress.', 'yacht-booking' ); ?></p>
|
||||
<ol style="margin-left: 20px;">
|
||||
<li><?php esc_html_e( 'Dodaj wydarzenie w tym samym kalendarzu, który jest połączony z pluginem (widoczny wyżej jako \"Kalendarz\").', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Ustaw datę rozpoczęcia i datę zakończenia (wydarzenie całodniowe lub godzinowe).', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Zapisz wydarzenie w Google Calendar.', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Kliknij \"Synchronizuj teraz\" albo poczekaj na automatyczną synchronizację (cron uruchamiany co godzinę).', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Po synchronizacji wydarzenie z Google zostanie zaimportowane do panelu jako rezerwacja techniczna (źródło: Google Calendar), którą można usunąć z poziomu listy rezerwacji.', 'yacht-booking' ); ?></li>
|
||||
</ol>
|
||||
<p class="description"><?php esc_html_e( 'Uwaga: import obejmuje wydarzenia od teraz do +1 roku i pomija wydarzenia, które zostały utworzone przez ten plugin (aby uniknąć duplikatów).', 'yacht-booking' ); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
// =====================================================================
|
||||
// Globalna synchronizacja iCal (jeden wspólny kalendarz Google,
|
||||
// podział na jachty po prefiksie nazwy w tytule eventu).
|
||||
// Działa niezależnie od OAuth — wystarczy publiczny URL kalendarza Google.
|
||||
// =====================================================================
|
||||
$global_import_url = (string) get_option( 'yacht_booking_global_ical_import_url', '' );
|
||||
$global_export_url = \YachtBooking\Integrations\ICal\ICal_Feed::get_global_feed_url();
|
||||
$last_global_run = (string) get_option( 'yacht_booking_global_ical_last_import', '' );
|
||||
?>
|
||||
<div class="card" style="margin-top: 30px;">
|
||||
<h3><?php esc_html_e( 'Globalna synchronizacja iCal (jeden wspólny kalendarz)', 'yacht-booking' ); ?></h3>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Mechanizm dwukierunkowej synchronizacji wszystkich jachtów z jednym kalendarzem Google, bez OAuth. Plugin rozpoznaje jacht po prefiksie tytułu eventu w formacie: "Nazwa jachtu - opis" (np. "Maja - Kowalski 5 osób"). Eventy bez rozpoznanego prefiksu są ignorowane.', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
|
||||
/**
|
||||
* Handle OAuth callback
|
||||
*/
|
||||
private function handle_oauth_callback() {
|
||||
if ( ! isset( $_GET['code'] ) ) {
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=google-calendar&error=1' ) );
|
||||
exit;
|
||||
}
|
||||
<h4><?php esc_html_e( 'Krok 1: Dodaj feed pluginu jako kalendarz w Google', 'yacht-booking' ); ?></h4>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Skopiuj poniższy URL i dodaj go w Google Calendar: "Inne kalendarze" → "+ Dodaj" → "Z URL-a". Rezerwacje ze strony pojawią się w Twoim Google Calendar (Google odświeża subskrypcję co kilka godzin).', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
<p>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value="<?php echo esc_attr( $global_export_url ); ?>"
|
||||
class="regular-text code"
|
||||
id="yacht-global-ical-export-url"
|
||||
style="width: 100%; max-width: 600px;"
|
||||
onclick="this.select();"
|
||||
/>
|
||||
<button type="button" class="button" onclick="navigator.clipboard.writeText(document.getElementById('yacht-global-ical-export-url').value); this.textContent='<?php echo esc_js( __( 'Skopiowano!', 'yacht-booking' ) ); ?>';">
|
||||
<?php esc_html_e( 'Kopiuj', 'yacht-booking' ); ?>
|
||||
</button>
|
||||
</p>
|
||||
<form method="post" action="" style="margin-top: 10px;" onsubmit="return confirm('<?php echo esc_js( __( 'Wygenerować nowy token? Poprzedni URL przestanie działać.', 'yacht-booking' ) ); ?>')">
|
||||
<?php wp_nonce_field( 'yacht_booking_regenerate_global_ical_token' ); ?>
|
||||
<button type="submit" name="yacht_booking_regenerate_global_ical_token" class="button button-secondary">
|
||||
<?php esc_html_e( 'Wygeneruj nowy token', 'yacht-booking' ); ?>
|
||||
</button>
|
||||
<span class="description" style="margin-left: 10px;">
|
||||
<?php esc_html_e( 'Unieważnia bieżący URL — używaj gdy podejrzewasz wyciek.', 'yacht-booking' ); ?>
|
||||
</span>
|
||||
</form>
|
||||
|
||||
$code = sanitize_text_field( wp_unslash( $_GET['code'] ) );
|
||||
$result = \YachtBooking\Integrations\GoogleCalendar\OAuth_Handler::authenticate( $code );
|
||||
<h4 style="margin-top: 25px;"><?php esc_html_e( 'Krok 2: Wklej iCal URL Twojego kalendarza Google', 'yacht-booking' ); ?></h4>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'W Google Calendar otwórz ustawienia kalendarza → "Tajny adres w formacie iCal" → skopiuj URL i wklej poniżej. Plugin pobiera ten kalendarz co godzinę i importuje rozpoznane eventy jako blokady.', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
<form method="post" action="">
|
||||
<?php wp_nonce_field( 'yacht_booking_save_global_ical' ); ?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="global_ical_import_url"><?php esc_html_e( 'iCal Import URL', 'yacht-booking' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="url"
|
||||
name="global_ical_import_url"
|
||||
id="global_ical_import_url"
|
||||
class="regular-text"
|
||||
value="<?php echo esc_attr( $global_import_url ); ?>"
|
||||
placeholder="https://calendar.google.com/calendar/ical/..."
|
||||
style="width: 100%; max-width: 600px;"
|
||||
/>
|
||||
<?php if ( $last_global_run ) : ?>
|
||||
<p class="description">
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s: timestamp */
|
||||
esc_html__( 'Ostatni import: %s', 'yacht-booking' ),
|
||||
esc_html( $last_global_run )
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="submit">
|
||||
<input type="submit" name="yacht_booking_save_global_ical" class="button-primary" value="<?php esc_attr_e( 'Zapisz URL', 'yacht-booking' ); ?>">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
if ( $result ) {
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=google-calendar&connected=1' ) );
|
||||
} else {
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=google-calendar&error=1' ) );
|
||||
}
|
||||
exit;
|
||||
<h4 style="margin-top: 25px;"><?php esc_html_e( 'Krok 3: Importuj teraz (opcjonalnie)', 'yacht-booking' ); ?></h4>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Cron godzinny pobiera kalendarz automatycznie. Możesz wymusić import ręcznie:', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
<form method="post" action="">
|
||||
<?php wp_nonce_field( 'yacht_booking_run_global_ical_import' ); ?>
|
||||
<button type="submit" name="yacht_booking_run_global_ical_import" class="button">
|
||||
<?php esc_html_e( 'Importuj teraz', 'yacht-booking' ); ?>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<h4 style="margin-top: 25px;"><?php esc_html_e( 'Jak to działa', 'yacht-booking' ); ?></h4>
|
||||
<ul style="margin-left: 20px; list-style: disc;">
|
||||
<li><?php esc_html_e( 'Tytuły eventów w Google Calendar muszą mieć format: "Nazwa jachtu - dowolny opis" (separator to spacja-myślnik-spacja).', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Dopasowanie po pełnej nazwie jachtu lub jego aliasie (pole "Alias dla Google Calendar" w edycji jachtu).', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Eventy bez separatora albo z nieznanym prefiksem są ignorowane (nie tworzą blokad).', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Usunięcie eventu w Google Calendar usuwa odpowiadającą blokadę po następnym imporcie.', 'yacht-booking' ); ?></li>
|
||||
<li><?php esc_html_e( 'Eventy zaimportowane z Google nie są wysyłane z powrotem (anti-loop).', 'yacht-booking' ); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1441,23 +1384,6 @@ class Admin {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Google Calendar credentials
|
||||
*/
|
||||
private function save_gcal_credentials() {
|
||||
if ( isset( $_POST['gcal_client_id'] ) && isset( $_POST['gcal_client_secret'] ) ) {
|
||||
$credentials = array(
|
||||
'client_id' => sanitize_text_field( wp_unslash( $_POST['gcal_client_id'] ) ),
|
||||
'client_secret' => sanitize_text_field( wp_unslash( $_POST['gcal_client_secret'] ) ),
|
||||
);
|
||||
|
||||
\YachtBooking\Integrations\GoogleCalendar\OAuth_Handler::save_credentials( $credentials );
|
||||
}
|
||||
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=yacht-bookings-settings&tab=google-calendar&saved=1' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process booking actions (approve, cancel, delete)
|
||||
*/
|
||||
|
||||
@@ -44,7 +44,6 @@ class Yacht_List_Table extends \WP_List_Table {
|
||||
return array(
|
||||
'cb' => '<input type="checkbox" />',
|
||||
'title' => __( 'Nazwa jachtu', 'yacht-booking' ),
|
||||
'gcal' => __( 'Google Calendar', 'yacht-booking' ),
|
||||
'bookings' => __( 'Rezerwacje', 'yacht-booking' ),
|
||||
'date' => __( 'Data utworzenia', 'yacht-booking' ),
|
||||
);
|
||||
@@ -163,28 +162,6 @@ class Yacht_List_Table extends \WP_List_Table {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Column Google Calendar
|
||||
*
|
||||
* @param object $item Item.
|
||||
* @return string
|
||||
*/
|
||||
public function column_gcal( $item ) {
|
||||
$gcal_id = Yacht::get_gcal_id( $item->ID );
|
||||
|
||||
if ( $gcal_id ) {
|
||||
return sprintf(
|
||||
'<span class="gcal-status connected"><span class="dashicons dashicons-yes-alt"></span> %s</span>',
|
||||
esc_html__( 'Połączony', 'yacht-booking' )
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<span class="gcal-status disconnected"><span class="dashicons dashicons-dismiss"></span> %s</span>',
|
||||
esc_html__( 'Niepołączony', 'yacht-booking' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Column bookings count
|
||||
*
|
||||
|
||||
@@ -11,12 +11,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
}
|
||||
|
||||
// Get yacht data
|
||||
$title = $yacht ? $yacht->post_title : '';
|
||||
$content = $yacht ? $yacht->post_content : '';
|
||||
$gcal_id = $yacht ? \YachtBooking\Yacht::get_gcal_id( $yacht->ID ) : '';
|
||||
$ical_import_url = $yacht ? \YachtBooking\Integrations\ICal\ICal_Import::get_import_url( $yacht->ID ) : '';
|
||||
$ical_feed_url = $yacht ? \YachtBooking\Integrations\ICal\ICal_Feed::get_feed_url( $yacht->ID ) : '';
|
||||
$ical_last_import = $yacht ? \YachtBooking\Integrations\ICal\ICal_Import::get_last_import_time( $yacht->ID ) : '';
|
||||
$title = $yacht ? $yacht->post_title : '';
|
||||
$content = $yacht ? $yacht->post_content : '';
|
||||
$gcal_alias = $yacht ? \YachtBooking\Yacht::get_gcal_alias( $yacht->ID ) : '';
|
||||
|
||||
$page_title = $yacht ? __( 'Edytuj Jacht', 'yacht-booking' ) : __( 'Dodaj Jacht', 'yacht-booking' );
|
||||
?>
|
||||
@@ -89,100 +86,28 @@ $page_title = $yacht ? __( 'Edytuj Jacht', 'yacht-booking' ) : __( 'Dodaj Jacht'
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Google Calendar ID -->
|
||||
<!-- Alias dla globalnej synchronizacji iCal -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="yacht_gcal_id">
|
||||
<?php esc_html_e( 'Google Calendar ID', 'yacht-booking' ); ?>
|
||||
<label for="yacht_gcal_alias">
|
||||
<?php esc_html_e( 'Alias dla Google Calendar', 'yacht-booking' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
name="yacht_gcal_id"
|
||||
id="yacht_gcal_id"
|
||||
value="<?php echo esc_attr( $gcal_id ); ?>"
|
||||
class="regular-text code"
|
||||
placeholder="nazwa@group.calendar.google.com"
|
||||
name="yacht_gcal_alias"
|
||||
id="yacht_gcal_alias"
|
||||
value="<?php echo esc_attr( $gcal_alias ); ?>"
|
||||
class="regular-text"
|
||||
placeholder="<?php echo esc_attr( $title ); ?>"
|
||||
/>
|
||||
<p class="description">
|
||||
<?php
|
||||
echo wp_kses_post(
|
||||
sprintf(
|
||||
/* translators: %s: link to Google Calendar settings */
|
||||
__( 'ID kalendarza Google do synchronizacji rezerwacji. <a href="%s" target="_blank">Jak znaleźć Calendar ID?</a>', 'yacht-booking' ),
|
||||
'https://docs.simplecalendar.io/find-google-calendar-id/'
|
||||
)
|
||||
);
|
||||
?>
|
||||
<?php esc_html_e( 'Opcjonalny krótki alias używany w tytule eventu Google Calendar (np. "Maja" zamiast "Marina Maja Sailing 35"). Plugin rozpoznaje jacht po prefiksie tytułu eventu w formacie "Alias - opis". Jeśli puste — używana jest pełna nazwa jachtu.', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
<?php if ( $gcal_id ) : ?>
|
||||
<p class="gcal-status connected">
|
||||
<span class="dashicons dashicons-yes-alt"></span>
|
||||
<?php esc_html_e( 'Połączony z Google Calendar', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- iCal Import URL -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="yacht_ical_import_url">
|
||||
<?php esc_html_e( 'Import iCal (URL)', 'yacht-booking' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="url"
|
||||
name="yacht_ical_import_url"
|
||||
id="yacht_ical_import_url"
|
||||
value="<?php echo esc_attr( $ical_import_url ); ?>"
|
||||
class="large-text code"
|
||||
placeholder="https://calendar.google.com/calendar/ical/...basic.ics"
|
||||
/>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Wklej adres URL pliku .ics z Google Calendar (lub innego kalendarza). Wydarzenia zostaną zaimportowane jako zablokowane terminy (synchronizacja co godzinę).', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
<p class="description">
|
||||
<strong><?php esc_html_e( 'Jak uzyskać link iCal z Google Calendar:', 'yacht-booking' ); ?></strong>
|
||||
<?php esc_html_e( 'Google Calendar → Ustawienia → Wybierz kalendarz → „Tajny adres w formacie iCal" → Skopiuj link.', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
<?php if ( $ical_last_import ) : ?>
|
||||
<p>
|
||||
<span class="dashicons dashicons-update" style="color: #2271b1;"></span>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s: date/time */
|
||||
esc_html__( 'Ostatni import: %s', 'yacht-booking' ),
|
||||
esc_html( $ical_last_import )
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php if ( $yacht ) : ?>
|
||||
<!-- iCal Export Feed URL -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<?php esc_html_e( 'Eksport iCal (feed)', 'yacht-booking' ); ?>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $ical_feed_url ); ?>"
|
||||
class="large-text code"
|
||||
readonly
|
||||
onclick="this.select();"
|
||||
/>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Skopiuj ten link i dodaj go w Google Calendar (Inne kalendarze → Z adresu URL), aby rezerwacje z tego systemu pojawiły się w Twoim kalendarzu.', 'yacht-booking' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -18,14 +18,111 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
class Installer {
|
||||
|
||||
/**
|
||||
* Run installation
|
||||
* Run installation / upgrade.
|
||||
*
|
||||
* Wykonuje cleanup migration (raz, gdy upgrade z wersji < CURRENT)
|
||||
* przed nadpisaniem yacht_booking_version w `set_version()`.
|
||||
*/
|
||||
public function install() {
|
||||
$this->create_tables();
|
||||
$this->create_options();
|
||||
$this->migrate();
|
||||
$this->set_version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup migration — usuwa martwe dane po wycofaniu OAuth + per-yacht iCal (09-03).
|
||||
*
|
||||
* Idempotencja: porównuje zapisaną wersję z `YACHT_BOOKING_VERSION`. Jeśli zapisana
|
||||
* jest >= bieżącej, cleanup nie uruchamia się (`set_version()` w install() nadpisuje).
|
||||
*
|
||||
* Cleanup kasuje:
|
||||
* - meta jachtów: _yacht_gcal_id, _yacht_ical_import_url, _yacht_ical_token, _yacht_ical_last_import
|
||||
* - opcje OAuth: yacht_booking_gcal_credentials, yacht_booking_gcal_tokens, yacht_booking_gcal_calendar_id, yacht_booking_gcal_sync_enabled
|
||||
* - rezerwacje z _booking_source = 'ical_import' (per-yacht — nie odświeżane już)
|
||||
* - stare crony OAuth + per-yacht iCal
|
||||
*
|
||||
* NIE kasuje:
|
||||
* - _yacht_gcal_alias (aktywny dla globalnego importu)
|
||||
* - rezerwacji 'ical_import_global' (aktywne) ani 'website' (rezerwacje klientów)
|
||||
*/
|
||||
private function migrate() {
|
||||
$installed_version = get_option( 'yacht_booking_version', '' );
|
||||
|
||||
// Pierwsza instalacja (brak wpisu) — nic do migracji.
|
||||
if ( '' === $installed_version ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Already on current or newer version — nothing to migrate.
|
||||
if ( version_compare( $installed_version, YACHT_BOOKING_VERSION, '>=' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete stale post meta across ALL posts.
|
||||
$stale_meta_keys = array(
|
||||
'_yacht_gcal_id',
|
||||
'_yacht_ical_import_url',
|
||||
'_yacht_ical_token',
|
||||
'_yacht_ical_last_import',
|
||||
);
|
||||
foreach ( $stale_meta_keys as $meta_key ) {
|
||||
delete_post_meta_by_key( $meta_key );
|
||||
}
|
||||
|
||||
// Delete stale options (OAuth credentials, tokens, calendar id, sync flag).
|
||||
$stale_options = array(
|
||||
'yacht_booking_gcal_credentials',
|
||||
'yacht_booking_gcal_tokens',
|
||||
'yacht_booking_gcal_calendar_id',
|
||||
'yacht_booking_gcal_sync_enabled',
|
||||
'yacht_booking_gcal_token',
|
||||
'yacht_booking_gcal_webhook_token',
|
||||
);
|
||||
foreach ( $stale_options as $option ) {
|
||||
delete_option( $option );
|
||||
}
|
||||
|
||||
// Delete bookings imported by old per-yacht iCal mechanism.
|
||||
$stale_bookings = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'post_status' => 'any',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => '_booking_source',
|
||||
'value' => 'ical_import',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
if ( ! empty( $stale_bookings ) ) {
|
||||
// Ensure Availability class is loaded (Installer is invoked from activation hook
|
||||
// before main plugin bootstrap, so autoloader may not be active yet).
|
||||
if ( ! class_exists( '\YachtBooking\Availability' ) ) {
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'includes/class-availability.php';
|
||||
}
|
||||
foreach ( $stale_bookings as $booking_id ) {
|
||||
\YachtBooking\Availability::clear_booking_availability( $booking_id );
|
||||
wp_delete_post( $booking_id, true );
|
||||
}
|
||||
}
|
||||
|
||||
// Clear cron hooks for removed mechanisms.
|
||||
$stale_cron_hooks = array(
|
||||
'yacht_booking_ical_import', // per-yacht iCal import
|
||||
'yacht_booking_pull_from_gcal', // OAuth pull
|
||||
'yacht_booking_sync_to_gcal', // OAuth push
|
||||
'yacht_booking_update_in_gcal', // OAuth update
|
||||
'yacht_booking_delete_from_gcal', // OAuth delete
|
||||
);
|
||||
foreach ( $stale_cron_hooks as $hook ) {
|
||||
wp_clear_scheduled_hook( $hook );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create custom database tables
|
||||
*/
|
||||
@@ -67,7 +164,9 @@ class Installer {
|
||||
'yacht_booking_currency_symbol' => 'zł',
|
||||
'yacht_booking_terms_page_id' => 0,
|
||||
'yacht_booking_enable_notifications' => 'yes',
|
||||
'yacht_booking_gcal_sync_enabled' => 'no',
|
||||
'yacht_booking_global_ical_import_url' => '',
|
||||
'yacht_booking_global_ical_token' => '',
|
||||
'yacht_booking_global_ical_last_import' => '',
|
||||
);
|
||||
|
||||
foreach ( $options as $key => $value ) {
|
||||
|
||||
@@ -83,18 +83,7 @@ class Yacht_Booking {
|
||||
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
|
||||
// Load iCal integration (globalna sync z 09-02 — jedyny mechanizm sync z GCal)
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/ical/class-ical-feed.php';
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/ical/class-ical-import.php';
|
||||
|
||||
|
||||
@@ -92,16 +92,6 @@ class Yacht {
|
||||
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
|
||||
*
|
||||
@@ -134,13 +124,23 @@ class Yacht {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update yacht Google Calendar ID
|
||||
* Get yacht alias for Google Calendar global sync.
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_gcal_alias( $yacht_id ) {
|
||||
return (string) get_post_meta( $yacht_id, '_yacht_gcal_alias', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update yacht alias for Google Calendar global sync.
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @param string $gcal_id Google Calendar ID.
|
||||
* @param string $alias Alias.
|
||||
*/
|
||||
public static function update_gcal_id( $yacht_id, $gcal_id ) {
|
||||
update_post_meta( $yacht_id, '_yacht_gcal_id', sanitize_text_field( $gcal_id ) );
|
||||
public static function update_gcal_alias( $yacht_id, $alias ) {
|
||||
update_post_meta( $yacht_id, '_yacht_gcal_alias', sanitize_text_field( $alias ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,521 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Google Calendar Service
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking\Integrations\GoogleCalendar;
|
||||
|
||||
use YachtBooking\Booking;
|
||||
use YachtBooking\Availability;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Google Calendar Service class
|
||||
*/
|
||||
class GCal_Service {
|
||||
|
||||
/**
|
||||
* Google Calendar API base URL
|
||||
*/
|
||||
const API_BASE = 'https://www.googleapis.com/calendar/v3';
|
||||
|
||||
/**
|
||||
* Booking source meta value for imported Google events.
|
||||
*/
|
||||
const EXTERNAL_BOOKING_SOURCE = 'google_calendar_external';
|
||||
|
||||
/**
|
||||
* Create event in Google Calendar
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return string|false Event ID or false on error.
|
||||
*/
|
||||
public static function create_event( $booking_id ) {
|
||||
$access_token = OAuth_Handler::get_access_token();
|
||||
$calendar_id = self::get_calendar_id();
|
||||
|
||||
if ( ! $access_token || ! $calendar_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get booking data
|
||||
$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 );
|
||||
$customer_name = Booking::get_customer_name( $booking_id );
|
||||
$customer_email = Booking::get_customer_email( $booking_id );
|
||||
$customer_phone = Booking::get_customer_phone( $booking_id );
|
||||
$status = Booking::get_status( $booking_id );
|
||||
|
||||
// Prepare event data
|
||||
$event = array(
|
||||
'summary' => sprintf(
|
||||
/* translators: 1: yacht name, 2: customer name */
|
||||
__( 'Rezerwacja: %1$s - %2$s', 'yacht-booking' ),
|
||||
$yacht ? $yacht->post_title : __( 'Jacht', 'yacht-booking' ),
|
||||
$customer_name
|
||||
),
|
||||
'description' => sprintf(
|
||||
"Booking ID: #%d\n\nKlient: %s\nEmail: %s\nTelefon: %s\nStatus: %s\n\nZarządzanie: %s",
|
||||
$booking_id,
|
||||
$customer_name,
|
||||
$customer_email,
|
||||
$customer_phone,
|
||||
$status,
|
||||
admin_url( 'admin.php?page=yacht-bookings-list&booking_id=' . $booking_id )
|
||||
),
|
||||
'start' => array(
|
||||
'date' => $start_date, // All-day event
|
||||
),
|
||||
'end' => array(
|
||||
'date' => date( 'Y-m-d', strtotime( $end_date . ' +1 day' ) ), // Exclusive end date
|
||||
),
|
||||
'colorId' => 'confirmed' === $status ? '9' : '11', // Blue for confirmed, red for pending
|
||||
);
|
||||
|
||||
$response = wp_remote_post(
|
||||
self::API_BASE . '/calendars/' . urlencode( $calendar_id ) . '/events',
|
||||
array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $access_token,
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $event ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
self::log_error( 'Create event failed: ' . $response->get_error_message() );
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
if ( isset( $body['id'] ) ) {
|
||||
// Save Google Event ID to booking meta
|
||||
update_post_meta( $booking_id, '_gcal_event_id', $body['id'] );
|
||||
return $body['id'];
|
||||
}
|
||||
|
||||
self::log_error( 'Create event failed: ' . wp_remote_retrieve_body( $response ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update event in Google Calendar
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function update_event( $booking_id ) {
|
||||
$access_token = OAuth_Handler::get_access_token();
|
||||
$calendar_id = self::get_calendar_id();
|
||||
$event_id = get_post_meta( $booking_id, '_gcal_event_id', true );
|
||||
|
||||
if ( ! $access_token || ! $calendar_id || ! $event_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get booking data
|
||||
$status = Booking::get_status( $booking_id );
|
||||
|
||||
// Update only color based on status
|
||||
$event = array(
|
||||
'colorId' => 'confirmed' === $status ? '9' : ( 'cancelled' === $status ? '8' : '11' ),
|
||||
);
|
||||
|
||||
$response = wp_remote_request(
|
||||
self::API_BASE . '/calendars/' . urlencode( $calendar_id ) . '/events/' . urlencode( $event_id ),
|
||||
array(
|
||||
'method' => 'PATCH',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $access_token,
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $event ),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
self::log_error( 'Update event failed: ' . $response->get_error_message() );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete event from Google Calendar
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete_event( $booking_id ) {
|
||||
$access_token = OAuth_Handler::get_access_token();
|
||||
$calendar_id = self::get_calendar_id();
|
||||
$event_id = get_post_meta( $booking_id, '_gcal_event_id', true );
|
||||
|
||||
if ( ! $access_token || ! $calendar_id || ! $event_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$response = wp_remote_request(
|
||||
self::API_BASE . '/calendars/' . urlencode( $calendar_id ) . '/events/' . urlencode( $event_id ),
|
||||
array(
|
||||
'method' => 'DELETE',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $access_token,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
self::log_error( 'Delete event failed: ' . $response->get_error_message() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove event ID from meta
|
||||
delete_post_meta( $booking_id, '_gcal_event_id' );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync events from Google Calendar to WordPress
|
||||
*
|
||||
* @param int $yacht_id Yacht post ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function sync_from_gcal( $yacht_id ) {
|
||||
$access_token = OAuth_Handler::get_access_token();
|
||||
$calendar_id = self::get_calendar_id();
|
||||
|
||||
if ( ! $access_token || ! $calendar_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch events from now to +1 year
|
||||
$time_min = gmdate( 'Y-m-d\TH:i:s\Z' );
|
||||
$time_max = gmdate( 'Y-m-d\TH:i:s\Z', strtotime( '+1 year' ) );
|
||||
|
||||
$response = wp_remote_get(
|
||||
add_query_arg(
|
||||
array(
|
||||
'timeMin' => $time_min,
|
||||
'timeMax' => $time_max,
|
||||
'singleEvents' => 'true',
|
||||
'orderBy' => 'startTime',
|
||||
),
|
||||
self::API_BASE . '/calendars/' . urlencode( $calendar_id ) . '/events'
|
||||
),
|
||||
array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $access_token,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
self::log_error( 'Sync from GCal failed: ' . $response->get_error_message() );
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
$events = isset( $body['items'] ) ? $body['items'] : array();
|
||||
|
||||
// Internal bookings (created in WordPress and pushed to Google) should not be re-imported.
|
||||
$internal_event_ids = self::get_internal_booking_event_ids();
|
||||
$external_booking_map = self::get_external_booking_map( $yacht_id );
|
||||
$seen_external_ids = array();
|
||||
|
||||
foreach ( $events as $event ) {
|
||||
if ( empty( $event['id'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip events already linked to internal WordPress bookings.
|
||||
if ( in_array( $event['id'], $internal_event_ids, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$range = self::get_blocked_range_from_event( $event );
|
||||
if ( ! $range ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$booking_id = isset( $external_booking_map[ $event['id'] ] ) ? (int) $external_booking_map[ $event['id'] ] : 0;
|
||||
$booking_id = self::upsert_external_booking( $yacht_id, $event, $range, $booking_id );
|
||||
|
||||
if ( $booking_id ) {
|
||||
$seen_external_ids[] = $event['id'];
|
||||
}
|
||||
}
|
||||
|
||||
// Remove stale imported placeholders (event removed from Google Calendar).
|
||||
foreach ( $external_booking_map as $event_id => $booking_id ) {
|
||||
if ( ! in_array( $event_id, $seen_external_ids, true ) ) {
|
||||
Availability::clear_booking_availability( $booking_id );
|
||||
// Prevent before_delete_post Google delete call for already-removed external events.
|
||||
delete_post_meta( $booking_id, '_gcal_event_id' );
|
||||
wp_delete_post( $booking_id, true );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calendar ID
|
||||
*
|
||||
* @return string|false Calendar ID or false.
|
||||
*/
|
||||
public static function get_calendar_id() {
|
||||
$calendar_id = get_option( 'yacht_booking_gcal_calendar_id' );
|
||||
|
||||
// Default to primary calendar if not set
|
||||
return $calendar_id ? $calendar_id : 'primary';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set calendar ID
|
||||
*
|
||||
* @param string $calendar_id Calendar ID.
|
||||
* @return bool
|
||||
*/
|
||||
public static function set_calendar_id( $calendar_id ) {
|
||||
return update_option( 'yacht_booking_gcal_calendar_id', $calendar_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of user's calendars
|
||||
*
|
||||
* @return array|false Array of calendars or false on error.
|
||||
*/
|
||||
public static function get_calendar_list() {
|
||||
$access_token = OAuth_Handler::get_access_token();
|
||||
|
||||
if ( ! $access_token ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$response = wp_remote_get(
|
||||
self::API_BASE . '/users/me/calendarList',
|
||||
array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $access_token,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
return isset( $body['items'] ) ? $body['items'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log error
|
||||
*
|
||||
* @param string $message Error message.
|
||||
*/
|
||||
private static function log_error( $message ) {
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
||||
error_log( '[Yacht Booking - GCal] ' . $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Google Calendar event to blocked date range.
|
||||
*
|
||||
* IMPORTANT: Availability::mark_as_blocked() expects end date to be exclusive.
|
||||
*
|
||||
* @param array $event Google Calendar event payload.
|
||||
* @return array|false
|
||||
*/
|
||||
private static function get_blocked_range_from_event( $event ) {
|
||||
// Skip cancelled events.
|
||||
if ( isset( $event['status'] ) && 'cancelled' === $event['status'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip "free" events (Show as: Available).
|
||||
if ( isset( $event['transparency'] ) && 'transparent' === $event['transparency'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All-day events: end date from Google is already exclusive.
|
||||
if ( isset( $event['start']['date'] ) && isset( $event['end']['date'] ) ) {
|
||||
$start_date = $event['start']['date'];
|
||||
$end_date = $event['end']['date'];
|
||||
|
||||
if ( strtotime( $end_date ) <= strtotime( $start_date ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array(
|
||||
'start_date' => $start_date,
|
||||
'end_date' => $end_date,
|
||||
);
|
||||
}
|
||||
|
||||
// Timed events: convert to full occupied-day range.
|
||||
if ( isset( $event['start']['dateTime'] ) && isset( $event['end']['dateTime'] ) ) {
|
||||
$start_ts = strtotime( $event['start']['dateTime'] );
|
||||
$end_ts = strtotime( $event['end']['dateTime'] );
|
||||
|
||||
if ( false === $start_ts || false === $end_ts || $end_ts <= $start_ts ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$start_date = gmdate( 'Y-m-d', $start_ts );
|
||||
|
||||
// Last occupied second to avoid midnight edge-cases.
|
||||
$last_occupied_ts = $end_ts - 1;
|
||||
$last_occupied = gmdate( 'Y-m-d', $last_occupied_ts );
|
||||
$end_date = gmdate( 'Y-m-d', strtotime( $last_occupied . ' +1 day' ) );
|
||||
|
||||
if ( strtotime( $end_date ) <= strtotime( $start_date ) ) {
|
||||
$end_date = gmdate( 'Y-m-d', strtotime( $start_date . ' +1 day' ) );
|
||||
}
|
||||
|
||||
return array(
|
||||
'start_date' => $start_date,
|
||||
'end_date' => $end_date,
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Google event IDs that belong to internal bookings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_internal_booking_event_ids() {
|
||||
global $wpdb;
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT event_pm.meta_value
|
||||
FROM {$wpdb->postmeta} event_pm
|
||||
INNER JOIN {$wpdb->posts} p ON p.ID = event_pm.post_id
|
||||
LEFT JOIN {$wpdb->postmeta} source_pm
|
||||
ON source_pm.post_id = event_pm.post_id
|
||||
AND source_pm.meta_key = '_booking_source'
|
||||
WHERE event_pm.meta_key = '_gcal_event_id'
|
||||
AND p.post_type = 'yacht_booking'
|
||||
AND p.post_status = 'publish'
|
||||
AND (source_pm.meta_value IS NULL OR source_pm.meta_value != %s)",
|
||||
self::EXTERNAL_BOOKING_SOURCE
|
||||
);
|
||||
|
||||
$ids = $wpdb->get_col( $sql );
|
||||
|
||||
return is_array( $ids ) ? $ids : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get imported external booking map for yacht: event_id => booking_id.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @return array
|
||||
*/
|
||||
private static function get_external_booking_map( $yacht_id ) {
|
||||
$bookings = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => '_booking_source',
|
||||
'value' => self::EXTERNAL_BOOKING_SOURCE,
|
||||
),
|
||||
array(
|
||||
'key' => '_booking_yacht_id',
|
||||
'value' => (int) $yacht_id,
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$map = array();
|
||||
foreach ( $bookings as $booking_id ) {
|
||||
$event_id = get_post_meta( $booking_id, '_gcal_event_id', true );
|
||||
if ( ! empty( $event_id ) ) {
|
||||
$map[ $event_id ] = (int) $booking_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update imported external booking placeholder.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @param array $event Google event payload.
|
||||
* @param array $range Date range (exclusive end).
|
||||
* @param int $existing_booking_id Existing booking ID.
|
||||
* @return int|false
|
||||
*/
|
||||
private static function upsert_external_booking( $yacht_id, $event, $range, $existing_booking_id = 0 ) {
|
||||
$summary = isset( $event['summary'] ) && '' !== trim( $event['summary'] ) ? sanitize_text_field( $event['summary'] ) : __( 'Wydarzenie z Google Calendar', 'yacht-booking' );
|
||||
$start_date = $range['start_date'];
|
||||
$end_date = $range['end_date'];
|
||||
$event_id = $event['id'];
|
||||
$event_url = isset( $event['htmlLink'] ) ? esc_url_raw( $event['htmlLink'] ) : '';
|
||||
|
||||
$post_data = array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'post_status' => 'publish',
|
||||
'post_title' => sprintf(
|
||||
/* translators: %s: Google event summary */
|
||||
__( 'Blokada Google Calendar: %s', 'yacht-booking' ),
|
||||
$summary
|
||||
),
|
||||
);
|
||||
|
||||
if ( $existing_booking_id > 0 ) {
|
||||
$post_data['ID'] = $existing_booking_id;
|
||||
$booking_id = wp_update_post( $post_data, true );
|
||||
} else {
|
||||
$booking_id = wp_insert_post( $post_data, true );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $booking_id ) || ! $booking_id ) {
|
||||
self::log_error( 'Failed to upsert external booking for event: ' . $event_id );
|
||||
return false;
|
||||
}
|
||||
|
||||
update_post_meta( $booking_id, '_booking_yacht_id', (int) $yacht_id );
|
||||
update_post_meta( $booking_id, '_booking_start_date', $start_date );
|
||||
update_post_meta( $booking_id, '_booking_end_date', $end_date );
|
||||
update_post_meta( $booking_id, '_booking_status', 'confirmed' );
|
||||
update_post_meta( $booking_id, '_booking_customer_name', __( 'Google Calendar (import)', 'yacht-booking' ) );
|
||||
update_post_meta( $booking_id, '_booking_customer_email', '' );
|
||||
update_post_meta( $booking_id, '_booking_customer_phone', '' );
|
||||
update_post_meta( $booking_id, '_booking_total_price', 0 );
|
||||
update_post_meta( $booking_id, '_booking_source', self::EXTERNAL_BOOKING_SOURCE );
|
||||
update_post_meta( $booking_id, '_gcal_event_id', $event_id );
|
||||
update_post_meta( $booking_id, '_booking_notes', $event_url );
|
||||
|
||||
// Rebuild availability tied to this imported booking.
|
||||
Availability::clear_booking_availability( $booking_id );
|
||||
Availability::mark_as_booked( $yacht_id, $start_date, $end_date, $booking_id );
|
||||
|
||||
return (int) $booking_id;
|
||||
}
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Google Calendar OAuth Handler
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking\Integrations\GoogleCalendar;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth Handler class
|
||||
*/
|
||||
class OAuth_Handler {
|
||||
|
||||
/**
|
||||
* Google OAuth endpoints
|
||||
*/
|
||||
const AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
|
||||
const TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
||||
const SCOPE = 'https://www.googleapis.com/auth/calendar';
|
||||
|
||||
/**
|
||||
* Get OAuth authorization URL
|
||||
*
|
||||
* @return string|false Authorization URL or false on error.
|
||||
*/
|
||||
public static function get_auth_url() {
|
||||
$credentials = self::get_credentials();
|
||||
|
||||
if ( ! $credentials ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Force production URL for Google OAuth
|
||||
$redirect_uri = 'https://jachty.pagedev.pl/wp-admin/admin.php?page=yacht-bookings-settings&tab=google-calendar&gcal_callback=1';
|
||||
|
||||
// Build URL manually to ensure proper encoding
|
||||
$params = array(
|
||||
'client_id' => $credentials['client_id'],
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'response_type' => 'code',
|
||||
'scope' => self::SCOPE,
|
||||
'access_type' => 'offline',
|
||||
'prompt' => 'consent',
|
||||
);
|
||||
|
||||
// Build query string with proper URL encoding
|
||||
$query = http_build_query( $params, '', '&' );
|
||||
|
||||
return self::AUTH_URL . '?' . $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange authorization code for access token
|
||||
*
|
||||
* @param string $code Authorization code.
|
||||
* @return array|false Token data or false on error.
|
||||
*/
|
||||
public static function authenticate( $code ) {
|
||||
$credentials = self::get_credentials();
|
||||
|
||||
if ( ! $credentials ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Force production URL for Google OAuth
|
||||
$redirect_uri = 'https://jachty.pagedev.pl/wp-admin/admin.php?page=yacht-bookings-settings&tab=google-calendar&gcal_callback=1';
|
||||
|
||||
$response = wp_remote_post(
|
||||
self::TOKEN_URL,
|
||||
array(
|
||||
'body' => array(
|
||||
'code' => $code,
|
||||
'client_id' => $credentials['client_id'],
|
||||
'client_secret' => $credentials['client_secret'],
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'grant_type' => 'authorization_code',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
if ( isset( $body['access_token'] ) ) {
|
||||
// Save tokens
|
||||
self::save_tokens( $body );
|
||||
return $body;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access token (refresh if expired)
|
||||
*
|
||||
* @return string|false Access token or false on error.
|
||||
*/
|
||||
public static function get_access_token() {
|
||||
$tokens = get_option( 'yacht_booking_gcal_tokens' );
|
||||
|
||||
if ( ! $tokens || ! isset( $tokens['access_token'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if token is expired
|
||||
$expires_at = isset( $tokens['expires_at'] ) ? $tokens['expires_at'] : 0;
|
||||
|
||||
if ( time() >= $expires_at ) {
|
||||
// Token expired, refresh it
|
||||
$new_tokens = self::refresh_access_token();
|
||||
|
||||
if ( ! $new_tokens ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $new_tokens['access_token'];
|
||||
}
|
||||
|
||||
return $tokens['access_token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh access token using refresh token
|
||||
*
|
||||
* @return array|false New token data or false on error.
|
||||
*/
|
||||
public static function refresh_access_token() {
|
||||
$credentials = self::get_credentials();
|
||||
$tokens = get_option( 'yacht_booking_gcal_tokens' );
|
||||
|
||||
if ( ! $credentials || ! $tokens || ! isset( $tokens['refresh_token'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$response = wp_remote_post(
|
||||
self::TOKEN_URL,
|
||||
array(
|
||||
'body' => array(
|
||||
'client_id' => $credentials['client_id'],
|
||||
'client_secret' => $credentials['client_secret'],
|
||||
'refresh_token' => $tokens['refresh_token'],
|
||||
'grant_type' => 'refresh_token',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
if ( isset( $body['access_token'] ) ) {
|
||||
// Preserve refresh_token (not always returned)
|
||||
if ( ! isset( $body['refresh_token'] ) && isset( $tokens['refresh_token'] ) ) {
|
||||
$body['refresh_token'] = $tokens['refresh_token'];
|
||||
}
|
||||
|
||||
self::save_tokens( $body );
|
||||
return $body;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save OAuth credentials
|
||||
*
|
||||
* @param array $credentials Credentials array.
|
||||
* @return bool
|
||||
*/
|
||||
public static function save_credentials( $credentials ) {
|
||||
if ( ! isset( $credentials['client_id'] ) || ! isset( $credentials['client_secret'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return update_option( 'yacht_booking_gcal_credentials', $credentials );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get saved credentials
|
||||
*
|
||||
* @return array|false Credentials or false.
|
||||
*/
|
||||
public static function get_credentials() {
|
||||
$credentials = get_option( 'yacht_booking_gcal_credentials' );
|
||||
|
||||
if ( ! $credentials || ! isset( $credentials['client_id'] ) || ! isset( $credentials['client_secret'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save OAuth tokens
|
||||
*
|
||||
* @param array $tokens Token data.
|
||||
*/
|
||||
private static function save_tokens( $tokens ) {
|
||||
// Calculate expiry time
|
||||
if ( isset( $tokens['expires_in'] ) ) {
|
||||
$tokens['expires_at'] = time() + (int) $tokens['expires_in'];
|
||||
}
|
||||
|
||||
update_option( 'yacht_booking_gcal_tokens', $tokens );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all Google Calendar data (disconnect)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function disconnect() {
|
||||
delete_option( 'yacht_booking_gcal_credentials' );
|
||||
delete_option( 'yacht_booking_gcal_tokens' );
|
||||
delete_option( 'yacht_booking_gcal_calendar_id' );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if connected to Google Calendar
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_connected() {
|
||||
$tokens = get_option( 'yacht_booking_gcal_tokens' );
|
||||
return ! empty( $tokens ) && isset( $tokens['access_token'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connected email (from token info)
|
||||
*
|
||||
* @return string|false Email or false.
|
||||
*/
|
||||
public static function get_connected_email() {
|
||||
$access_token = self::get_access_token();
|
||||
|
||||
if ( ! $access_token ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$response = wp_remote_get(
|
||||
'https://www.googleapis.com/oauth2/v2/userinfo',
|
||||
array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $access_token,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
return isset( $body['email'] ) ? $body['email'] : false;
|
||||
}
|
||||
}
|
||||
@@ -1,377 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Google Calendar Sync Controller
|
||||
*
|
||||
* @package YachtBooking
|
||||
*/
|
||||
|
||||
namespace YachtBooking\Integrations\GoogleCalendar;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync Controller class
|
||||
*
|
||||
* Handles automatic synchronization between WordPress bookings and Google Calendar
|
||||
*/
|
||||
class Sync_Controller {
|
||||
|
||||
/**
|
||||
* Instance
|
||||
*
|
||||
* @var Sync_Controller
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return Sync_Controller
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
// Hook into booking events
|
||||
add_action( 'yacht_booking_created', array( $this, 'on_booking_created' ), 20, 1 );
|
||||
add_action( 'yacht_booking_status_changed', array( $this, 'on_booking_status_changed' ), 20, 2 );
|
||||
add_action( 'before_delete_post', array( $this, 'on_booking_deleted' ), 10, 1 );
|
||||
|
||||
// AJAX handler for manual sync
|
||||
add_action( 'wp_ajax_yacht_booking_manual_sync', array( $this, 'ajax_manual_sync' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle booking created
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
*/
|
||||
public function on_booking_created( $booking_id ) {
|
||||
// Check if connected
|
||||
if ( ! OAuth_Handler::is_connected() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedule async sync (avoid blocking the request)
|
||||
wp_schedule_single_event(
|
||||
time(),
|
||||
'yacht_booking_sync_to_gcal',
|
||||
array( $booking_id )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle booking status changed
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @param string $new_status New status.
|
||||
*/
|
||||
public function on_booking_status_changed( $booking_id, $new_status ) {
|
||||
// Check if connected
|
||||
if ( ! OAuth_Handler::is_connected() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event_id = get_post_meta( $booking_id, '_gcal_event_id', true );
|
||||
|
||||
if ( 'cancelled' === $new_status && $event_id ) {
|
||||
// Delete event from Google Calendar
|
||||
wp_schedule_single_event(
|
||||
time(),
|
||||
'yacht_booking_delete_from_gcal',
|
||||
array( $booking_id )
|
||||
);
|
||||
} elseif ( 'confirmed' === $new_status ) {
|
||||
if ( $event_id ) {
|
||||
// Update existing event
|
||||
wp_schedule_single_event(
|
||||
time(),
|
||||
'yacht_booking_update_in_gcal',
|
||||
array( $booking_id )
|
||||
);
|
||||
} else {
|
||||
// Create new event
|
||||
wp_schedule_single_event(
|
||||
time(),
|
||||
'yacht_booking_sync_to_gcal',
|
||||
array( $booking_id )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle booking deleted
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
*/
|
||||
public function on_booking_deleted( $post_id ) {
|
||||
// Check if it's a booking
|
||||
if ( get_post_type( $post_id ) !== 'yacht_booking' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if connected
|
||||
if ( ! OAuth_Handler::is_connected() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event_id = get_post_meta( $post_id, '_gcal_event_id', true );
|
||||
|
||||
if ( $event_id ) {
|
||||
// Delete event from Google Calendar (sync now, before post is deleted)
|
||||
GCal_Service::delete_event( $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register cron actions
|
||||
*/
|
||||
public static function register_cron_actions() {
|
||||
// Sync to Google Calendar
|
||||
add_action( 'yacht_booking_sync_to_gcal', array( __CLASS__, 'sync_booking_to_gcal' ) );
|
||||
|
||||
// Update in Google Calendar
|
||||
add_action( 'yacht_booking_update_in_gcal', array( __CLASS__, 'update_booking_in_gcal' ) );
|
||||
|
||||
// Delete from Google Calendar
|
||||
add_action( 'yacht_booking_delete_from_gcal', array( __CLASS__, 'delete_booking_from_gcal' ) );
|
||||
|
||||
// Pull from Google Calendar (hourly)
|
||||
add_action( 'yacht_booking_pull_from_gcal', array( __CLASS__, 'pull_from_gcal' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync booking to Google Calendar (cron action)
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
*/
|
||||
public static function sync_booking_to_gcal( $booking_id ) {
|
||||
$event_id = GCal_Service::create_event( $booking_id );
|
||||
|
||||
if ( $event_id ) {
|
||||
self::log( sprintf( 'Booking #%d synced to Google Calendar (Event ID: %s)', $booking_id, $event_id ) );
|
||||
} else {
|
||||
self::log( sprintf( 'Failed to sync booking #%d to Google Calendar', $booking_id ), 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update booking in Google Calendar (cron action)
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
*/
|
||||
public static function update_booking_in_gcal( $booking_id ) {
|
||||
$result = GCal_Service::update_event( $booking_id );
|
||||
|
||||
if ( $result ) {
|
||||
self::log( sprintf( 'Booking #%d updated in Google Calendar', $booking_id ) );
|
||||
} else {
|
||||
self::log( sprintf( 'Failed to update booking #%d in Google Calendar', $booking_id ), 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete booking from Google Calendar (cron action)
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
*/
|
||||
public static function delete_booking_from_gcal( $booking_id ) {
|
||||
$result = GCal_Service::delete_event( $booking_id );
|
||||
|
||||
if ( $result ) {
|
||||
self::log( sprintf( 'Booking #%d deleted from Google Calendar', $booking_id ) );
|
||||
} else {
|
||||
self::log( sprintf( 'Failed to delete booking #%d from Google Calendar', $booking_id ), 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull events from Google Calendar (cron action)
|
||||
*/
|
||||
public static function pull_from_gcal() {
|
||||
// Get all yachts
|
||||
$yachts = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht',
|
||||
'posts_per_page' => -1,
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $yachts as $yacht ) {
|
||||
$result = GCal_Service::sync_from_gcal( $yacht->ID );
|
||||
|
||||
if ( $result ) {
|
||||
self::log( sprintf( 'Synced events from Google Calendar for yacht #%d', $yacht->ID ) );
|
||||
} else {
|
||||
self::log( sprintf( 'Failed to sync events from Google Calendar for yacht #%d', $yacht->ID ), 'error' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup hourly cron job for pulling from Google Calendar
|
||||
*/
|
||||
public static function setup_cron() {
|
||||
if ( ! wp_next_scheduled( 'yacht_booking_pull_from_gcal' ) ) {
|
||||
wp_schedule_event( time(), 'hourly', 'yacht_booking_pull_from_gcal' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cron jobs
|
||||
*/
|
||||
public static function clear_cron() {
|
||||
wp_clear_scheduled_hook( 'yacht_booking_pull_from_gcal' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log message
|
||||
*
|
||||
* @param string $message Message.
|
||||
* @param string $type Log type (info|error).
|
||||
*/
|
||||
private static function log( $message, $type = 'info' ) {
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
||||
$prefix = 'error' === $type ? 'ERROR' : 'INFO';
|
||||
error_log( sprintf( '[Yacht Booking - GCal Sync] [%s] %s', $prefix, $message ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for manual sync
|
||||
*/
|
||||
public function ajax_manual_sync() {
|
||||
// Check nonce
|
||||
check_ajax_referer( 'yacht_booking_manual_sync', 'nonce' );
|
||||
|
||||
// Check capabilities
|
||||
if ( ! current_user_can( 'yacht_booking_manage_settings' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Brak uprawnień', 'yacht-booking' ) ) );
|
||||
}
|
||||
|
||||
// Check if connected
|
||||
if ( ! OAuth_Handler::is_connected() ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Nie połączono z Google Calendar', 'yacht-booking' ) ) );
|
||||
}
|
||||
|
||||
// Statistics
|
||||
$yachts_synced = 0;
|
||||
$bookings_pushed = 0;
|
||||
$bookings_skipped = 0;
|
||||
$errors = array();
|
||||
|
||||
// STEP 1: Push WordPress bookings to Google Calendar
|
||||
$bookings = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'posts_per_page' => -1,
|
||||
'post_status' => 'publish',
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $bookings as $booking ) {
|
||||
$event_id = get_post_meta( $booking->ID, '_gcal_event_id', true );
|
||||
$status = get_post_meta( $booking->ID, '_booking_status', true );
|
||||
$source = get_post_meta( $booking->ID, '_booking_source', true );
|
||||
|
||||
// Never push imported Google placeholders back to Google.
|
||||
if ( GCal_Service::EXTERNAL_BOOKING_SOURCE === $source ) {
|
||||
$bookings_skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip cancelled bookings
|
||||
if ( 'cancelled' === $status ) {
|
||||
$bookings_skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only push if not already in Google Calendar
|
||||
if ( empty( $event_id ) ) {
|
||||
$result = GCal_Service::create_event( $booking->ID );
|
||||
|
||||
if ( $result ) {
|
||||
$bookings_pushed++;
|
||||
self::log( sprintf( 'Manual sync: Pushed booking #%d to Google Calendar', $booking->ID ) );
|
||||
} else {
|
||||
self::log( sprintf( 'Manual sync: Failed to push booking #%d to Google Calendar', $booking->ID ), 'error' );
|
||||
}
|
||||
} else {
|
||||
$bookings_skipped++;
|
||||
}
|
||||
}
|
||||
|
||||
// STEP 2: Pull external events from Google Calendar
|
||||
$yachts = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht',
|
||||
'posts_per_page' => -1,
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $yachts as $yacht ) {
|
||||
$result = GCal_Service::sync_from_gcal( $yacht->ID );
|
||||
|
||||
if ( $result ) {
|
||||
$yachts_synced++;
|
||||
self::log( sprintf( 'Manual sync: Pulled events from Google Calendar for yacht #%d', $yacht->ID ) );
|
||||
} else {
|
||||
$errors[] = sprintf( __( 'Błąd synchronizacji dla jachtu: %s', 'yacht-booking' ), $yacht->post_title );
|
||||
self::log( sprintf( 'Manual sync: Failed to pull events from Google Calendar for yacht #%d', $yacht->ID ), 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
// Build success message
|
||||
$messages = array();
|
||||
|
||||
if ( $bookings_pushed > 0 ) {
|
||||
$messages[] = sprintf(
|
||||
/* translators: %d: number of bookings pushed */
|
||||
_n( 'Wysłano %d rezerwację do Google Calendar', 'Wysłano %d rezerwacji do Google Calendar', $bookings_pushed, 'yacht-booking' ),
|
||||
$bookings_pushed
|
||||
);
|
||||
}
|
||||
|
||||
if ( $bookings_skipped > 0 ) {
|
||||
$messages[] = sprintf(
|
||||
/* translators: %d: number of bookings skipped */
|
||||
_n( 'Pominięto %d rezerwację (już zsynchronizowana lub anulowana)', 'Pominięto %d rezerwacji (już zsynchronizowane lub anulowane)', $bookings_skipped, 'yacht-booking' ),
|
||||
$bookings_skipped
|
||||
);
|
||||
}
|
||||
|
||||
if ( $yachts_synced > 0 ) {
|
||||
$messages[] = sprintf(
|
||||
/* translators: %d: number of yachts synced */
|
||||
_n( 'Pobrano wydarzenia dla %d jachtu z Google Calendar', 'Pobrano wydarzenia dla %d jachtów z Google Calendar', $yachts_synced, 'yacht-booking' ),
|
||||
$yachts_synced
|
||||
);
|
||||
}
|
||||
|
||||
if ( count( $errors ) > 0 ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => implode( '<br>', $errors ),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'message' => implode( '<br>', $messages ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,18 +32,18 @@ class ICal_Feed {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add rewrite rule for ical feed
|
||||
* Add rewrite rule for global ical feed
|
||||
*/
|
||||
public static function add_rewrite_rules() {
|
||||
add_rewrite_rule(
|
||||
'^yacht-ical/([0-9]+)/([a-zA-Z0-9]+)\.ics$',
|
||||
'index.php?yacht_ical_id=$matches[1]&yacht_ical_token=$matches[2]',
|
||||
'^yacht-ical-global/([a-zA-Z0-9]+)\.ics$',
|
||||
'index.php?yacht_ical_global=1&yacht_ical_token=$matches[1]',
|
||||
'top'
|
||||
);
|
||||
|
||||
// Flush rewrite rules if our rule is not registered yet.
|
||||
$rules = get_option( 'rewrite_rules' );
|
||||
if ( is_array( $rules ) && ! isset( $rules['^yacht-ical/([0-9]+)/([a-zA-Z0-9]+)\.ics$'] ) ) {
|
||||
if ( is_array( $rules ) && ! isset( $rules['^yacht-ical-global/([a-zA-Z0-9]+)\.ics$'] ) ) {
|
||||
flush_rewrite_rules( false );
|
||||
}
|
||||
}
|
||||
@@ -55,94 +55,82 @@ class ICal_Feed {
|
||||
* @return array
|
||||
*/
|
||||
public static function add_query_vars( $vars ) {
|
||||
$vars[] = 'yacht_ical_id';
|
||||
$vars[] = 'yacht_ical_token';
|
||||
$vars[] = 'yacht_ical_global';
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle feed request
|
||||
* Handle feed request — globalny feed iCal (jeden plik dla całej floty).
|
||||
*/
|
||||
public static function handle_feed_request() {
|
||||
$yacht_id = (int) get_query_var( 'yacht_ical_id', 0 );
|
||||
$token = get_query_var( 'yacht_ical_token', '' );
|
||||
$is_global = (int) get_query_var( 'yacht_ical_global', 0 );
|
||||
$token = get_query_var( 'yacht_ical_token', '' );
|
||||
|
||||
if ( ! $yacht_id || ! $token ) {
|
||||
if ( ! $is_global || ! $token ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$yacht = get_post( $yacht_id );
|
||||
if ( ! $yacht || 'yacht' !== $yacht->post_type ) {
|
||||
status_header( 404 );
|
||||
exit;
|
||||
}
|
||||
|
||||
$stored_token = self::get_feed_token( $yacht_id );
|
||||
$stored_token = self::get_global_feed_token();
|
||||
if ( ! $stored_token || ! hash_equals( $stored_token, $token ) ) {
|
||||
status_header( 403 );
|
||||
exit;
|
||||
}
|
||||
|
||||
self::output_ics( $yacht );
|
||||
self::output_global_ics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create feed token for a yacht
|
||||
* Get or create global feed token (one shared token for all-yachts feed).
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_feed_token( $yacht_id ) {
|
||||
$token = get_post_meta( $yacht_id, '_yacht_ical_token', true );
|
||||
public static function get_global_feed_token() {
|
||||
$token = get_option( 'yacht_booking_global_ical_token', '' );
|
||||
|
||||
if ( empty( $token ) ) {
|
||||
$token = wp_generate_password( 24, false );
|
||||
update_post_meta( $yacht_id, '_yacht_ical_token', $token );
|
||||
update_option( 'yacht_booking_global_ical_token', $token );
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate feed token
|
||||
* Regenerate global feed token (invalidates the previous URL).
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function regenerate_token( $yacht_id ) {
|
||||
public static function regenerate_global_token() {
|
||||
$token = wp_generate_password( 24, false );
|
||||
update_post_meta( $yacht_id, '_yacht_ical_token', $token );
|
||||
update_option( 'yacht_booking_global_ical_token', $token );
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get feed URL for a yacht
|
||||
* Get global feed URL (all yachts in one .ics).
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_feed_url( $yacht_id ) {
|
||||
$token = self::get_feed_token( $yacht_id );
|
||||
return home_url( sprintf( '/yacht-ical/%d/%s.ics', $yacht_id, $token ) );
|
||||
public static function get_global_feed_url() {
|
||||
$token = self::get_global_feed_token();
|
||||
return home_url( sprintf( '/yacht-ical-global/%s.ics', $token ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Output .ics file
|
||||
* Output global .ics feed — wszystkie jachty w jednym pliku.
|
||||
*
|
||||
* @param \WP_Post $yacht Yacht post.
|
||||
* Każdy event ma SUMMARY w formacie "{nazwa_jachtu} - {klient}".
|
||||
* Eventy z _booking_source = 'ical_import_global' są pomijane (anti-loop —
|
||||
* nie wysyłamy z powrotem do Google tego, co stamtąd przyszło).
|
||||
*/
|
||||
private static function output_ics( $yacht ) {
|
||||
private static function output_global_ics() {
|
||||
$bookings = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'posts_per_page' => -1,
|
||||
'post_status' => 'publish',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => '_booking_yacht_id',
|
||||
'value' => $yacht->ID,
|
||||
),
|
||||
array(
|
||||
'key' => '_booking_status',
|
||||
'value' => 'cancelled',
|
||||
@@ -156,7 +144,7 @@ class ICal_Feed {
|
||||
$domain = wp_parse_url( home_url(), PHP_URL_HOST );
|
||||
|
||||
header( 'Content-Type: text/calendar; charset=utf-8' );
|
||||
header( 'Content-Disposition: inline; filename="' . sanitize_file_name( $yacht->post_title ) . '.ics"' );
|
||||
header( 'Content-Disposition: inline; filename="yachts-all.ics"' );
|
||||
header( 'Cache-Control: no-cache, must-revalidate' );
|
||||
|
||||
$lines = array();
|
||||
@@ -165,10 +153,32 @@ class ICal_Feed {
|
||||
$lines[] = 'PRODID:-//YachtBooking//NONSGML v1.0//PL';
|
||||
$lines[] = 'CALSCALE:GREGORIAN';
|
||||
$lines[] = 'METHOD:PUBLISH';
|
||||
$lines[] = 'X-WR-CALNAME:' . self::escape_ical( $yacht->post_title . ' - ' . $site_name );
|
||||
$lines[] = 'X-WR-CALNAME:' . self::escape_ical(
|
||||
sprintf(
|
||||
/* translators: %s: site name */
|
||||
__( 'Wszystkie jachty - %s', 'yacht-booking' ),
|
||||
$site_name
|
||||
)
|
||||
);
|
||||
$lines[] = 'X-WR-TIMEZONE:Europe/Warsaw';
|
||||
|
||||
foreach ( $bookings as $booking ) {
|
||||
// Anti-loop: pomiń eventy które zostały zaimportowane z globalnego GCal.
|
||||
$source = get_post_meta( $booking->ID, '_booking_source', true );
|
||||
if ( ICal_Import::GLOBAL_IMPORT_SOURCE === $source ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$yacht_id = (int) Booking::get_yacht_id( $booking->ID );
|
||||
if ( ! $yacht_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$yacht = get_post( $yacht_id );
|
||||
if ( ! $yacht ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$start = Booking::get_start_date( $booking->ID );
|
||||
$end = Booking::get_end_date( $booking->ID );
|
||||
$status = Booking::get_status( $booking->ID );
|
||||
@@ -178,10 +188,11 @@ class ICal_Feed {
|
||||
continue;
|
||||
}
|
||||
|
||||
// iCal DTEND for all-day events is exclusive
|
||||
// iCal DTEND for all-day events is exclusive.
|
||||
$end_exclusive = gmdate( 'Ymd', strtotime( $end . ' +1 day' ) );
|
||||
$created = get_the_date( 'Ymd\THis\Z', $booking );
|
||||
|
||||
// Prefiks nazwy jachtu — kluczowy dla późniejszego importu po prefiksie.
|
||||
$summary = sprintf( '%s - %s', $yacht->post_title, $name );
|
||||
if ( 'pending' === $status ) {
|
||||
$summary = '[' . __( 'Oczekująca', 'yacht-booking' ) . '] ' . $summary;
|
||||
|
||||
@@ -24,23 +24,31 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
class ICal_Import {
|
||||
|
||||
/**
|
||||
* Booking source identifier for iCal imports.
|
||||
* Booking source identifier for iCal imports (globalny — wspólny GCal,
|
||||
* podział po prefiksie nazwy jachtu w SUMMARY).
|
||||
*/
|
||||
const IMPORT_SOURCE = 'ical_import';
|
||||
const GLOBAL_IMPORT_SOURCE = 'ical_import_global';
|
||||
|
||||
/**
|
||||
* Separator między prefiksem nazwy jachtu a resztą tytułu eventu.
|
||||
*
|
||||
* Przykład SUMMARY: "Maja - Kowalski 5 osób" → prefix="Maja", reszta="Kowalski 5 osób".
|
||||
*/
|
||||
const SUMMARY_SEPARATOR = ' - ';
|
||||
|
||||
/**
|
||||
* Register cron actions
|
||||
*/
|
||||
public static function register() {
|
||||
add_action( 'yacht_booking_ical_import', array( __CLASS__, 'run_import' ) );
|
||||
add_action( 'yacht_booking_ical_global_import', array( __CLASS__, 'run_global_import' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup cron schedule
|
||||
*/
|
||||
public static function setup_cron() {
|
||||
if ( ! wp_next_scheduled( 'yacht_booking_ical_import' ) ) {
|
||||
wp_schedule_event( time(), 'hourly', 'yacht_booking_ical_import' );
|
||||
if ( ! wp_next_scheduled( 'yacht_booking_ical_global_import' ) ) {
|
||||
wp_schedule_event( time(), 'hourly', 'yacht_booking_ical_global_import' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,57 +56,26 @@ class ICal_Import {
|
||||
* Clear cron
|
||||
*/
|
||||
public static function clear_cron() {
|
||||
wp_clear_scheduled_hook( 'yacht_booking_ical_import' );
|
||||
wp_clear_scheduled_hook( 'yacht_booking_ical_global_import' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run import for all yachts that have an iCal URL configured.
|
||||
* Globalny iCal import — pobiera jeden URL kalendarza Google z ustawień
|
||||
* globalnych, parsuje eventy i przypisuje je do jachtów po prefiksie w SUMMARY.
|
||||
*
|
||||
* Format SUMMARY: "{nazwa_jachtu_lub_alias} - {opis}".
|
||||
* Eventy bez separatora lub bez dopasowanego jachtu są ignorowane (logowane).
|
||||
*
|
||||
* @return bool True przy sukcesie HTTP, false przy błędzie pobrania/braku URL.
|
||||
*/
|
||||
public static function run_import() {
|
||||
$yachts = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
public static function run_global_import() {
|
||||
$url = get_option( 'yacht_booking_global_ical_import_url', '' );
|
||||
$url = is_string( $url ) ? trim( $url ) : '';
|
||||
|
||||
foreach ( $yachts as $yacht_id ) {
|
||||
$url = self::get_import_url( $yacht_id );
|
||||
if ( $url ) {
|
||||
self::import_for_yacht( $yacht_id, $url );
|
||||
}
|
||||
if ( empty( $url ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iCal import URL for a yacht.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_import_url( $yacht_id ) {
|
||||
return get_post_meta( $yacht_id, '_yacht_ical_import_url', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set iCal import URL for a yacht.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @param string $url iCal URL.
|
||||
*/
|
||||
public static function set_import_url( $yacht_id, $url ) {
|
||||
update_post_meta( $yacht_id, '_yacht_ical_import_url', esc_url_raw( $url ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import events from iCal URL for a specific yacht.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @param string $url iCal URL.
|
||||
* @return bool
|
||||
*/
|
||||
public static function import_for_yacht( $yacht_id, $url ) {
|
||||
$response = wp_remote_get(
|
||||
$url,
|
||||
array(
|
||||
@@ -108,19 +85,26 @@ class ICal_Import {
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
self::log( sprintf( 'iCal fetch failed for yacht #%d: %s', $yacht_id, $response->get_error_message() ), 'error' );
|
||||
self::log( sprintf( 'Global iCal fetch failed: %s', $response->get_error_message() ), 'error' );
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
if ( empty( $body ) ) {
|
||||
self::log( sprintf( 'iCal empty response for yacht #%d', $yacht_id ), 'error' );
|
||||
self::log( 'Global iCal: empty response body', 'error' );
|
||||
return false;
|
||||
}
|
||||
|
||||
$events = self::parse_ics( $body );
|
||||
$events = self::parse_ics( $body );
|
||||
$yacht_map = self::build_yacht_lookup_map();
|
||||
|
||||
$existing_map = self::get_existing_import_map( $yacht_id );
|
||||
if ( empty( $yacht_map ) ) {
|
||||
self::log( 'Global iCal: no yachts in DB — nothing to match', 'error' );
|
||||
update_option( 'yacht_booking_global_ical_last_import', current_time( 'mysql' ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
$existing_map = self::get_existing_global_import_map();
|
||||
$seen_uids = array();
|
||||
|
||||
foreach ( $events as $event ) {
|
||||
@@ -128,39 +112,198 @@ class ICal_Import {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip past events
|
||||
// Skip past events.
|
||||
if ( strtotime( $event['end'] ) < time() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$seen_uids[] = $event['uid'];
|
||||
$booking_id = isset( $existing_map[ $event['uid'] ] ) ? (int) $existing_map[ $event['uid'] ] : 0;
|
||||
$booking_id = self::upsert_booking( $yacht_id, $event, $booking_id );
|
||||
$summary = isset( $event['summary'] ) ? (string) $event['summary'] : '';
|
||||
$yacht_id = self::match_yacht_by_prefix( $summary, $yacht_map );
|
||||
|
||||
if ( ! $yacht_id ) {
|
||||
self::log( sprintf( 'Global iCal: skip event "%s" — no yacht match for prefix', $summary ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
$seen_uids[] = $event['uid'];
|
||||
$existing_id = isset( $existing_map[ $event['uid'] ] ) ? (int) $existing_map[ $event['uid'] ] : 0;
|
||||
|
||||
$booking_id = self::upsert_global_booking( $yacht_id, $event, $existing_id );
|
||||
|
||||
if ( ! $booking_id ) {
|
||||
self::log( sprintf( 'Failed to upsert iCal event %s for yacht #%d', $event['uid'], $yacht_id ), 'error' );
|
||||
self::log( sprintf( 'Global iCal: failed to upsert event %s', $event['uid'] ), 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
// Remove stale imports (events deleted from external calendar)
|
||||
// Stale cleanup — usuń bookingi których UID nie ma już w feedzie.
|
||||
foreach ( $existing_map as $uid => $booking_id ) {
|
||||
if ( ! in_array( $uid, $seen_uids, true ) ) {
|
||||
Availability::clear_booking_availability( $booking_id );
|
||||
\YachtBooking\Availability::clear_booking_availability( $booking_id );
|
||||
wp_delete_post( $booking_id, true );
|
||||
}
|
||||
}
|
||||
|
||||
update_post_meta( $yacht_id, '_yacht_ical_last_import', current_time( 'mysql' ) );
|
||||
update_option( 'yacht_booking_global_ical_last_import', current_time( 'mysql' ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buduje mapę: lowercase(alias|post_title) => yacht_id.
|
||||
*
|
||||
* Alias (`_yacht_gcal_alias`) ma priorytet nad post_title. Klucze są
|
||||
* znormalizowane przez mb_strtolower + trim, dla matchowania case-insensitive
|
||||
* i niezależnego od białych znaków.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function build_yacht_lookup_map() {
|
||||
$yachts = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
)
|
||||
);
|
||||
|
||||
$map = array();
|
||||
foreach ( $yachts as $yacht ) {
|
||||
$alias = \YachtBooking\Yacht::get_gcal_alias( $yacht->ID );
|
||||
$key = '' !== trim( (string) $alias ) ? $alias : $yacht->post_title;
|
||||
$key = mb_strtolower( trim( (string) $key ) );
|
||||
|
||||
if ( '' === $key ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Nie nadpisuj — w razie kolizji wygrywa pierwszy. Kolizje są
|
||||
// wpisem do logu w czasie matchowania (nie tutaj — bezgłośnie pierwszy).
|
||||
if ( ! isset( $map[ $key ] ) ) {
|
||||
$map[ $key ] = (int) $yacht->ID;
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wyciąga prefiks (przed pierwszym " - ") z SUMMARY i dopasowuje do jachtu
|
||||
* w mapie (case-insensitive).
|
||||
*
|
||||
* @param string $summary SUMMARY eventu.
|
||||
* @param array $yacht_map Mapa lowercase(klucz) => yacht_id.
|
||||
* @return int 0 gdy brak dopasowania.
|
||||
*/
|
||||
protected static function match_yacht_by_prefix( $summary, $yacht_map ) {
|
||||
if ( '' === $summary ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$pos = mb_strpos( $summary, self::SUMMARY_SEPARATOR );
|
||||
if ( false === $pos ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$prefix = mb_substr( $summary, 0, $pos );
|
||||
$prefix = mb_strtolower( trim( $prefix ) );
|
||||
|
||||
if ( '' === $prefix ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return isset( $yacht_map[ $prefix ] ) ? (int) $yacht_map[ $prefix ] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca mapę istniejących globalnych importów: uid => booking_id.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_existing_global_import_map() {
|
||||
$bookings = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'key' => '_booking_source',
|
||||
'value' => self::GLOBAL_IMPORT_SOURCE,
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$map = array();
|
||||
foreach ( $bookings as $booking_id ) {
|
||||
$uid = get_post_meta( $booking_id, '_ical_event_uid', true );
|
||||
if ( $uid ) {
|
||||
$map[ $uid ] = (int) $booking_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tworzy lub aktualizuje booking placeholder z globalnego importu.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @param array $event Parsed event data.
|
||||
* @param int $existing_id Existing booking ID (0 dla nowego).
|
||||
* @return int|false
|
||||
*/
|
||||
protected static function upsert_global_booking( $yacht_id, $event, $existing_id = 0 ) {
|
||||
$summary = ! empty( $event['summary'] ) ? sanitize_text_field( $event['summary'] ) : __( 'Blokada Google Calendar', 'yacht-booking' );
|
||||
$start_date = $event['start'];
|
||||
$end_date = $event['end'];
|
||||
|
||||
$post_data = array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'post_status' => 'publish',
|
||||
'post_title' => sprintf(
|
||||
/* translators: %s: event summary */
|
||||
__( 'GCal: %s', 'yacht-booking' ),
|
||||
$summary
|
||||
),
|
||||
);
|
||||
|
||||
if ( $existing_id > 0 ) {
|
||||
$post_data['ID'] = $existing_id;
|
||||
$booking_id = wp_update_post( $post_data, true );
|
||||
} else {
|
||||
$booking_id = wp_insert_post( $post_data, true );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $booking_id ) || ! $booking_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
update_post_meta( $booking_id, '_booking_yacht_id', (int) $yacht_id );
|
||||
update_post_meta( $booking_id, '_booking_start_date', $start_date );
|
||||
update_post_meta( $booking_id, '_booking_end_date', $end_date );
|
||||
update_post_meta( $booking_id, '_booking_status', 'confirmed' );
|
||||
update_post_meta( $booking_id, '_booking_customer_name', __( 'Google Calendar (import)', 'yacht-booking' ) );
|
||||
update_post_meta( $booking_id, '_booking_customer_email', '' );
|
||||
update_post_meta( $booking_id, '_booking_customer_phone', '' );
|
||||
update_post_meta( $booking_id, '_booking_total_price', 0 );
|
||||
update_post_meta( $booking_id, '_booking_source', self::GLOBAL_IMPORT_SOURCE );
|
||||
update_post_meta( $booking_id, '_ical_event_uid', $event['uid'] );
|
||||
update_post_meta( $booking_id, '_booking_notes', $summary );
|
||||
|
||||
\YachtBooking\Availability::clear_booking_availability( $booking_id );
|
||||
\YachtBooking\Availability::mark_as_booked( $yacht_id, $start_date, $end_date, $booking_id );
|
||||
|
||||
return (int) $booking_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse .ics content into array of events.
|
||||
*
|
||||
* @param string $ics_content Raw .ics content.
|
||||
* @return array
|
||||
*/
|
||||
private static function parse_ics( $ics_content ) {
|
||||
protected static function parse_ics( $ics_content ) {
|
||||
$events = array();
|
||||
$lines = preg_split( '/\r\n|\r|\n/', $ics_content );
|
||||
$in_event = false;
|
||||
@@ -277,106 +420,6 @@ class ICal_Import {
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing imported bookings map: uid => booking_id.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @return array
|
||||
*/
|
||||
private static function get_existing_import_map( $yacht_id ) {
|
||||
$bookings = get_posts(
|
||||
array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => '_booking_source',
|
||||
'value' => self::IMPORT_SOURCE,
|
||||
),
|
||||
array(
|
||||
'key' => '_booking_yacht_id',
|
||||
'value' => (int) $yacht_id,
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$map = array();
|
||||
foreach ( $bookings as $booking_id ) {
|
||||
$uid = get_post_meta( $booking_id, '_ical_event_uid', true );
|
||||
if ( $uid ) {
|
||||
$map[ $uid ] = (int) $booking_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update imported booking placeholder.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @param array $event Parsed event data.
|
||||
* @param int $existing_id Existing booking ID (0 for new).
|
||||
* @return int|false
|
||||
*/
|
||||
private static function upsert_booking( $yacht_id, $event, $existing_id = 0 ) {
|
||||
$summary = ! empty( $event['summary'] ) ? sanitize_text_field( $event['summary'] ) : __( 'Blokada iCal', 'yacht-booking' );
|
||||
$start_date = $event['start'];
|
||||
$end_date = $event['end'];
|
||||
|
||||
$post_data = array(
|
||||
'post_type' => 'yacht_booking',
|
||||
'post_status' => 'publish',
|
||||
'post_title' => sprintf(
|
||||
/* translators: %s: event summary */
|
||||
__( 'Import iCal: %s', 'yacht-booking' ),
|
||||
$summary
|
||||
),
|
||||
);
|
||||
|
||||
if ( $existing_id > 0 ) {
|
||||
$post_data['ID'] = $existing_id;
|
||||
$booking_id = wp_update_post( $post_data, true );
|
||||
} else {
|
||||
$booking_id = wp_insert_post( $post_data, true );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $booking_id ) || ! $booking_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
update_post_meta( $booking_id, '_booking_yacht_id', (int) $yacht_id );
|
||||
update_post_meta( $booking_id, '_booking_start_date', $start_date );
|
||||
update_post_meta( $booking_id, '_booking_end_date', $end_date );
|
||||
update_post_meta( $booking_id, '_booking_status', 'confirmed' );
|
||||
update_post_meta( $booking_id, '_booking_customer_name', __( 'Import iCal', 'yacht-booking' ) );
|
||||
update_post_meta( $booking_id, '_booking_customer_email', '' );
|
||||
update_post_meta( $booking_id, '_booking_customer_phone', '' );
|
||||
update_post_meta( $booking_id, '_booking_total_price', 0 );
|
||||
update_post_meta( $booking_id, '_booking_source', self::IMPORT_SOURCE );
|
||||
update_post_meta( $booking_id, '_ical_event_uid', $event['uid'] );
|
||||
update_post_meta( $booking_id, '_booking_notes', $summary );
|
||||
|
||||
Availability::clear_booking_availability( $booking_id );
|
||||
Availability::mark_as_booked( $yacht_id, $start_date, $end_date, $booking_id );
|
||||
|
||||
return (int) $booking_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last import time for yacht.
|
||||
*
|
||||
* @param int $yacht_id Yacht ID.
|
||||
* @return string
|
||||
*/
|
||||
public static function get_last_import_time( $yacht_id ) {
|
||||
return get_post_meta( $yacht_id, '_yacht_ical_last_import', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log message.
|
||||
*
|
||||
|
||||
@@ -37,13 +37,21 @@ function yacht_booking_delete_options() {
|
||||
delete_option( 'yacht_booking_terms_page_id' );
|
||||
delete_option( 'yacht_booking_email_templates' );
|
||||
delete_option( 'yacht_booking_enable_notifications' );
|
||||
delete_option( 'yacht_booking_gcal_sync_enabled' );
|
||||
delete_option( 'yacht_booking_gcal_token' );
|
||||
delete_option( 'yacht_booking_gcal_webhook_token' );
|
||||
delete_option( 'yacht_booking_enabled' );
|
||||
delete_option( 'yacht_booking_capabilities_added' );
|
||||
|
||||
// Globalna iCal sync (z 09-02).
|
||||
delete_option( 'yacht_booking_global_ical_import_url' );
|
||||
delete_option( 'yacht_booking_global_ical_token' );
|
||||
delete_option( 'yacht_booking_global_ical_last_import' );
|
||||
|
||||
// Legacy keys (defensive — gdyby pozostały po starszych wersjach).
|
||||
delete_option( 'yacht_booking_gcal_credentials' );
|
||||
delete_option( 'yacht_booking_gcal_tokens' );
|
||||
delete_option( 'yacht_booking_gcal_calendar_id' );
|
||||
delete_option( 'yacht_booking_capabilities_added' );
|
||||
delete_option( 'yacht_booking_gcal_sync_enabled' );
|
||||
delete_option( 'yacht_booking_gcal_token' );
|
||||
delete_option( 'yacht_booking_gcal_webhook_token' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,11 +65,7 @@ function yacht_booking_activate() {
|
||||
$installer = new YachtBooking\Installer();
|
||||
$installer->install();
|
||||
|
||||
// Setup Google Calendar cron jobs
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-sync-controller.php';
|
||||
YachtBooking\Integrations\GoogleCalendar\Sync_Controller::setup_cron();
|
||||
|
||||
// Setup iCal import cron
|
||||
// Setup iCal import cron (globalny — z 09-02)
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/ical/class-ical-import.php';
|
||||
YachtBooking\Integrations\ICal\ICal_Import::setup_cron();
|
||||
|
||||
@@ -82,10 +78,6 @@ register_activation_hook( __FILE__, 'yacht_booking_activate' );
|
||||
* Plugin deactivation hook
|
||||
*/
|
||||
function yacht_booking_deactivate() {
|
||||
// Clear Google Calendar cron jobs
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/google-calendar/class-sync-controller.php';
|
||||
YachtBooking\Integrations\GoogleCalendar\Sync_Controller::clear_cron();
|
||||
|
||||
// Clear iCal import cron
|
||||
require_once YACHT_BOOKING_PLUGIN_DIR . 'integrations/ical/class-ical-import.php';
|
||||
YachtBooking\Integrations\ICal\ICal_Import::clear_cron();
|
||||
|
||||
Reference in New Issue
Block a user