This commit is contained in:
2026-04-24 18:53:11 +02:00
parent 01581a1dd8
commit b1ed6fe44a
10 changed files with 392 additions and 65 deletions

View File

@@ -13,7 +13,7 @@ Phases: 1 of 2 complete
| Phase | Name | Plans | Status | Completed | | Phase | Name | Plans | Status | Completed |
|-------|------|-------|--------|-----------| |-------|------|-------|--------|-----------|
| 1 | StatLink Autolinking | 1 | Complete ✓ | 2026-04-09 | | 1 | StatLink Autolinking | 1 | Complete ✓ | 2026-04-09 |
| 2 | Admin Panel Upgrade | 2 | Planning | - | | 2 | Admin Panel Upgrade | 3 | Planning | - |
## Phase Details ## Phase Details
@@ -38,10 +38,12 @@ Phases: 1 of 2 complete
- Widok /statlink z listą linkowanych artykułów - Widok /statlink z listą linkowanych artykułów
- Zdalne wlaczanie/wylaczanie komentowania dla pojedynczej strony WordPress - Zdalne wlaczanie/wylaczanie komentowania dla pojedynczej strony WordPress
- Lista komentarzy z danego serwisu z mozliwoscia usuwania - Lista komentarzy z danego serwisu z mozliwoscia usuwania
- Masowe zamykanie komentarzy i pingow w istniejacych wpisach
**Plans:** **Plans:**
- [ ] 02-01: Migrator + sidebar + widok StatLink - [ ] 02-01: Migrator + sidebar + widok StatLink
- [ ] 02-02: Zdalne zarzadzanie komentarzami WordPress - [ ] 02-02: Zdalne zarzadzanie komentarzami WordPress
- [ ] 02-03: Twarde zamykanie komentarzy w istniejacych wpisach
--- ---
*Roadmap created: 2026-04-09* *Roadmap created: 2026-04-09*

View File

@@ -1,9 +1,9 @@
## Current Position ## Current Position
Phase: 02-admin-panel-upgrade — In Progress Phase: 02-admin-panel-upgrade — In Progress
Plan: 02-02 complete Plan: 02-03 complete
Status: UNIFY complete. Loop complete — ready for next plan. Status: UNIFY complete. Loop complete — ready for next plan.
Last activity: 2026-04-24T07:11:11.932Z Last activity: 2026-04-24T09:15:37.793Z
## Loop Position ## Loop Position
@@ -16,6 +16,6 @@ PLAN ──▶ APPLY ──▶ UNIFY
## Session Continuity ## Session Continuity
Last session: 2026-04-24 Last session: 2026-04-24
Stopped at: Plan 02-02 complete Stopped at: Plan 02-03 complete
Next action: paul_workflow('plan') for next plan Next action: paul_workflow('plan') for next plan
Resume file: .paul/phases/02-admin-panel-upgrade/02-02-SUMMARY.md Resume file: .paul/phases/02-admin-panel-upgrade/02-03-SUMMARY.md

View File

