` z 09-02.
+ - Usunąć lokalne require_once `OAuth_Handler` i `GCal_Service` w tej metodzie (już niepotrzebne).
+ - Pozostawić bez zmian `process_yacht_save`, `save_yacht`, list table'y, eksport CSV, etc.
+
+ Avoid:
+ - Nie ruszać sekcji "Globalna synchronizacja iCal" (z 09-02) — to jest TO co zostaje
+ - Nie usuwać CSS/JS dla globalnego iCal
+ - Nie usuwać AJAX handler globalnego iCal (jeśli istnieje — w 09-02 jest tylko form POST, żaden AJAX)
+
+
+ - `ls integrations/google-calendar/` zwraca pustkę lub katalog nie istnieje
+ - `grep -r "OAuth_Handler\|GCal_Service\|Sync_Controller\|GoogleCalendar" wp-content/plugins/yacht-booking-system --include="*.php"` zwraca 0 wyników (poza komentarzami w summary/changelog)
+ - `php -l` przechodzi na: yacht-booking-system.php, class-yacht-booking.php, class-admin.php
+ - W WP Admin → Settings → Google Calendar widać TYLKO sekcję "Globalna synchronizacja iCal"
+ - Brak fatal error przy aktywacji (sprawdź `?WP_DEBUG=true` lub error log)
+
+
AC-1 i część AC-2 satisfied (sekcja OAuth zniknęła)
+
+
+
+ Task 2: Usunąć per-jacht iCal feed/import + pole gcal_id z UI
+
+ wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php,
+ wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-feed.php,
+ wp-content/plugins/yacht-booking-system/admin/views/yacht-edit.php,
+ wp-content/plugins/yacht-booking-system/admin/class-yacht-list-table.php,
+ wp-content/plugins/yacht-booking-system/admin/class-admin.php,
+ wp-content/plugins/yacht-booking-system/includes/class-yacht.php
+
+
+ 1. **`class-ical-import.php` — usuwamy mechanizm per-yacht, zostaje tylko globalny**:
+ - Usunąć stałą `IMPORT_SOURCE` (zachować `GLOBAL_IMPORT_SOURCE` i `SUMMARY_SEPARATOR`)
+ - W `register()`: usunąć `add_action( 'yacht_booking_ical_import', ... )` (zostaje tylko `yacht_booking_ical_global_import`)
+ - W `setup_cron()`: usunąć `wp_schedule_event` dla `yacht_booking_ical_import`
+ - W `clear_cron()`: usunąć `wp_clear_scheduled_hook( 'yacht_booking_ical_import' )`
+ - Usunąć metody publiczne: `run_import()`, `get_import_url()`, `set_import_url()`, `get_last_import_time()`
+ - Usunąć metody prywatne: `import_for_yacht()`, `get_existing_import_map()`, `upsert_booking()` (per-yacht — zachować `upsert_global_booking`!)
+ - Zachować: `parse_ics`, helpers (`line_starts_with`, `extract_value`, `parse_ical_date`, `unescape_ical`), `log`, oraz cały globalny mechanizm (run_global_import, build_yacht_lookup_map, match_yacht_by_prefix, get_existing_global_import_map, upsert_global_booking)
+
+ 2. **`class-ical-feed.php` — usuwamy per-yacht feed, zostaje tylko globalny**:
+ - W `add_rewrite_rules()`: usunąć rule `^yacht-ical/([0-9]+)/([a-zA-Z0-9]+)\.ics$` (zostaje tylko global). Zaktualizować flush check.
+ - W `add_query_vars()`: usunąć `yacht_ical_id` (zostają `yacht_ical_token` i `yacht_ical_global`)
+ - W `handle_feed_request()`: usunąć cały blok per-yacht (zachować tylko branch global)
+ - Usunąć metody: `get_feed_token()`, `regenerate_token()`, `get_feed_url()`, `output_ics()`
+ - Zachować: `register()`, `add_rewrite_rules()` (już poprawiony), `add_query_vars()` (poprawiony), `handle_feed_request()` (poprawiony), wszystkie metody global, `escape_ical()`
+
+ 3. **`admin/views/yacht-edit.php`**:
+ - Usunąć całe wiersze tabeli z polami: `yacht_gcal_id`, `yacht_ical_import_url`, oraz blok wyświetlający per-yacht iCal Feed URL (z tokenem)
+ - Usunąć z PHP na górze pliku: `$gcal_id = ...`, `$ical_import_url = ...`, `$ical_feed_url = ...`, `$ical_last_import = ...`
+ - **ZACHOWAĆ** pole `yacht_gcal_alias` (z 09-02 — używane przez globalny import)
+ - Pozostawić bez zmian: title, description, capacity, price_per_day, features
+
+ 4. **`admin/class-yacht-list-table.php`**:
+ - Usunąć z `get_columns()` kolumnę dla GCal i iCal status (jeśli istnieją)
+ - Usunąć metodę `column_gcal_id()` lub equivalent (linia ok. 173 — `Yacht::get_gcal_id`)
+ - Sprawdź czy są inne kolumny używające usuniętych meta — usuń ich rendering
+
+ 5. **`admin/class-admin.php` — czyszczenie save_yacht**:
+ - W `save_yacht()`: usunąć linie:
+ - `$gcal_id = ...`
+ - `Yacht::update_gcal_id( $saved_id, $gcal_id );`
+ - `$ical_import_url = ...`
+ - `\YachtBooking\Integrations\ICal\ICal_Import::set_import_url( ... )`
+ - **ZACHOWAĆ** zapis aliasu (`Yacht::update_gcal_alias`)
+
+ 6. **`includes/class-yacht.php`**:
+ - Usunąć metody: `get_gcal_id()`, `update_gcal_id()`
+ - **ZACHOWAĆ**: get_capacity, update_capacity, get_price_per_day, update_price_per_day, get_features, update_features, get_gcal_alias, update_gcal_alias
+
+ Avoid:
+ - Nie tknąć w żaden sposób mechanizmu globalnego iCal (run_global_import, output_global_ics, alias)
+ - Nie usuwać cron `yacht_booking_ical_global_import` (z 09-02)
+ - Nie usuwać `parse_ics` ani helperów iCal — używane przez globalny import
+
+
+ - `php -l` przechodzi na: class-ical-import.php, class-ical-feed.php, yacht-edit.php, class-yacht-list-table.php, class-admin.php, class-yacht.php
+ - `grep -r "yacht_booking_ical_import\|run_import\|import_for_yacht\|get_gcal_id\|update_gcal_id\|_yacht_gcal_id\|_yacht_ical_import_url" wp-content/plugins/yacht-booking-system --include="*.php"` zwraca 0 wyników (lub tylko komentarze)
+ - W WP Admin → Edycja jachtu: NIE widać pól "Google Calendar ID", "iCal Import URL", "iCal Feed URL". WIDAĆ pole "Alias dla Google Calendar".
+ - URL `/yacht-ical/824/jakistoken.ics` zwraca 404
+ - URL `/yacht-ical-global/{token}.ics` nadal działa
+
+ AC-3, AC-4, część AC-2 (UI per-yacht) satisfied
+
+
+
+ Task 3: Cleanup migration w Installer + uninstall.php
+
+ wp-content/plugins/yacht-booking-system/includes/class-installer.php,
+ wp-content/plugins/yacht-booking-system/uninstall.php
+
+
+ 1. **`class-installer.php` — dodać metodę `migrate()` wywoływaną z `install()`**:
+ - Logika: pobierz `yacht_booking_version` z opcji. Jeśli istnieje i jest niższa niż `YACHT_BOOKING_VERSION` (czyli to upgrade) — uruchom cleanup.
+ - Cleanup operacje:
+ a. **Delete post meta** dla wszystkich jachtów:
+ - `_yacht_gcal_id`
+ - `_yacht_ical_import_url`
+ - `_yacht_ical_token`
+ - `_yacht_ical_last_import`
+ Sposób: `delete_post_meta_by_key( '_yacht_gcal_id' )` (WordPress core function — usuwa meta dla wszystkich postów). Powtórzyć dla każdego klucza.
+ b. **Delete options** (OAuth/credentials):
+ - `yacht_booking_gcal_calendar_id`
+ - `yacht_booking_gcal_credentials`
+ - `yacht_booking_gcal_oauth_access_token`
+ - `yacht_booking_gcal_oauth_refresh_token`
+ - `yacht_booking_gcal_oauth_token_expires`
+ - `yacht_booking_gcal_oauth_email`
+ - `yacht_booking_gcal_sync_enabled`
+ (sprawdź faktyczne nazwy w `class-oauth-handler.php` ZANIM zostanie usunięty w Task 1 — albo grep bashem PRZED. Lista powyżej to typowe; jeśli różne nazwy, skoryguj)
+ c. **Delete bookings** z `_booking_source = 'ical_import'` (per-yacht):
+ ```php
+ $bookings = get_posts( array(
+ 'post_type' => 'yacht_booking',
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ 'meta_query' => array(
+ array( 'key' => '_booking_source', 'value' => 'ical_import' ),
+ ),
+ ) );
+ foreach ( $bookings as $booking_id ) {
+ Availability::clear_booking_availability( $booking_id );
+ wp_delete_post( $booking_id, true );
+ }
+ ```
+ d. **Clear stale crons**: `wp_clear_scheduled_hook( 'yacht_booking_ical_import' )`, `wp_clear_scheduled_hook( 'yacht_booking_pull_from_gcal' )`, `wp_clear_scheduled_hook( 'yacht_booking_sync_to_gcal' )`, `wp_clear_scheduled_hook( 'yacht_booking_update_in_gcal' )`, `wp_clear_scheduled_hook( 'yacht_booking_delete_from_gcal' )`.
+ - Idempotencja: `set_version()` w `install()` ZAWSZE nadpisuje `yacht_booking_version` aktualną wartością → przy kolejnej aktywacji warunek `version < CURRENT` jest fałszem → migrate() nic nie robi. ✓
+ - Wywołać `migrate()` w `install()` PRZED `set_version()`.
+
+ 2. **`uninstall.php`**:
+ - Sprawdź obecną zawartość (`Read`).
+ - Usuń `delete_option` dla nieistniejących już kluczy GCal (te z punktu 1.b)
+ - **ZACHOWAJ** delete_option dla:
+ - `yacht_booking_default_status`, `yacht_booking_email_from_name`, `yacht_booking_email_from`, `yacht_booking_date_format`, `yacht_booking_currency_symbol`, `yacht_booking_terms_page_id`, `yacht_booking_enable_notifications`
+ - `yacht_booking_global_ical_import_url`, `yacht_booking_global_ical_token`, `yacht_booking_global_ical_last_import`
+ - `yacht_booking_email_templates`, `yacht_booking_version`, `yacht_booking_installed_at`, `yacht_booking_enabled`
+ - **ZACHOWAJ** delete `wp_yacht_availability` table + delete_post_meta dla wszystkich CPT meta + wp_delete_post dla CPT.
+
+ Avoid:
+ - Nie usuwać `_yacht_gcal_alias` (aktywny dla globalnego importu)
+ - Nie usuwać `_booking_source = 'ical_import_global'` (aktywne globalne importy z 09-02 — zostawić!)
+ - Nie usuwać innych user data (rezerwacji ze strony — `_booking_source != 'ical_import'`)
+ - Cleanup ma być idempotentny — uruchamia się TYLKO przy upgrade z wersji < CURRENT
+
+
+ - `php -l` przechodzi na: class-installer.php, uninstall.php
+ - Symulacja upgrade: ustaw `yacht_booking_version` na '0.9.0' w bazie testowej (lub starsza), aktywuj plugin → cleanup wykonuje się; wszystkie wymienione meta i opcje zniknęły; rezerwacje ze starym source 'ical_import' usunięte; rezerwacje globalne ('ical_import_global') i ze strony pozostały
+ - Po cleanup: `wp_options` nie zawiera kluczy gcal_oauth_*, gcal_credentials, gcal_calendar_id, gcal_sync_enabled
+ - Druga aktywacja po pierwszej → migrate() nic nie robi (cicha)
+
+ AC-5, AC-7 satisfied
+
+
+
+
+ Posprzątany plugin: jeden mechanizm sync (globalny iCal), zero martwych pól, czysta baza po upgrade.
+ - Usunięte 3 pliki OAuth/Sync/GCal_Service
+ - Usunięty per-jacht iCal feed (URL `/yacht-ical/{id}/...` zwraca 404)
+ - Usunięty per-jacht iCal import (cron i pole)
+ - Usunięte pole "Google Calendar ID" z yacht-edit
+ - Cleanup migration: stare meta, opcje OAuth, rezerwacje per-yacht 'ical_import' → wyczyszczone
+ - Settings → Google Calendar zawiera TYLKO sekcję "Globalna synchronizacja iCal"
+
+
+ 1. Po deploy FTP wejdź do WP Admin (jeśli plugin nieaktywny — aktywuj). To wyzwoli `Installer::install()` z migracją.
+ 2. Sprawdź WP Admin → Rezerwacje Jachtów → Ustawienia → Google Calendar:
+ - **NIE ma** sekcji OAuth (Client ID, "Połącz z Google Calendar", "Synchronizuj teraz")
+ - **JEST** sekcja "Globalna synchronizacja iCal" z 09-02 (export URL, import URL, "Importuj teraz")
+ 3. Wejdź w edycję dowolnego jachtu (np. yacht_id=824):
+ - **NIE ma** pól "Google Calendar ID", "iCal Import URL", "iCal Feed URL"
+ - **JEST** pole "Alias dla Google Calendar"
+ 4. Sprawdź listę jachtów (Rezerwacje Jachtów → Wszystkie Jachty):
+ - **Nie ma** kolumn związanych z GCal/iCal per-jacht
+ 5. Otwórz w przeglądarce dowolny stary URL per-yacht: `https://jachty3.pagedev.pl/yacht-ical/824/jakikolwiek_token.ics` → **404 Not Found**
+ 6. Otwórz globalny URL z Settings (Krok 1) → **zwraca .ics z rezerwacjami**
+ 7. Settings → "Importuj teraz" → komunikat sukcesu, w error log nie ma fatal error
+ 8. Sprawdź bazę (phpMyAdmin lub WP-CLI):
+ - `SELECT * FROM wp_postmeta WHERE meta_key IN ('_yacht_gcal_id','_yacht_ical_import_url','_yacht_ical_token','_yacht_ical_last_import')` → 0 wierszy
+ - `SELECT * FROM wp_options WHERE option_name LIKE 'yacht_booking_gcal_%'` → 0 wierszy
+ - `SELECT post_id FROM wp_postmeta WHERE meta_key='_booking_source' AND meta_value='ical_import'` → 0 wierszy
+ - `SELECT option_value FROM wp_options WHERE option_name='yacht_booking_global_ical_token'` → JEST (zachowane)
+ 9. Frontend: otwórz stronę z kalendarzem dowolnego jachtu → kalendarz się ładuje, dostępność pokazuje (test regresji widget)
+ 10. Złóż testową rezerwację na frontendzie → trafia do bazy, pojawia się w admin → po refresh GCal subscription pojawi się w globalnym kalendarzu Google admina
+
+ Wpisz "approved" gdy 10 punktów przejdzie, lub opisz problemy do naprawy
+
+
+
+
+
+
+## DO NOT CHANGE
+- **Globalna synchronizacja iCal (09-02)** — `output_global_ics`, `run_global_import`, `build_yacht_lookup_map`, `match_yacht_by_prefix`, `upsert_global_booking`, settings UI sekcja "Globalna synchronizacja iCal", `_yacht_gcal_alias`, opcje `yacht_booking_global_ical_*`, cron `yacht_booking_ical_global_import`
+- **`Yacht::get_gcal_alias`/`update_gcal_alias`** — używane przez globalny import
+- **CPT yacht_booking i yacht** — schemat, capabilities, registration
+- **`wp_yacht_availability`** — tabela i logika `Availability`
+- **Frontend widget i shortcode** — działają dzięki `_booking_yacht_id`
+- **REST API** (`Rest_Controller`) — endpoint POST /bookings używa wewnętrznych funkcji booking::create, niezależny od GCal
+- **Email templates + Inquiry CPT + CSV export** — out of scope
+
+## SCOPE LIMITS
+- Plan NIE dotyka security audit (to 09-04)
+- Plan NIE dotyka tłumaczeń .pot/.po/.mo (to 09-05)
+- Plan NIE refactoruje `parse_ics` ani helperów iCal (działają — zostają)
+- Plan NIE zmienia formatu prefiksu w SUMMARY (separator `" - "` z 09-02 zostaje)
+- Brak nowych dependencies
+- Brak admin notice o migracji (klient wybrał "po cichu")
+- Plan NIE migruje rezerwacji typu 'ical_import' do 'ical_import_global' — usuwa je (klient ponownie zaimportuje przez globalny mechanizm)
+
+
+
+
+Przed deklaracją ukończenia planu:
+- [ ] `php -l` przechodzi na wszystkich zmodyfikowanych plikach
+- [ ] `grep -r "OAuth_Handler\|GCal_Service\|Sync_Controller"` w plugin dir → 0 wyników (poza logami/changelog)
+- [ ] `grep -r "yacht_booking_ical_import[^_]"` (bez `_global`) → 0 wyników
+- [ ] `grep -r "_yacht_gcal_id\|_yacht_ical_import_url\|_yacht_ical_token"` → 0 wyników w runtime code
+- [ ] Aktywacja pluginu w WP nie powoduje fatal error (sprawdź error log)
+- [ ] Cleanup migration wykonuje się raz (przy upgrade) i jest idempotentne
+- [ ] Globalna iCal sync działa bez regresji (export + import + frontend)
+- [ ] URL per-yacht feed zwraca 404
+- [ ] uninstall.php usuwa wszystkie aktualne klucze (nic więcej, nic mniej)
+- [ ] Wszystkie 7 acceptance criteria spełnione
+- [ ] Checkpoint human-verify zaakceptowany przez klienta
+
+
+
+- Plugin ma JEDEN mechanizm synchronizacji z GCal: globalny iCal feed (eksport + import)
+- Zero martwego kodu (3 pliki usunięte, ~500+ linii zbędnego UI/cron usuniętych)
+- Czysta baza po upgrade (klient nie widzi starych pól, baza nie zawiera nieaktywnych meta/opcji)
+- Brak regresji w globalnej iCal sync ani frontendzie
+- Plugin gotowy produkcyjnie w warstwie integracji GCal (pozostaje security audit + i18n + docs)
+
+
+
diff --git a/.paul/phases/09-finalizacja/09-03-SUMMARY.md b/.paul/phases/09-finalizacja/09-03-SUMMARY.md
new file mode 100644
index 0000000..b6fbbf3
--- /dev/null
+++ b/.paul/phases/09-finalizacja/09-03-SUMMARY.md
@@ -0,0 +1,184 @@
+---
+phase: 09-finalizacja
+plan: 03
+subsystem: cleanup
+tags: [cleanup, oauth-removal, ical-consolidation, migration]
+
+requires:
+ - phase: 09-finalizacja/09-02
+ provides: globalna iCal sync (export feed + import + alias) — staje się jedynym mechanizmem po usunięciu konkurencji
+
+provides:
+ - Plugin z jednym mechanizmem GCal sync (globalny iCal)
+ - Cleanup migration "po cichu" (Installer::migrate, idempotent, version-aware)
+ - Czysta baza po upgrade (4 stale meta keys + 6 stale options + per-yacht ical_import bookings + 5 cron hooków usuniętych)
+ - Skrócony codebase (~700 linii martwego kodu usuniętych, 3 pliki OAuth znikają)
+
+affects:
+ - 09-04 Security audit — mniej powierzchni do auditu
+ - 09-05 Testy + i18n + dokumentacja — mniej do udokumentowania
+ - przyszłe milestone'y v1.x — czysty fundament bez OAuth dependencies
+
+tech-stack:
+ added: []
+ patterns:
+ - "Idempotent version-gated migration: Installer::migrate() porównuje get_option('yacht_booking_version') z YACHT_BOOKING_VERSION i wykonuje cleanup tylko przy upgrade"
+ - "Cleanup po cichu — bez admin notice, klient nie musi wykonywać żadnych akcji ręcznych"
+
+key-files:
+ created: []
+ modified:
+ - wp-content/plugins/yacht-booking-system/yacht-booking-system.php
+ - wp-content/plugins/yacht-booking-system/includes/class-yacht-booking.php
+ - wp-content/plugins/yacht-booking-system/includes/class-yacht.php
+ - wp-content/plugins/yacht-booking-system/includes/class-installer.php
+ - wp-content/plugins/yacht-booking-system/admin/class-admin.php
+ - wp-content/plugins/yacht-booking-system/admin/views/yacht-edit.php
+ - wp-content/plugins/yacht-booking-system/admin/class-yacht-list-table.php
+ - wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-import.php
+ - wp-content/plugins/yacht-booking-system/integrations/ical/class-ical-feed.php
+ - wp-content/plugins/yacht-booking-system/uninstall.php
+ deleted:
+ - wp-content/plugins/yacht-booking-system/integrations/google-calendar/class-sync-controller.php
+ - wp-content/plugins/yacht-booking-system/integrations/google-calendar/class-gcal-service.php
+ - wp-content/plugins/yacht-booking-system/integrations/google-calendar/class-oauth-handler.php
+
+key-decisions:
+ - "Per-yacht bookings z source 'ical_import' — usuwamy w migration (klient ponownie zaimportuje przez globalny mechanizm), zamiast migrować je do source 'ical_import_global'"
+ - "Migration idempotentna przez version_compare — kolejna aktywacja nie powtarza cleanup"
+ - "Bez admin notice po migration — klient wybrał 'po cichu'"
+ - "Per-yacht feedy iCal usunięte całkowicie (nie zostawione jako fallback) — model mentalny: jeden mechanizm"
+
+patterns-established:
+ - "Wersja-aware cleanup w Installer::install() — pattern do reuse w przyszłych planach refaktorujących/usuwających legacy"
+ - "FQN namespace + lazy require_once dla klas używanych w activation hook (przed ładowaniem bootstrap)"
+
+duration: ~30min
+started: 2026-05-06T11:30:00Z
+completed: 2026-05-06T12:00:00Z
+---
+
+# Phase 9 Plan 03: Cleanup OAuth + per-yacht iCal Summary
+
+**Wycofanie OAuth (3 pliki + cała sekcja UI), per-yacht iCal feed/import (pole "Google Calendar ID", "iCal Import URL", kolumna w yacht-list, cron) z bezgłośną migracją bazy — plugin sprowadzony do jednego mechanizmu sync (globalny iCal z 09-02).**
+
+## Performance
+
+| Metric | Value |
+|--------|-------|
+| Duration | ~30min |
+| Tasks | 3 auto + 1 human-verify (wszystkie zaakceptowane) |
+| Files modified | 10 |
+| Files deleted | 3 |
+| Lines removed | ~700+ (3 OAuth files + per-yacht iCal + UI + handlers) |
+
+## Acceptance Criteria Results
+
+| Criterion | Status | Notes |
+|-----------|--------|-------|
+| AC-1: Pliki OAuth usunięte i nie ładowane | Pass | 3 pliki + katalog `integrations/google-calendar/` usunięte. Bootstrap (`yacht-booking-system.php`, `class-yacht-booking.php`) bez require/wiring. PHP lint OK. |
+| AC-2: Sekcja OAuth zniknęła z Settings | Pass | `render_google_calendar_settings()` zawiera tylko nagłówek + sekcję "Globalna synchronizacja iCal". Usunięte: handle_oauth_callback, save_gcal_credentials, callback/disconnect/credentials w process_settings_save, notice connected/disconnected. |
+| AC-3: Pola per-jacht zniknęły z yacht-edit i yacht-list | Pass | yacht-edit nie ma "Google Calendar ID", "iCal Import URL", "iCal Feed URL". Ma tylko Alias. Lista jachtów bez kolumny "Google Calendar". |
+| AC-4: Per-jacht iCal cron i feed wyłączone | Pass | Cron `yacht_booking_ical_import` nie rejestrowany. Rewrite rule `^yacht-ical/(\d+)/...` usunięta — URL per-yacht zwróci 404. Globalny `^yacht-ical-global/...` aktywny. |
+| AC-5: Cleanup migration | Pass | `Installer::migrate()` — version-gated. Kasuje 4 meta keys (delete_post_meta_by_key), 6 opcji OAuth, bookings z source 'ical_import' (Availability::clear_booking_availability + wp_delete_post), 5 cron hooków. |
+| AC-6: Globalna iCal sync bez regresji | Pass | Boundary chronił z 09-02 — wszystkie globalne metody/UI/cron nietknięte, alias zachowany. |
+| AC-7: uninstall.php zaktualizowany | Pass | Dodane delete_option dla `yacht_booking_global_ical_*`, `yacht_booking_enabled`. Defensywne delete_option dla legacy gcal_* keys. |
+
+## Accomplishments
+
+- Plugin ma JEDEN spójny mechanizm sync z GCal (globalny iCal), zamiast 4 (OAuth push, OAuth pull, per-yacht iCal feed, per-yacht iCal import) — drastyczne zmniejszenie powierzchni mentalnej i bug surface
+- Czysta baza po automatycznej migracji — klient nie wykonuje żadnych akcji ręcznych, niepotrzebne meta i opcje znikają przy następnej aktywacji pluginu
+- Codebase odchudzony o ~700+ linii martwego kodu (3 pliki OAuth/Sync + UI/CRUD per-yacht iCal)
+- Zero regresji w globalnej iCal sync ani frontendzie (kalendarz widget, REST API, CPT, availability) — boundaries respektowane
+- Plugin gotowy produkcyjnie w warstwie integracji GCal — pozostają tylko 09-04 (security audit) i 09-05 (testy + i18n + docs) do zamknięcia milestone v1.0
+
+## Files Created/Modified
+
+| File | Change | Purpose |
+|------|--------|---------|
+| `integrations/google-calendar/class-sync-controller.php` | **Deleted** | OAuth orchestrator usunięty całkowicie |
+| `integrations/google-calendar/class-gcal-service.php` | **Deleted** | Google Calendar API calls usunięte |
+| `integrations/google-calendar/class-oauth-handler.php` | **Deleted** | OAuth tokens + credentials usunięte |
+| `yacht-booking-system.php` | Modified | Wycięte require + setup_cron + clear_cron Sync_Controller w activate/deactivate hooks |
+| `includes/class-yacht-booking.php` | Modified | Wycięte 3 require_once + Sync_Controller::get_instance/register_cron_actions w `load_dependencies()` |
+| `includes/class-yacht.php` | Modified | Usunięte `get_gcal_id()`, `update_gcal_id()` |
+| `includes/class-installer.php` | Modified | Dodana `migrate()` (version-gated, idempotent), usunięty `yacht_booking_gcal_sync_enabled` z create_options |
+| `admin/class-admin.php` | Modified | Wycięte: handle_oauth_callback, save_gcal_credentials, OAuth bloki w process_settings_save i display_admin_notices, cała sekcja OAuth w render_google_calendar_settings, save_yacht zapisuje tylko alias |
+| `admin/views/yacht-edit.php` | Modified | Usunięte 3 wiersze tabeli (Google Calendar ID, iCal Import URL, iCal Feed URL) + 4 zmienne PHP. Pozostaje Alias. |
+| `admin/class-yacht-list-table.php` | Modified | Usunięta kolumna `gcal` z get_columns + metoda column_gcal |
+| `integrations/ical/class-ical-import.php` | Modified | Usunięte: const IMPORT_SOURCE, run_import, get_import_url, set_import_url, import_for_yacht, get_existing_import_map, upsert_booking, get_last_import_time. Cron 'yacht_booking_ical_import' nie rejestrowany. |
+| `integrations/ical/class-ical-feed.php` | Modified | Usunięte: rewrite rule per-yacht, query var yacht_ical_id, branch per-yacht w handle_feed_request, output_ics, get_feed_token, regenerate_token, get_feed_url. Pozostaje globalna ścieżka. |
+| `uninstall.php` | Modified | Dodane: yacht_booking_global_ical_*, yacht_booking_enabled. Defensywne legacy gcal_* delete_option. |
+
+## Decisions Made
+
+| Decision | Rationale | Impact |
+|----------|-----------|--------|
+| Usunięcie per-yacht bookings 'ical_import' w migration | Bez per-yacht importu nie będą odświeżane — lepsze UX usunąć i pozwolić ponowny globalny import niż zostawić "skamieniałe" blokady | Klient po deploy uruchamia "Importuj teraz" raz i ma świeże dane |
+| Migration version-gated z version_compare | Idempotencja — kolejna aktywacja po pierwszej nie powtarza cleanup, bezpieczne dla repeated activate/deactivate | Zero ryzyka przy ręcznych re-aktywacjach lub WP auto-update |
+| FQN dla `\YachtBooking\Availability` w migrate() | Activation hook ładuje Installer ręcznie przed bootstrap — autoloader może być nieaktywny | Lazy require_once jako fallback wewnątrz pętli `if (!class_exists)` |
+| Defensywne delete_option dla legacy gcal_* w uninstall.php | Migration usuwa je przy upgrade, ale uninstall.php może być uruchomiony bezpośrednio na starszej wersji bez upgrade | Wszystkie ścieżki sprzątają legacy keys |
+| Brak admin notice po migration | Klient w fazie planowania wybrał "po cichu" — żeby nie zaprzątać uwagi | Płynne UX, klient zauważy tylko że stare pola znikły |
+
+## Deviations from Plan
+
+### Summary
+
+| Type | Count | Impact |
+|------|-------|--------|
+| Auto-fixed | 1 | Drobny |
+| Scope additions | 0 | — |
+| Deferred | 0 | — |
+
+**Total impact:** Plan wykonany w pełni, jeden drobny auto-fix dla bezpieczeństwa.
+
+### Auto-fixed Issues
+
+**1. [Migration safety] Availability class loading w activation hook**
+- **Found during:** Task 3 (Installer::migrate)
+- **Issue:** Migration wywołuje `\YachtBooking\Availability::clear_booking_availability()`, ale Installer jest ładowany przez activation hook PRZED `plugins_loaded` (gdzie autoloader spl_autoload_register() jest rejestrowany). Pierwotna implementacja zakładała że Availability jest dostępne automatycznie.
+- **Fix:** Dodany guard `if ( ! class_exists( '\YachtBooking\Availability' ) ) { require_once YACHT_BOOKING_PLUGIN_DIR . 'includes/class-availability.php'; }` przed pętlą usuwającą stale bookings. Dodatkowo otoczone `if (!empty($stale_bookings))` żeby nie ładować pliku gdy nie ma nic do usunięcia.
+- **Files:** includes/class-installer.php
+- **Verification:** PHP lint OK; logika idempotentna; w testach klient potwierdził że upgrade nie powoduje fatal error.
+
+### Deferred Items
+
+Brak.
+
+## Issues Encountered
+
+| Issue | Resolution |
+|-------|------------|
+| Brak konkretnych fatal errors lub regresji | — |
+
+## Reproduction Path (do testów regresyjnych)
+
+1. Po deploy FTP — aktywuj plugin (lub deactivate→activate na produkcji). Wyzwoli `Installer::install()` → `migrate()`.
+2. Sprawdź WP Admin → Settings → Google Calendar: tylko sekcja "Globalna synchronizacja iCal" (z 09-02).
+3. Sprawdź edycję jachtu: brak "Google Calendar ID"/"iCal Import URL"/"iCal Feed URL". Jest tylko "Alias dla Google Calendar".
+4. Lista jachtów: brak kolumny "Google Calendar".
+5. URL `/yacht-ical/X/token.ics` → 404. URL `/yacht-ical-global/{token}.ics` → poprawny .ics.
+6. phpMyAdmin: brak meta `_yacht_gcal_id`/`_yacht_ical_*`, brak opcji `yacht_booking_gcal_*` (poza globalnymi `*_global_ical_*`), brak bookingów z source `ical_import`.
+7. Druga aktywacja → migrate() nie powtarza cleanup (version match).
+
+## Next Phase Readiness
+
+**Ready:**
+- Codebase czysty, 1 mechanizm GCal sync
+- Migration mechanizm gotowy do reuse w przyszłych planach refaktorujących
+- Plugin z punktu widzenia integracji GCal jest produkcyjnie skończony
+
+**Concerns:**
+- Klient po deploy musi pamiętać o uruchomieniu globalnego "Importuj teraz" w Settings, żeby ponownie zaimportować eventy z Google Calendar (stare per-yacht zostały skasowane). Mitigacja: zostało udokumentowane w checkpoint verify steps.
+- `yacht_booking_capabilities_added` flag jest sprawdzany raz w `Yacht_Booking::add_custom_capabilities()` — zachowany w uninstall.php. Nie ruszamy go.
+
+**Blockers:** None
+
+## Otwarte kwestie milestone v1.0
+
+- 09-04: Security audit (nonce, escaping, SQL prepare, capabilities)
+- 09-05: Testy + tłumaczenia .pot/.po/.mo + dokumentacja użytkownika i dewelopera
+
+---
+*Phase: 09-finalizacja, Plan: 03*
+*Completed: 2026-05-06*
diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json
new file mode 100644
index 0000000..3cd3bb8
--- /dev/null
+++ b/.vscode/ftp-kr.sync.cache.json
@@ -0,0 +1,208 @@
+{
+ "ftp://host117523.hostido.net.pl@www@jachty3.pagedev.pl": {
+ "public_html": {
+ ".htaccess": {
+ "type": "-",
+ "size": 665,
+ "lmtime": 0,
+ "modified": false
+ },
+ "index.php": {
+ "type": "-",
+ "size": 405,
+ "lmtime": 0,
+ "modified": false
+ },
+ "license.txt": {
+ "type": "-",
+ "size": 19903,
+ "lmtime": 0,
+ "modified": false
+ },
+ "__MACOSX": {},
+ ".planning": {
+ "codebase": {
+ "ARCHITECTURE.md": {
+ "type": "-",
+ "size": 12531,
+ "lmtime": 1778017398128,
+ "modified": false
+ },
+ "CONVENTIONS.md": {
+ "type": "-",
+ "size": 9024,
+ "lmtime": 1778017439483,
+ "modified": false
+ },
+ "INTEGRATIONS.md": {
+ "type": "-",
+ "size": 13136,
+ "lmtime": 1778017420536,
+ "modified": false
+ },
+ "STACK.md": {
+ "type": "-",
+ "size": 3636,
+ "lmtime": 1778017353336,
+ "modified": false
+ },
+ "STRUCTURE.md": {
+ "type": "-",
+ "size": 8663,
+ "lmtime": 1778017445887,
+ "modified": false
+ },
+ "TESTING.md": {
+ "type": "-",
+ "size": 6979,
+ "lmtime": 1778017485643,
+ "modified": false
+ }
+ }
+ },
+ "readme.html": {
+ "type": "-",
+ "size": 7425,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-activate.php": {
+ "type": "-",
+ "size": 7349,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-admin": {},
+ "wp-blog-header.php": {
+ "type": "-",
+ "size": 351,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-comments-post.php": {
+ "type": "-",
+ "size": 2323,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-config.php": {
+ "type": "-",
+ "size": 3764,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-config-sample.php": {
+ "type": "-",
+ "size": 3339,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-content": {
+ "plugins": {
+ "yacht-booking-system": {
+ "frontend": {
+ "assets": {
+ "js": {
+ "calendar.js": {
+ "type": "-",
+ "size": 15422,
+ "lmtime": 1778018642248,
+ "modified": false
+ }
+ },
+ "css": {
+ "calendar.css": {
+ "type": "-",
+ "size": 12984,
+ "lmtime": 1778019168166,
+ "modified": false
+ },
+ "calendar.css.map": {
+ "type": "-",
+ "size": 4874,
+ "lmtime": 0,
+ "modified": false
+ },
+ "calendar.scss": {
+ "type": "-",
+ "size": 15260,
+ "lmtime": 1778019161179,
+ "modified": false
+ }
+ }
+ },
+ "class-calendar-widget.php": {
+ "type": "-",
+ "size": 21040,
+ "lmtime": 1778019128363,
+ "modified": false
+ },
+ "class-shortcode.php": {
+ "type": "-",
+ "size": 14528,
+ "lmtime": 0,
+ "modified": false
+ }
+ }
+ }
+ }
+ },
+ "wp-cron.php": {
+ "type": "-",
+ "size": 5617,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-includes": {},
+ "wp-links-opml.php": {
+ "type": "-",
+ "size": 2493,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-load.php": {
+ "type": "-",
+ "size": 3937,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-login.php": {
+ "type": "-",
+ "size": 51437,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-mail.php": {
+ "type": "-",
+ "size": 8727,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-settings.php": {
+ "type": "-",
+ "size": 31055,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-signup.php": {
+ "type": "-",
+ "size": 34516,
+ "lmtime": 0,
+ "modified": false
+ },
+ "wp-trackback.php": {
+ "type": "-",
+ "size": 5214,
+ "lmtime": 0,
+ "modified": false
+ },
+ "xmlrpc.php": {
+ "type": "-",
+ "size": 3205,
+ "lmtime": 0,
+ "modified": false
+ }
+ }
+ },
+ "$version": 1
+}
\ No newline at end of file
diff --git a/wp-content/plugins/yacht-booking-system/admin/class-admin.php b/wp-content/plugins/yacht-booking-system/admin/class-admin.php
index d34fb62..eee093f 100644
--- a/wp-content/plugins/yacht-booking-system/admin/class-admin.php
+++ b/wp-content/plugins/yacht-booking-system/admin/class-admin.php
@@ -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 {
-
-
-
-
@@ -174,6 +155,38 @@ class Admin {
+