From 01581a1dd80c9f2216a9b4ba5fe18943650bb6dc Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Fri, 24 Apr 2026 09:33:20 +0200 Subject: [PATCH] update --- .paul/ROADMAP.md | 7 +- .paul/STATE.md | 64 +---- .paul/STATE.md.bak | 66 +++++ .paul/changelog/2026-04-24.md | 16 ++ .paul/governance/governance_2026-04-10.jsonl | 15 + .../02-admin-panel-upgrade/02-02-PLAN.md | 224 +++++++++++++++ .../02-admin-panel-upgrade/02-02-SUMMARY.md | 39 +++ .vscode/ftp-kr.sync.cache.json | 12 +- config/routes.php | 3 + src/Controllers/SiteController.php | 81 ++++++ src/Services/OpenAIService.php | 40 ++- src/Services/WordPressService.php | 262 +++++++++++++++++- templates/sites/comments.php | 206 ++++++++++++++ templates/sites/dashboard.php | 26 ++ 14 files changed, 986 insertions(+), 75 deletions(-) create mode 100644 .paul/STATE.md.bak create mode 100644 .paul/changelog/2026-04-24.md create mode 100644 .paul/phases/02-admin-panel-upgrade/02-02-PLAN.md create mode 100644 .paul/phases/02-admin-panel-upgrade/02-02-SUMMARY.md create mode 100644 templates/sites/comments.php diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index a52cd24..f1dbb4f 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -13,7 +13,7 @@ Phases: 1 of 2 complete | Phase | Name | Plans | Status | Completed | |-------|------|-------|--------|-----------| | 1 | StatLink Autolinking | 1 | Complete ✓ | 2026-04-09 | -| 2 | Admin Panel Upgrade | 1 | Planning | - | +| 2 | Admin Panel Upgrade | 2 | Planning | - | ## Phase Details @@ -28,7 +28,7 @@ Phases: 1 of 2 complete ### Phase 2: Admin Panel Upgrade -**Goal:** Panel migracji bazy danych, nowoczesny sidebar (jak orderPRO), lista publikacji StatLink. +**Goal:** Panel migracji bazy danych, nowoczesny sidebar (jak orderPRO), lista publikacji StatLink oraz zdalne zarzadzanie komentarzami WordPress. **Depends on:** Phase 1 (tabela statlink_links) **Research:** Done (analiza orderPRO: Migrator, sidebar, CSS) @@ -36,9 +36,12 @@ Phases: 1 of 2 complete - Migrator engine (port z orderPRO) + panel /settings/database - Nowy sidebar z grupami, ikonami SVG, collapse - Widok /statlink z listą linkowanych artykułów +- Zdalne wlaczanie/wylaczanie komentowania dla pojedynczej strony WordPress +- Lista komentarzy z danego serwisu z mozliwoscia usuwania **Plans:** - [ ] 02-01: Migrator + sidebar + widok StatLink +- [ ] 02-02: Zdalne zarzadzanie komentarzami WordPress --- *Roadmap created: 2026-04-09* diff --git a/.paul/STATE.md b/.paul/STATE.md index a0a81df..737899d 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -1,65 +1,21 @@ -# Project State - -## Project Reference - -See: .paul/PROJECT.md (updated 2026-04-09) - -**Core value:** Zautomatyzowane tworzenie zaplecza SEO -**Current focus:** Phase 2 — Admin Panel Upgrade - ## Current Position -Milestone: v0.1 Initial Release -Phase: 2 of 2 (Admin Panel Upgrade) — Planning -Plan: 02-01 created, awaiting approval -Status: PLAN created, ready for APPLY -Last activity: 2026-04-09 — Phase 1 UNIFY completed, bugfixes applied - -Progress: -- Milestone: [████░░░░░░] 40% -- Phase 1: [██████████] 100% ✓ -- Phase 2: [░░░░░░░░░░] 0% +Phase: 02-admin-panel-upgrade — In Progress +Plan: 02-02 complete +Status: UNIFY complete. Loop complete — ready for next plan. +Last activity: 2026-04-24T07:11:11.932Z ## Loop Position -**Phase 1 (StatLink Auto-Linking):** +Current loop state: ``` PLAN ──▶ APPLY ──▶ UNIFY - ✓ ✓ ✓ [Phase 1 complete] + ✓ ✓ ✓ [Loop complete — ready for next plan] ``` -**Phase 2 (Admin Panel Upgrade):** -``` -PLAN ──▶ APPLY ──▶ UNIFY - ✓ ○ ○ [Plan 02-01 created, awaiting approval] -``` - -## Accumulated Context - -### Decisions -- StatLink.pl integration via Guzzle HTTP (cookie-based session) -- Login field name: "zaloguj" (not "loguj"), needs GET homepage first -- ilosc_dziennie: 0.02 (1 co 2 dni) -- Migrator: port z orderPRO z lock mechanism -- Sidebar: adaptacja orderPRO design do backPRO -- Anchor sanitization: Polish diacritics must be transliterated to ASCII for StatLink -- json_encode needs JSON_INVALID_UTF8_SUBSTITUTE when outputting scraped HTML -- OPcache reset required after FTP deploy for changes to take effect -- StatLink timeouts: connect_timeout=60s, timeout=120s, PHP set_time_limit=300s - -### Deferred Issues -- StatLink: no max retry count for permanently failing links (could block queue) -- StatLink: cron not yet configured on server (only manual token URL trigger) - -### Blockers/Concerns -None. - ## Session Continuity -Last session: 2026-04-09 -Stopped at: Phase 1 UNIFY complete, Phase 2 Plan 02-01 awaiting approval -Next action: Review and approve plan 02-01, then run /paul:apply -Resume file: .paul/phases/02-admin-panel-upgrade/02-01-PLAN.md - ---- -*STATE.md — Updated after every significant action* +Last session: 2026-04-24 +Stopped at: Plan 02-02 complete +Next action: paul_workflow('plan') for next plan +Resume file: .paul/phases/02-admin-panel-upgrade/02-02-SUMMARY.md \ No newline at end of file diff --git a/.paul/STATE.md.bak b/.paul/STATE.md.bak new file mode 100644 index 0000000..261f30b --- /dev/null +++ b/.paul/STATE.md.bak @@ -0,0 +1,66 @@ +# Project State + +## Project Reference + +See: .paul/PROJECT.md (updated 2026-04-09) + +**Core value:** Zautomatyzowane tworzenie zaplecza SEO +**Current focus:** Phase 2 — Admin Panel Upgrade + +## Current Position + +Milestone: v0.1 Initial Release +Phase: 2 of 2 (Admin Panel Upgrade) — Planning +Plan: 02-02 applied, awaiting UNIFY (depends on 02-01) +Status: APPLY complete — 3/3 PASS, ready for UNIFY +Last activity: 2026-04-24 - APPLY complete for .paul/phases/02-admin-panel-upgrade/02-02-PLAN.md + +Progress: +- Milestone: [████░░░░░░] 40% +- Phase 1: [██████████] 100% ✓ +- Phase 2: [░░░░░░░░░░] 0% + +## Loop Position + +**Phase 1 (StatLink Auto-Linking):** +``` +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ✓ ○ [APPLY complete, awaiting UNIFY] +``` + +**Phase 2 (Admin Panel Upgrade):** +``` +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ○ ○ [Plan 02-02 created, awaiting approval; 02-01 still pending] +``` + +## Accumulated Context + +### Decisions +- StatLink.pl integration via Guzzle HTTP (cookie-based session) +- Login field name: "zaloguj" (not "loguj"), needs GET homepage first +- ilosc_dziennie: 0.02 (1 co 2 dni) +- Migrator: port z orderPRO z lock mechanism +- Sidebar: adaptacja orderPRO design do backPRO +- Anchor sanitization: Polish diacritics must be transliterated to ASCII for StatLink +- json_encode needs JSON_INVALID_UTF8_SUBSTITUTE when outputting scraped HTML +- OPcache reset required after FTP deploy for changes to take effect +- WordPress comment management should use the existing BackPRO remote service for site options and WP REST API for comment list/delete. +- StatLink timeouts: connect_timeout=60s, timeout=120s, PHP set_time_limit=300s + +### Deferred Issues +- StatLink: no max retry count for permanently failing links (could block queue) +- StatLink: cron not yet configured on server (only manual token URL trigger) + +### Blockers/Concerns +None. + +## Session Continuity + +Last session: 2026-04-09 +Stopped at: Phase 2 Plan 02-02 APPLY complete +Next action: Run $paul-unify .paul/phases/02-admin-panel-upgrade/02-02-PLAN.md +Resume file: .paul/phases/02-admin-panel-upgrade/02-02-PLAN.md + +--- +*STATE.md — Updated after every significant action* diff --git a/.paul/changelog/2026-04-24.md b/.paul/changelog/2026-04-24.md new file mode 100644 index 0000000..a3dadb4 --- /dev/null +++ b/.paul/changelog/2026-04-24.md @@ -0,0 +1,16 @@ +# 2026-04-24 + +## Co zrobiono + +- [02-admin-panel-upgrade, Plan 02] +- Task 1: Rozszerzyc WordPressService o operacje komentarzy +- Task 2: Dodac akcje kontrolera i trasy komentarzy +- Task 3: Zbudowac UI komentarzy dla pojedynczej strony + +## Zmienione pliki + +- `src/Services/WordPressService.php` +- `src/Controllers/SiteController.php` +- `config/routes.php` +- `templates/sites/dashboard.php` +- `templates/sites/comments.php` \ No newline at end of file diff --git a/.paul/governance/governance_2026-04-10.jsonl b/.paul/governance/governance_2026-04-10.jsonl index 644ab94..febc41d 100644 --- a/.paul/governance/governance_2026-04-10.jsonl +++ b/.paul/governance/governance_2026-04-10.jsonl @@ -2,3 +2,18 @@ {"ts":"2026-04-10T07:18:51Z","tool":"Bash","cmd":"curl -s \"ftp://host700513.hostido.net.pl/public_html/storage/logs/openai_2026-04-10.log\" --user \"www@backpro.projectpro.pl:WGnT4LEn6dLYKvDkXZdd\" 2>&1\",\"timeout\":15000,\"description\":\"Download","cwd":"/c/visual studio code/projekty/backPRO"} {"ts":"2026-04-10T07:18:54Z","tool":"Bash","cmd":"curl -s \"ftp://host700513.hostido.net.pl/public_html/storage/logs/openai_2026-04-09.log\" --user \"www@backpro.projectpro.pl:WGnT4LEn6dLYKvDkXZdd\" 2>&1\",\"timeout\":15000,\"description\":\"Download","cwd":"/c/visual studio code/projekty/backPRO"} {"ts":"2026-04-10T07:19:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:25:16Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:25:22Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:32:25Z","tool":"Bash","cmd":"php -r \"\\n\\\\$pdo = new PDO('mysql:host=host700513.hostido.net.pl;dbname=host700513_backpro;charset=utf8mb4', 'host700513_backpro', 'Mq9wH2B8KPeQh2wQ32Ya');\\n\\\\$stmt = \\\\$pdo->query(\\\\\"SELE","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:32:59Z","tool":"Bash","cmd":"php -r \"\\n\\\\$pdo = new PDO('mysql:host=host700513.hostido.net.pl;dbname=host700513_backpro;charset=utf8mb4', 'host700513_backpro', 'Mq9wH2B8KPeQh2wQ32Ya');\\n\\\\$stmt = \\\\$pdo->query(\\\\\"SELE","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:33:42Z","tool":"Bash","cmd":"php -r \"\\n\\\\$pdo = new PDO('mysql:host=host700513.hostido.net.pl;dbname=host700513_backpro;charset=utf8mb4', 'host700513_backpro', 'Mq9wH2B8KPeQh2wQ32Ya');\\n\\\\$stmt = \\\\$pdo->query(\\\\\"SELE","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:34:19Z","tool":"Bash","cmd":"php -r \"\\n\\\\$pdo = new PDO('mysql:host=host700513.hostido.net.pl;dbname=host700513_backpro;charset=utf8mb4', 'host700513_backpro', 'Mq9wH2B8KPeQh2wQ32Ya');\\n\\\\$stmt = \\\\$pdo->query(\\\\\"SELE","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:34:41Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:34:52Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:34:58Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:35:10Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:36:29Z","tool":"Bash","cmd":"php -r \"\\n\\\\$pdo = new PDO('mysql:host=host700513.hostido.net.pl;dbname=host700513_backpro;charset=utf8mb4', 'host700513_backpro', 'Mq9wH2B8KPeQh2wQ32Ya');\\n\\\\$stmt = \\\\$pdo->query(\\\\\"SELE","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:36:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:37:38Z","tool":"Bash","cmd":"php -r \"\\n\\\\$pdo = new PDO('mysql:host=host700513.hostido.net.pl;dbname=host700513_backpro;charset=utf8mb4', 'host700513_backpro', 'Mq9wH2B8KPeQh2wQ32Ya');\\n\\\\$stmt = \\\\$pdo->query(\\\\\"SELE","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:43:04Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} +{"ts":"2026-04-10T18:43:10Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\backPRO\\\\src\\\\Services\\\\OpenAIService.php","cwd":"/c/visual studio code/projekty/backPRO"} diff --git a/.paul/phases/02-admin-panel-upgrade/02-02-PLAN.md b/.paul/phases/02-admin-panel-upgrade/02-02-PLAN.md new file mode 100644 index 0000000..476f0aa --- /dev/null +++ b/.paul/phases/02-admin-panel-upgrade/02-02-PLAN.md @@ -0,0 +1,224 @@ +--- +phase: 02-admin-panel-upgrade +plan: 02 +type: execute +wave: 2 +depends_on: ["02-01"] +files_modified: + - src/Services/WordPressService.php + - src/Controllers/SiteController.php + - templates/sites/dashboard.php + - templates/sites/comments.php + - config/routes.php +autonomous: true +delegation: off +--- + + +## Goal +Dodac do zarzadzania pojedyncza strona WordPress zdalna kontrole komentarzy: +1. Wlaczenie i wylaczenie mozliwosci komentowania na danym serwisie. +2. Pobranie listy komentarzy z danego serwisu. +3. Usuwanie komentarzy z poziomu BackPRO. + +## Purpose +BackPRO ma centralnie zarzadzac siecia stron zapleczowych. Komentarze sa ryzykiem moderacyjnym i spamowym, wiec operator powinien moc szybko wylaczyc komentowanie oraz usuwac niechciane komentarze bez logowania sie do kazdego panelu WordPress osobno. + +## Output +- Metody w `WordPressService` do statusu komentarzy, zmiany ustawien, listowania i usuwania komentarzy. +- Akcje w `SiteController` dla panelu komentarzy i operacji POST. +- Widok `/sites/{id}/comments` z tabela komentarzy, filtrami i akcja usuniecia. +- Karta/status komentarzy na dashboardzie strony. +- Nowe trasy w `config/routes.php`. + + + +## Project Context +@.paul/PROJECT.md +@.paul/ROADMAP.md +@.paul/STATE.md + +## Prior Work +@.paul/phases/02-admin-panel-upgrade/02-01-PLAN.md + +## Source Files +@src/Services/WordPressService.php +@src/Controllers/SiteController.php +@templates/sites/dashboard.php +@templates/sites/index.php +@config/routes.php +@src/Core/Controller.php + + + + +## AC-1: Status i przelaczanie komentowania +```gherkin +Given uzytkownik jest zalogowany w BackPRO i ma skonfigurowany serwis WordPress z plikiem zdalnym BackPRO +When przechodzi do dashboardu strony +Then widzi aktualny status komentowania dla nowych wpisow +And moze wlaczyc lub wylaczyc komentowanie z poziomu BackPRO +And po akcji widzi komunikat sukcesu albo konkretny blad polaczenia +``` + +## AC-2: Lista komentarzy z serwisu +```gherkin +Given uzytkownik jest zalogowany i strona ma poprawne dane WordPress API +When przechodzi do /sites/{id}/comments +Then widzi liste komentarzy z WordPressa z autorem, trescia, data, statusem i linkiem do wpisu +And moze filtrowac komentarze po statusie all/hold/approve/spam/trash +And widok poprawnie obsluguje pusta liste i blad pobrania danych +``` + +## AC-3: Usuwanie komentarzy +```gherkin +Given uzytkownik widzi komentarz na liscie komentarzy strony +When klika usun i potwierdza operacje +Then BackPRO usuwa komentarz przez WordPress API +And uzytkownik wraca do listy komentarzy z komunikatem wyniku +And bledy 401/403/404 sa obsluzone czytelnym komunikatem +``` + + + + + + + Task 1: Rozszerzyc WordPressService o operacje komentarzy + src/Services/WordPressService.php + + Dodac publiczne metody: + - getCommentSettings(array $site): array + - Uzyc callRemoteService($site, 'get_comment_settings'). + - Jezeli endpoint jest nieaktualny, wywolac ensureRemoteService(), odswiezyc site z bazy i ponowic probe. + - Zwracac success, default_comment_status, comments_enabled, message. + - setCommentsEnabled(array $site, bool $enabled): array + - Uzyc callRemoteService($site, 'set_comment_settings', ['comments_enabled' => '1'/'0']). + - Endpoint po stronie WordPress ma ustawiac option default_comment_status na open/closed. + - Nie zmieniac masowo istniejacych postow w tym planie, zeby nie zaskoczyc uzytkownika utrata historii dyskusji. + - getComments(array $site, string $status = 'all', int $page = 1, int $perPage = 20): array + - Uzyc WP REST `wp/v2/comments` z auth z buildAuthOption(). + - Parametry: status, page, per_page, orderby=date, order=desc, context=edit. + - Zwracac success, comments, page, total_pages, total, message. + - Dla 401/403 zwrocic komunikat o braku uprawnien aplikacyjnego hasla/API uzytkownika. + - deleteComment(array $site, int $commentId): array + - Uzyc WP REST DELETE `wp/v2/comments/{id}` z auth i query force=true. + - 404 traktowac jako czytelny blad "komentarz nie istnieje" albo sukces idempotentny tylko jesli API jasno zwraca deleted=true. + + Zaktualizowac BACKPRO_REMOTE_SERVICE_VERSION do kolejnej wersji i tresc getBackproRemoteServiceContent(): + - ping ma zwracac nowa wersje. + - action=get_comment_settings zwraca default_comment_status i comments_enabled. + - action=set_comment_settings waliduje comments_enabled i zapisuje update_option('default_comment_status', 'open'/'closed'). + + Zachowac istniejace fallbacki requestWp() i nie ruszac logiki publikacji, mediow, permalinkow ani indeksowania. + + php -l src/Services/WordPressService.php + AC-1, AC-2 i AC-3 maja warstwe komunikacji z WordPressem. + + + + Task 2: Dodac akcje kontrolera i trasy komentarzy + src/Controllers/SiteController.php, config/routes.php + + Dodac do SiteController: + - comments(string $id): void + - Auth::requireLogin(). + - Pobrac Site::find(), obsluzyc brak strony. + - Odczytac status z query `status` z whitelista: all, hold, approve, spam, trash. + - Odczytac page jako int >= 1. + - Wywolac WordPressService::getCommentSettings() oraz getComments(). + - Renderowac `sites/comments` z site, commentSettings, commentsResult, selectedStatus, page. + - updateCommentsEnabled(string $id): void + - POST z polem enabled=1/0. + - Wywolac setCommentsEnabled(). + - Flash success/danger i redirect do `/sites/{id}/comments`. + - deleteComment(string $id, string $commentId): void + - Walidowac commentId > 0. + - Wywolac deleteComment(). + - Flash success/danger i redirect z zachowaniem statusu, jesli podany. + + Dodac trasy: + - GET `/sites/{id}/comments` -> SiteController@comments + - POST `/sites/{id}/comments/settings` -> SiteController@updateCommentsEnabled + - POST `/sites/{id}/comments/{commentId}/delete` -> SiteController@deleteComment + + Kontroler ma zostac cienki: mapuje request, wywoluje WordPressService, ustawia flash i przekazuje dane do widoku. Nie wkladac logiki REST API do kontrolera. + + php -l src/Controllers/SiteController.php oraz php -l config/routes.php + AC-1, AC-2 i AC-3 dostepne przez routing BackPRO. + + + + Task 3: Zbudowac UI komentarzy dla pojedynczej strony + templates/sites/dashboard.php, templates/sites/comments.php + + Utworzyc `templates/sites/comments.php`: + - Naglowek: nazwa strony, link powrotu do dashboardu i listy stron. + - Karta "Komentowanie" z badge ON/OFF wedlug commentSettings. + - Dwa formularze POST do `/sites/{id}/comments/settings`: wlacz i wylacz komentarze. + - Krotka informacja, ze przelacznik dotyczy domyslnego komentowania nowych wpisow. + - Filtry statusu jako linki: Wszystkie, Oczekujace, Zatwierdzone, Spam, Kosz. + - Tabela komentarzy: autor, email/URL jesli dostepne, fragment tresci bez HTML, data, status, link do wpisu, akcja usun. + - Usuwanie jako POST z `data-confirm`. + - Paginacja na podstawie total_pages i aktualnej strony. + - Wszystkie dane z WordPressa escape przez htmlspecialchars. + + Zaktualizowac `templates/sites/dashboard.php`: + - Dodac karte lub przycisk "Komentarze" w sekcji zarzadzania strona. + - Pokazac status komentowania, jesli SiteController::dashboard przekaze commentSettings. + - Link do `/sites/{id}/comments`. + + Zaktualizowac SiteController::dashboard w ramach Task 2 lub tego taska: + - pobrac commentSettings przez WordPressService i przekazac do widoku dashboardu. + + Nie tworzyc osobnego globalnego ekranu komentarzy dla wszystkich stron w tym planie. + + php -l templates/sites/dashboard.php oraz php -l templates/sites/comments.php + AC-1, AC-2 i AC-3 maja kompletny interfejs w panelu strony. + + + + + + +## DO NOT CHANGE +- src/Services/PublisherService.php +- src/Models/Article.php +- src/Models/Topic.php +- migrations/* (brak zmian schematu bazy w tym planie) +- templates/articles/* (komentarze dotycza zarzadzania strona, nie artykulow BackPRO) + +## SCOPE LIMITS +- Przelacznik komentowania dotyczy domyslnego komentowania nowych wpisow (`default_comment_status`), bez masowego zamykania komentarzy w istniejacych postach. +- Nie dodawac moderacji approve/spam/unspam w tym planie; tylko lista i usuwanie. +- Nie cache'owac komentarzy lokalnie w bazie BackPRO. +- Nie dodawac nowych zaleznosci Composer. +- Nie zmieniac sposobu przechowywania danych API/FTP stron. + + + + +Before declaring plan complete: +- [ ] php -l src/Services/WordPressService.php +- [ ] php -l src/Controllers/SiteController.php +- [ ] php -l config/routes.php +- [ ] php -l templates/sites/dashboard.php +- [ ] php -l templates/sites/comments.php +- [ ] /sites/{id}/dashboard pokazuje wejscie do komentarzy i status ustawienia +- [ ] /sites/{id}/comments pokazuje tabele albo czytelny blad pobrania +- [ ] POST ustawienia komentowania zmienia default_comment_status na WordPressie +- [ ] POST usuniecia komentarza usuwa komentarz albo pokazuje czytelny blad +- [ ] Aktualizacja pliku serwisowego BackPRO podnosi wersje remote service + + + +- Operator moze wlaczyc i wylaczyc komentowanie dla wybranej strony. +- Operator widzi komentarze pobrane z danego serwisu WordPress. +- Operator moze usuwac komentarze z BackPRO. +- Bledy polaczenia i uprawnien sa czytelne w UI. +- Brak regresji w publikacji, permalinkach, StatLink i istniejacym dashboardzie strony. + + + +After completion, create `.paul/phases/02-admin-panel-upgrade/02-02-SUMMARY.md` + diff --git a/.paul/phases/02-admin-panel-upgrade/02-02-SUMMARY.md b/.paul/phases/02-admin-panel-upgrade/02-02-SUMMARY.md new file mode 100644 index 0000000..3829df7 --- /dev/null +++ b/.paul/phases/02-admin-panel-upgrade/02-02-SUMMARY.md @@ -0,0 +1,39 @@ +--- +phase: 02-admin-panel-upgrade +plan: 02 +completed: 2026-04-24T07:11:11.932Z +--- + +# Phase 02-02 Summary + +**** + +## Acceptance Criteria Results + +| Criterion | Status | +|-----------|--------| +| Task 1: Rozszerzyc WordPressService o operacje komentarzy | Pass — Dodano status i ustawianie komentarzy przez backpro-remote-service v1.5.0 oraz listowanie/usuwanie komentarzy przez WP REST API. Zweryfikowano php -l src/Services/WordPressService.php. | +| Task 2: Dodac akcje kontrolera i trasy komentarzy | Pass — Dodano akcje comments, updateCommentsEnabled i deleteComment w SiteController oraz trasy /sites/{id}/comments. Zweryfikowano php -l src/Controllers/SiteController.php i php -l config/routes.php. | +| Task 3: Zbudowac UI komentarzy dla pojedynczej strony | Pass — Dodano templates/sites/comments.php oraz kartę/link komentarzy na dashboardzie strony. Zweryfikowano php -l templates/sites/dashboard.php i php -l templates/sites/comments.php. | + +## Accomplishments + +- Task 1: Rozszerzyc WordPressService o operacje komentarzy: Dodano status i ustawianie komentarzy przez backpro-remote-service v1.5.0 oraz listowanie/usuwanie komentarzy przez WP REST API. Zweryfikowano php -l src/Services/WordPressService.php. +- Task 2: Dodac akcje kontrolera i trasy komentarzy: Dodano akcje comments, updateCommentsEnabled i deleteComment w SiteController oraz trasy /sites/{id}/comments. Zweryfikowano php -l src/Controllers/SiteController.php i php -l config/routes.php. +- Task 3: Zbudowac UI komentarzy dla pojedynczej strony: Dodano templates/sites/comments.php oraz kartę/link komentarzy na dashboardzie strony. Zweryfikowano php -l templates/sites/dashboard.php i php -l templates/sites/comments.php. + +## Files Modified + +- `src/Services/WordPressService.php` +- `src/Controllers/SiteController.php` +- `config/routes.php` +- `templates/sites/dashboard.php` +- `templates/sites/comments.php` + +## Deviations + +Live verification against a real WordPress site was not performed in this APPLY; automated PHP lint verification passed for all changed PHP/template files. + +--- +*Phase: 02-admin-panel-upgrade, Plan: 02* +*Completed: 2026-04-24* \ No newline at end of file diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index 8a8f7f9..6fe4047 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -299,6 +299,12 @@ "size": 28906, "lmtime": 1775727816732, "modified": false + }, + "governance_2026-04-10.jsonl": { + "type": "-", + "size": 4580, + "lmtime": 1775846590836, + "modified": false } }, "phases": { @@ -756,9 +762,9 @@ }, "OpenAIService.php": { "type": "-", - "size": 9032, - "lmtime": 1771375416097, - "modified": true + "size": 10203, + "lmtime": 1775846590297, + "modified": false }, "PublisherService.php": { "type": "-", diff --git a/config/routes.php b/config/routes.php index 988012c..34f2820 100644 --- a/config/routes.php +++ b/config/routes.php @@ -32,6 +32,9 @@ $router->post('/sites/{id}', 'SiteController', 'update'); $router->post('/sites/{id}/delete', 'SiteController', 'destroy'); $router->post('/sites/{id}/test', 'SiteController', 'testConnection'); $router->get('/sites/{id}/dashboard', 'SiteController', 'dashboard'); +$router->get('/sites/{id}/comments', 'SiteController', 'comments'); +$router->post('/sites/{id}/comments/settings', 'SiteController', 'updateCommentsEnabled'); +$router->post('/sites/{id}/comments/{commentId}/delete', 'SiteController', 'deleteComment'); $router->post('/sites/{id}/dashboard/permalinks/enable', 'SiteController', 'enablePrettyPermalinks'); $router->post('/sites/{id}/dashboard/remote-service/update', 'SiteController', 'updateRemoteService'); $router->post('/sites/{id}/dashboard/theme/install', 'SiteController', 'installBackproNewsTheme'); diff --git a/src/Controllers/SiteController.php b/src/Controllers/SiteController.php index d6198b6..3189239 100644 --- a/src/Controllers/SiteController.php +++ b/src/Controllers/SiteController.php @@ -215,14 +215,95 @@ class SiteController extends Controller $wp = new WordPressService(); $permalinkStatus = $wp->getPermalinkSettings($site); $remoteServiceStatus = $wp->getRemoteServiceStatus($site); + $commentSettings = $wp->getCommentSettings($site); $this->view('sites/dashboard', [ 'site' => $site, 'permalinkStatus' => $permalinkStatus, 'remoteServiceStatus' => $remoteServiceStatus, + 'commentSettings' => $commentSettings, ]); } + public function comments(string $id): void + { + Auth::requireLogin(); + + $site = Site::find((int) $id); + if (!$site) { + $this->flash('danger', 'Strona nie znaleziona.'); + $this->redirect('/sites'); + return; + } + + $allowedStatuses = ['all', 'hold', 'approve', 'spam', 'trash']; + $selectedStatus = (string) $this->input('status', 'all'); + if (!in_array($selectedStatus, $allowedStatuses, true)) { + $selectedStatus = 'all'; + } + + $page = max(1, (int) $this->input('page', 1)); + $wp = new WordPressService(); + + $this->view('sites/comments', [ + 'site' => $site, + 'commentSettings' => $wp->getCommentSettings($site), + 'commentsResult' => $wp->getComments($site, $selectedStatus, $page), + 'selectedStatus' => $selectedStatus, + 'page' => $page, + ]); + } + + public function updateCommentsEnabled(string $id): void + { + Auth::requireLogin(); + + $site = Site::find((int) $id); + if (!$site) { + $this->flash('danger', 'Strona nie znaleziona.'); + $this->redirect('/sites'); + return; + } + + $enabled = (string) $this->input('enabled', '0') === '1'; + $wp = new WordPressService(); + $result = $wp->setCommentsEnabled($site, $enabled); + + if (!empty($result['success'])) { + $this->flash('success', (string) ($result['message'] ?? 'Zmieniono ustawienia komentarzy.')); + } else { + $this->flash('danger', (string) ($result['message'] ?? 'Nie udalo sie zmienic ustawien komentarzy.')); + } + + $this->redirect("/sites/{$id}/comments"); + } + + public function deleteComment(string $id, string $commentId): void + { + Auth::requireLogin(); + + $site = Site::find((int) $id); + if (!$site) { + $this->flash('danger', 'Strona nie znaleziona.'); + $this->redirect('/sites'); + return; + } + + $wp = new WordPressService(); + $result = $wp->deleteComment($site, (int) $commentId); + + if (!empty($result['success'])) { + $this->flash('success', (string) ($result['message'] ?? 'Komentarz zostal usuniety.')); + } else { + $this->flash('danger', (string) ($result['message'] ?? 'Nie udalo sie usunac komentarza.')); + } + + $status = (string) $this->input('status', 'all'); + $allowedStatuses = ['all', 'hold', 'approve', 'spam', 'trash']; + $statusQuery = in_array($status, $allowedStatuses, true) ? '?status=' . urlencode($status) : ''; + $this->redirect("/sites/{$id}/comments{$statusQuery}"); + } + public function seoPanel(string $id): void { Auth::requireLogin(); diff --git a/src/Services/OpenAIService.php b/src/Services/OpenAIService.php index 8c6bbc0..b14060e 100644 --- a/src/Services/OpenAIService.php +++ b/src/Services/OpenAIService.php @@ -57,7 +57,7 @@ class OpenAIService $qualityFeedback = ''; $lastPrompt = ''; - for ($attempt = 1; $attempt <= 2; $attempt++) { + for ($attempt = 1; $attempt <= 3; $attempt++) { $userPrompt = $this->buildUserPrompt( $topicName, $topicDescription, @@ -105,7 +105,8 @@ class OpenAIService ]; } - Logger::error('OpenAI generation failed after quality retries', 'openai'); + Logger::error('OpenAI generation failed after quality retries. Last feedback: ' . $qualityFeedback, 'openai'); + Logger::error('OpenAI generation failed after quality retries. Last feedback: ' . $qualityFeedback, 'publish'); return null; } @@ -153,12 +154,17 @@ class OpenAIService int $maxWords ): string { $prompt = "Napisz artykul na temat: {$topicName}\n"; - $prompt .= "Docelowa dlugosc: {$minWords}-{$maxWords} slow.\n"; + $prompt .= "KRYTYCZNE WYMAGANIE DLUGOSCI: Artykul MUSI miec minimum {$minWords} slow. Docelowo {$minWords}-{$maxWords} slow. Artykuly ponizej {$minWords} slow beda ODRZUCONE.\n"; + $prompt .= "Aby osiagnac wymagana dlugosc:\n"; + $prompt .= "- Kazda sekcja H2 musi miec minimum 150-200 slow z konkretnymi przykladami, danymi i scenariuszami.\n"; + $prompt .= "- Uzyj minimum 5 sekcji H2 (nie liczac FAQ i zakonczenia).\n"; + $prompt .= "- Rozwin kazdy punkt — nie pisz ogolnikow, podaj detale, porownania, liczby.\n\n"; $prompt .= "Tytul ma byc samodzielny i nie moze zaczynac sie od nazwy tematu ani kategorii.\n"; $prompt .= "Tresc ma byc konkretna, praktyczna i naturalna. Bez ogolnikow.\n"; $prompt .= "Wstep: 2-3 krotkie akapity i jasna obietnica, czego czytelnik sie dowie.\n"; - $prompt .= "Srodek: minimum 3 sekcje H2, w kazdej przynajmniej jeden konkret (przyklad, liczba, scenariusz, checklista).\n"; - $prompt .= "Wstaw jedna sekcje H2 o nazwie \"Najczestsze bledy\" i jedna H2 \"FAQ\" z 3 pytaniami i odpowiedziami.\n"; + $prompt .= "Srodek: minimum 5 sekcji H2, w kazdej przynajmniej jeden konkret (przyklad, liczba, scenariusz, checklista).\n"; + $prompt .= "Wstaw jedna sekcje H2 o nazwie \"Najczestsze bledy\".\n"; + $prompt .= "Opcjonalnie dodaj sekcje H2 \"FAQ\" z 3 pytaniami i odpowiedziami — tylko jesli pasuje do tematu.\n"; $prompt .= "Zakonczenie ma byc praktyczne: \"Co warto zapamietac\" jako lista punktowana.\n"; $prompt .= "Uzywaj tylko HTML:

,

,

,