@@ -1,66 +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 ## Current Position
Milestone: v0.1 Initial Release Phase: 02-admin-panel-upgrade — Planning
Phase: 2 of 2 (Admin Panel Upgrade) — Planning Plan: 02-03 applied, awaiting UNIFY
Plan: 02-02 applied, awaiting UNIFY (depends on 02-01)
Status: APPLY complete — 3/3 PASS, ready for UNIFY 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 Last activity: 2026-04-24 - APPLY complete for .paul/phases/02-admin-panel-upgrade/02-03-PLAN.md
Progress:
- Milestone: [████░░░░░░] 40%
- Phase 1: [██████████] 100% ✓
- Phase 2: [░░░░░░░░░░] 0%
## Loop Position ## Loop Position
**Phase 1 (StatLink Auto-Linking):** Current loop state:
``` ```
PLAN ──▶ APPLY ──▶ UNIFY PLAN ──▶ APPLY ──▶ UNIFY
✓ ✓ ○ [APPLY complete, awaiting 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 ## Session Continuity
Last session: 2026-04-09 Last session: 2026-04-24
Stopped at: Phase 2 Plan 02-02 APPLY complete Stopped at: Plan 02-03 APPLY complete
Next action: Run $paul-unify .paul/phases/02-admin-panel-upgrade/02-02-PLAN.md Next action: Run $paul-unify .paul/phases/02-admin-panel-upgrade/02-03-PLAN.md
Resume file: .paul/phases/02-admin-panel-upgrade/02-02-PLAN.md Resume file: .paul/phases/02-admin-panel-upgrade/02-03-PLAN.md
---
*STATE.md — Updated after every significant action*

View File

@@ -2,15 +2,14 @@
## Co zrobiono ## Co zrobiono
- [02-admin-panel-upgrade, Plan 02] - [02-admin-panel-upgrade, Plan 03]
- Task 1: Rozszerzyc WordPressService o operacje komentarzy - Task 1: Dodac operacje close_existing_comments do WordPressService i remote service
- Task 2: Dodac akcje kontrolera i trasy komentarzy - Task 2: Dodac akcje kontrolera i trase masowego zamkniecia
- Task 3: Zbudowac UI komentarzy dla pojedynczej strony - Task 3: Dodac UI hard-close w panelu komentarzy
## Zmienione pliki ## Zmienione pliki
- `src/Services/WordPressService.php` - `src/Services/WordPressService.php`
- `src/Controllers/SiteController.php` - `src/Controllers/SiteController.php`
- `config/routes.php` - `config/routes.php`
- `templates/sites/dashboard.php`
- `templates/sites/comments.php` - `templates/sites/comments.php`

View File

@@ -0,0 +1,188 @@
---
phase: 02-admin-panel-upgrade
plan: 03
type: execute
wave: 3
depends_on: ["02-02"]
files_modified:
- src/Services/WordPressService.php
- src/Controllers/SiteController.php
- templates/sites/comments.php
- config/routes.php
autonomous: true
delegation: off
---
<objective>
## Goal
Dodac "twarde" zamykanie komentarzy dla istniejacych tresci WordPress:
1. Zamkniecie komentarzy i pingow/trackbackow we wszystkich istniejacych wpisach strony.
2. Opcjonalne usuniecie oczekujacych komentarzy spamowych po zamknieciu komentarzy.
3. Pokazanie wyniku operacji w panelu komentarzy BackPRO.
## Purpose
Samo wylaczenie komentowania nowych wpisow nie chroni starych artykulow, ktore maja `comment_status = open`. Boty moga dalej dodawac komentarze pod istniejacymi wpisami. Operator BackPRO potrzebuje jednej akcji, ktora zdalnie domyka cala powierzchnie komentarzy dla wybranego serwisu.
## Output
- Nowa metoda `closeExistingComments()` w `WordPressService`.
- Nowe akcje remote service w `backpro-remote-service.php` generowanym przez `WordPressService`.
- Nowa akcja POST w `SiteController` i trasa w `config/routes.php`.
- Sekcja w `templates/sites/comments.php` z przyciskiem "Zamknij komentarze w istniejacych wpisach" i opcja usuniecia oczekujacych komentarzy.
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Prior Work
@.paul/phases/02-admin-panel-upgrade/02-02-SUMMARY.md
## Source Files
@src/Services/WordPressService.php
@src/Controllers/SiteController.php
@templates/sites/comments.php
@config/routes.php
</context>
<acceptance_criteria>
## AC-1: Masowe zamkniecie istniejacych wpisow
```gherkin
Given uzytkownik jest zalogowany w BackPRO i strona ma aktywny plik serwisowy BackPRO
When klika "Zamknij komentarze w istniejacych wpisach"
Then WordPress ustawia comment_status='closed' i ping_status='closed' dla wszystkich opublikowanych wpisow
And BackPRO pokazuje liczbe zaktualizowanych wpisow
```
## AC-2: Opcjonalne czyszczenie oczekujacych komentarzy
```gherkin
Given uzytkownik widzi panel komentarzy danej strony
When zaznacza opcje usuniecia oczekujacych komentarzy i uruchamia operacje
Then WordPress usuwa komentarze ze statusem hold
And BackPRO pokazuje liczbe usunietych komentarzy
```
## AC-3: Bezpieczna obsluga bledow i odswiezenie statusu
```gherkin
Given remote service jest nieaktualny albo niedostepny
When uzytkownik uruchamia masowe zamkniecie komentarzy
Then BackPRO probuje zaktualizowac plik serwisowy i ponawia akcje
And przy niepowodzeniu pokazuje czytelny komunikat bledu bez psucia listy komentarzy
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Dodac operacje close_existing_comments do WordPressService i remote service</name>
<files>src/Services/WordPressService.php</files>
<action>
Dodac publiczna metode:
- closeExistingComments(array $site, bool $deletePendingComments = false): array
- Wywoluje callRemoteService($site, 'close_existing_comments', ['delete_pending_comments' => '1'/'0']).
- Jezeli akcja nie dziala przez stary plik serwisowy, wywoluje ensureRemoteService(), odswieza site z bazy i ponawia akcje.
- Zwraca success, posts_updated, pending_comments_deleted, message.
- Przy bledzie zwraca success=false i czytelny message.
Zaktualizowac BACKPRO_REMOTE_SERVICE_VERSION do kolejnej wersji, np. 1.6.0.
W getBackproRemoteServiceContent() dodac action `close_existing_comments`:
- Ustawic default_comment_status='closed' i default_ping_status='closed', aby nowe tresci tez byly domyslnie zamkniete.
- Wykonac bezposredni update przez global `$wpdb`:
- UPDATE {$wpdb->posts} SET comment_status='closed', ping_status='closed' WHERE post_type IN ('post','page') AND post_status NOT IN ('trash','auto-draft')
- Zliczyc liczbe zaktualizowanych rekordow z wyniku query.
- Jesli `delete_pending_comments=1`, usunac komentarze ze statusem `hold` przez wp_delete_comment($commentId, true).
- Zwracac JSON z posts_updated, pending_comments_deleted i message.
Nie modyfikowac istniejacych metod listowania/usuwania pojedynczych komentarzy poza ewentualnym wspolnym helperem formatowania wyniku.
</action>
<verify>php -l src/Services/WordPressService.php</verify>
<done>AC-1, AC-2 i AC-3 maja warstwe zdalnej operacji WordPress.</done>
</task>
<task type="auto">
<name>Task 2: Dodac akcje kontrolera i trase masowego zamkniecia</name>
<files>src/Controllers/SiteController.php, config/routes.php</files>
<action>
Dodac do SiteController metode:
- closeExistingComments(string $id): void
- Auth::requireLogin().
- Pobrac Site::find(), obsluzyc brak strony.
- Odczytac checkbox/field `delete_pending_comments` jako bool.
- Wywolac WordPressService::closeExistingComments($site, $deletePendingComments).
- Flash success z liczba posts_updated i pending_comments_deleted.
- Flash danger z message przy bledzie.
- Redirect do `/sites/{id}/comments`.
Dodac trase:
- POST `/sites/{id}/comments/close-existing` -> SiteController@closeExistingComments
Zachowac cienki kontroler: nie wpisywac SQL ani logiki WordPress do kontrolera.
</action>
<verify>php -l src/Controllers/SiteController.php oraz php -l config/routes.php</verify>
<done>AC-1, AC-2 i AC-3 sa dostepne z panelu przez POST route.</done>
</task>
<task type="auto">
<name>Task 3: Dodac UI hard-close w panelu komentarzy</name>
<files>templates/sites/comments.php</files>
<action>
W `templates/sites/comments.php` dodac karte/sekcje pod ustawieniem "Komentowanie nowych wpisow":
- Tytul: "Istniejace wpisy".
- Krotkie wyjasnienie: operacja zamyka komentarze i pingi/trackbacki w juz opublikowanych tresciach.
- Formularz POST do `/sites/{id}/comments/close-existing`.
- Checkbox `delete_pending_comments` opisany jako "Usun oczekujace komentarze przy tej operacji".
- Przycisk w stylu ostrzegawczym, np. `btn-outline-danger`, z data-confirm zawierajacym ostrzezenie, ze operacja dotyczy wszystkich wpisow tej strony.
- Po flashu sukcesu/bledu istniejacy layout ma normalnie pokazac komunikat.
Nie ukrywac listy komentarzy ani istniejacych filtrow statusu.
</action>
<verify>php -l templates/sites/comments.php</verify>
<done>AC-1 i AC-2 maja widoczny, potwierdzany interfejs uzytkownika.</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- src/Services/PublisherService.php
- src/Models/Article.php
- src/Models/Topic.php
- migrations/* (brak zmian schematu bazy)
- templates/articles/*
- cron/*
## SCOPE LIMITS
- Nie dodawac globalnej operacji dla wszystkich stron naraz; zakres to pojedyncza strona.
- Nie dodawac moderacji approve/spam/unspam w tym planie.
- Nie usuwac zatwierdzonych komentarzy, chyba ze uzytkownik usunie je osobno istniejaca funkcja.
- Nie dodawac nowych zaleznosci Composer.
- Nie modyfikowac bazy lokalnej BackPRO.
</boundaries>
<verification>
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/comments.php
- [ ] /sites/{id}/comments pokazuje sekcje "Istniejace wpisy"
- [ ] POST /sites/{id}/comments/close-existing wywoluje remote service i pokazuje wynik
- [ ] Remote service version wzrosla, aby wymusic aktualizacje pliku na serwerze
</verification>
<success_criteria>
- Operator moze jednym kliknieciem zamknac komentarze i pingi w istniejacych wpisach wybranej strony.
- Operator moze przy tej operacji usunac komentarze oczekujace na moderacje.
- Wynik operacji pokazuje liczby zaktualizowanych wpisow i usunietych komentarzy.
- Brak regresji w istniejacym widoku listy komentarzy i usuwaniu pojedynczego komentarza.
</success_criteria>
<output>
After completion, create `.paul/phases/02-admin-panel-upgrade/02-03-SUMMARY.md`
</output>

View File

@@ -0,0 +1,38 @@
---
phase: 02-admin-panel-upgrade
plan: 03
completed: 2026-04-24T09:15:37.793Z
---
# Phase 02-03 Summary
****
## Acceptance Criteria Results
| Criterion | Status |
|-----------|--------|
| Task 1: Dodac operacje close_existing_comments do WordPressService i remote service | Pass — Dodano WordPressService::closeExistingComments(), akcje remote service close_existing_comments oraz podniesiono BACKPRO_REMOTE_SERVICE_VERSION do 1.6.0. Po live bledzie poprawiono generowany SQL string w remote service i zweryfikowano php -l dla wygenerowanego pliku. |
| Task 2: Dodac akcje kontrolera i trase masowego zamkniecia | Pass — Dodano SiteController::closeExistingComments() oraz POST /sites/{id}/comments/close-existing. Zweryfikowano php -l src/Controllers/SiteController.php i php -l config/routes.php. |
| Task 3: Dodac UI hard-close w panelu komentarzy | Pass — Dodano sekcje Istniejace wpisy w templates/sites/comments.php z checkboxem usuwania oczekujacych komentarzy i confirmem. Zweryfikowano php -l templates/sites/comments.php. |
## Accomplishments
- Task 1: Dodac operacje close_existing_comments do WordPressService i remote service: Dodano WordPressService::closeExistingComments(), akcje remote service close_existing_comments oraz podniesiono BACKPRO_REMOTE_SERVICE_VERSION do 1.6.0. Po live bledzie poprawiono generowany SQL string w remote service i zweryfikowano php -l dla wygenerowanego pliku.
- Task 2: Dodac akcje kontrolera i trase masowego zamkniecia: Dodano SiteController::closeExistingComments() oraz POST /sites/{id}/comments/close-existing. Zweryfikowano php -l src/Controllers/SiteController.php i php -l config/routes.php.
- Task 3: Dodac UI hard-close w panelu komentarzy: Dodano sekcje Istniejace wpisy w templates/sites/comments.php z checkboxem usuwania oczekujacych komentarzy i confirmem. Zweryfikowano php -l templates/sites/comments.php.
## Files Modified
- `src/Services/WordPressService.php`
- `src/Controllers/SiteController.php`
- `config/routes.php`
- `templates/sites/comments.php`
## Deviations
During live testing the remote service returned non-JSON because the generated PHP contained escaped SQL string quotes (\"UPDATE ...\"). Fixed the generator in WordPressService.php, generated a temporary backpro-remote-service.php, and verified it with php -l. User confirmed the feature works after the fix.
---
*Phase: 02-admin-panel-upgrade, Plan: 03*
*Completed: 2026-04-24*

View File

@@ -34,6 +34,7 @@ $router->post('/sites/{id}/test', 'SiteController', 'testConnection');
$router->get('/sites/{id}/dashboard', 'SiteController', 'dashboard'); $router->get('/sites/{id}/dashboard', 'SiteController', 'dashboard');
$router->get('/sites/{id}/comments', 'SiteController', 'comments'); $router->get('/sites/{id}/comments', 'SiteController', 'comments');
$router->post('/sites/{id}/comments/settings', 'SiteController', 'updateCommentsEnabled'); $router->post('/sites/{id}/comments/settings', 'SiteController', 'updateCommentsEnabled');
$router->post('/sites/{id}/comments/close-existing', 'SiteController', 'closeExistingComments');
$router->post('/sites/{id}/comments/{commentId}/delete', 'SiteController', 'deleteComment'); $router->post('/sites/{id}/comments/{commentId}/delete', 'SiteController', 'deleteComment');
$router->post('/sites/{id}/dashboard/permalinks/enable', 'SiteController', 'enablePrettyPermalinks'); $router->post('/sites/{id}/dashboard/permalinks/enable', 'SiteController', 'enablePrettyPermalinks');
$router->post('/sites/{id}/dashboard/remote-service/update', 'SiteController', 'updateRemoteService'); $router->post('/sites/{id}/dashboard/remote-service/update', 'SiteController', 'updateRemoteService');

View File

@@ -304,6 +304,35 @@ class SiteController extends Controller
$this->redirect("/sites/{$id}/comments{$statusQuery}"); $this->redirect("/sites/{$id}/comments{$statusQuery}");
} }
public function closeExistingComments(string $id): void
{
Auth::requireLogin();
$site = Site::find((int) $id);
if (!$site) {
$this->flash('danger', 'Strona nie znaleziona.');
$this->redirect('/sites');
return;
}
$deletePendingComments = (string) $this->input('delete_pending_comments', '0') === '1';
$wp = new WordPressService();
$result = $wp->closeExistingComments($site, $deletePendingComments);
if (!empty($result['success'])) {
$postsUpdated = (int) ($result['posts_updated'] ?? 0);
$deletedPending = (int) ($result['pending_comments_deleted'] ?? 0);
$this->flash(
'success',
"Zamknieto komentarze i pingi. Zaktualizowane wpisy: {$postsUpdated}, usuniete oczekujace komentarze: {$deletedPending}."
);
} else {
$this->flash('danger', (string) ($result['message'] ?? 'Nie udalo sie zamknac komentarzy w istniejacych wpisach.'));
}
$this->redirect("/sites/{$id}/comments");
}
public function seoPanel(string $id): void public function seoPanel(string $id): void
{ {
Auth::requireLogin(); Auth::requireLogin();

View File

@@ -12,7 +12,7 @@ class WordPressService
{ {
private const BACKPRO_MU_PLUGIN_FILENAME = 'backpro-remote-tools.php'; private const BACKPRO_MU_PLUGIN_FILENAME = 'backpro-remote-tools.php';
private const BACKPRO_REMOTE_SERVICE_FILENAME = 'backpro-remote-service.php'; private const BACKPRO_REMOTE_SERVICE_FILENAME = 'backpro-remote-service.php';
private const BACKPRO_REMOTE_SERVICE_VERSION = '1.5.0'; private const BACKPRO_REMOTE_SERVICE_VERSION = '1.6.0';
private const BACKPRO_NEWS_THEME_SLUG = 'backpro-news-mag'; private const BACKPRO_NEWS_THEME_SLUG = 'backpro-news-mag';
private const BACKPRO_NEWS_THEME_SOURCE_DIR = 'assets/wp-theme-backpro-news'; private const BACKPRO_NEWS_THEME_SOURCE_DIR = 'assets/wp-theme-backpro-news';
private Client $client; private Client $client;
@@ -638,6 +638,38 @@ class WordPressService
} }
} }
public function closeExistingComments(array $site, bool $deletePendingComments = false): array
{
$params = ['delete_pending_comments' => $deletePendingComments ? '1' : '0'];
$result = $this->callRemoteService($site, 'close_existing_comments', $params);
if (!empty($result['success'])) {
return $this->formatCloseExistingCommentsResult($result);
}
$ensure = $this->ensureRemoteService($site);
if (empty($ensure['success'])) {
return [
'success' => false,
'posts_updated' => 0,
'pending_comments_deleted' => 0,
'message' => (string) ($ensure['message'] ?? $result['message'] ?? 'Brak endpointu BackPRO na WordPress.'),
];
}
$refreshedSite = !empty($site['id']) ? (Site::find((int) $site['id']) ?: $site) : $site;
$retry = $this->callRemoteService($refreshedSite, 'close_existing_comments', $params);
if (!empty($retry['success'])) {
return $this->formatCloseExistingCommentsResult($retry);
}
return [
'success' => false,
'posts_updated' => 0,
'pending_comments_deleted' => 0,
'message' => (string) ($retry['message'] ?? 'Nie udalo sie zamknac komentarzy w istniejacych wpisach.'),
];
}
public function ensureRemoteService(array $site): array public function ensureRemoteService(array $site): array
{ {
$siteData = $this->prepareRemoteServiceMetadata($site); $siteData = $this->prepareRemoteServiceMetadata($site);
@@ -1180,6 +1212,16 @@ class WordPressService
]; ];
} }
private function formatCloseExistingCommentsResult(array $data): array
{
return [
'success' => true,
'posts_updated' => max(0, (int) ($data['posts_updated'] ?? 0)),
'pending_comments_deleted' => max(0, (int) ($data['pending_comments_deleted'] ?? 0)),
'message' => (string) ($data['message'] ?? 'Komentarze w istniejacych wpisach zostaly zamkniete.'),
];
}
private function formatWpRequestError(RequestException $e, string $fallback): string private function formatWpRequestError(RequestException $e, string $fallback): string
{ {
$statusCode = $e->hasResponse() ? (int) $e->getResponse()->getStatusCode() : 0; $statusCode = $e->hasResponse() ? (int) $e->getResponse()->getStatusCode() : 0;
@@ -1342,7 +1384,7 @@ if (!defined('ABSPATH')) {
\$action = (string) (\$_POST['action'] ?? ''); \$action = (string) (\$_POST['action'] ?? '');
if (\$action === 'ping') { if (\$action === 'ping') {
echo json_encode(['success' => true, 'message' => 'pong', 'version' => '1.5.0']); echo json_encode(['success' => true, 'message' => 'pong', 'version' => '1.6.0']);
exit; exit;
} }
@@ -1463,6 +1505,48 @@ if (\$action === 'set_comment_settings') {
exit; exit;
} }
if (\$action === 'close_existing_comments') {
global \$wpdb;
update_option('default_comment_status', 'closed');
update_option('default_ping_status', 'closed');
\$postsUpdated = \$wpdb->query(
"UPDATE {\$wpdb->posts}
SET comment_status = 'closed', ping_status = 'closed'
WHERE post_type IN ('post', 'page')
AND post_status NOT IN ('trash', 'auto-draft')"
);
if (\$postsUpdated === false) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'posts_update_failed']);
exit;
}
\$deletePending = (string) (\$_POST['delete_pending_comments'] ?? '0') === '1';
\$deletedPending = 0;
if (\$deletePending) {
\$pendingIds = \$wpdb->get_col(
"SELECT comment_ID FROM {\$wpdb->comments} WHERE comment_approved = '0'"
);
foreach ((array) \$pendingIds as \$commentId) {
if (wp_delete_comment((int) \$commentId, true)) {
\$deletedPending++;
}
}
}
echo json_encode([
'success' => true,
'posts_updated' => (int) \$postsUpdated,
'pending_comments_deleted' => \$deletedPending,
'message' => 'Zamknieto komentarze i pingi w istniejacych wpisach.',
]);
exit;
}
if (\$action === 'cleanup') { if (\$action === 'cleanup') {
@unlink(__FILE__); @unlink(__FILE__);
echo json_encode(['success' => true, 'message' => 'service_deleted']); echo json_encode(['success' => true, 'message' => 'service_deleted']);

View File

@@ -77,6 +77,37 @@ $badgeClasses = [
</div> </div>
</div> </div>
<div class="card border-danger mb-4">
<div class="card-header bg-danger-subtle d-flex justify-content-between align-items-center">
<h5 class="mb-0 text-danger">Istniejace wpisy</h5>
<span class="badge bg-danger">Hard close</span>
</div>
<div class="card-body">
<p class="mb-2">
Ta operacja zamyka komentarze oraz pingi/trackbacki w juz opublikowanych tresciach tej strony.
</p>
<p class="small text-muted mb-3">
Uzyj jej po wylaczeniu komentowania nowych wpisow, gdy boty nadal dodaja komentarze pod starszymi artykulami.
</p>
<form
method="post"
action="/sites/<?= (int) $site['id'] ?>/comments/close-existing"
data-confirm="Zamknac komentarze i pingi we wszystkich istniejacych wpisach tej strony WordPress?"
>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="delete_pending_comments" value="1" id="deletePendingComments">
<label class="form-check-label" for="deletePendingComments">
Usun oczekujace komentarze przy tej operacji
</label>
</div>
<button type="submit" class="btn btn-outline-danger">
<i class="bi bi-shield-slash me-1"></i>Zamknij komentarze w istniejacych wpisach
</button>
</form>
</div>
</div>
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Lista komentarzy</h5> <h5 class="mb-0">Lista komentarzy</h5>