feat: batches with dry run, undo, and CSV import and export

- POST /_seo/api/v1/batch runs changes in one transaction: dry_run
  answers the diff and the conflicts (loops, chains, noindex on a
  published record, canonicals to redirected or noindex addresses) and
  rolls back; a refused change refuses the batch (D-12)
- undo of a change or a whole batch from History and through MCP, with
  the undo_stale, undo_loop and undo_gone refusals (D-13)
- CSV export and import of redirects, rules and records by URL: BOM,
  comma or semicolon, formula cells escaped, a dry-run preview, all or
  nothing with via import; the screen's apply token is HMAC-SHA256 over
  the file and a fingerprint of the data (transfer.stale)
- Import and export singleton with the seo_transfer widget action,
  History record actions undo and undo_batch, navigation entry
- MCP tools undo_change, undo_batch, apply_batch, export_csv, import_csv
- TestBatch, TestUndo, TestCSVTransfer, TestLoopRefused mcp and import
This commit is contained in:
2026-10-27 12:35:26 +01:00
parent aa0c3bcacf
commit ab3b219fec
27 changed files with 3319 additions and 96 deletions

View File

@@ -8,6 +8,7 @@ nav:
seo: SEO
history: Historia zmian
rules: Reguły adresów
transfer: Import i eksport
permissions:
tab: SEO
manage: Zarządzanie SEO
@@ -190,6 +191,23 @@ history:
diff_before: Przed
diff_after: Po
list_empty: Nie zarejestrowano jeszcze zmian SEO. Każda zmiana z panelu, MCP lub importu pojawi się tutaj i będzie można ją cofnąć.
undo: Cofnij tę zmianę
undo_confirm: Cofnąć tę zmianę? Przywrócone zostaną wcześniejsze wartości, a cofnięcie zostanie zapisane jako nowa zmiana.
undone: Cofnięto zmianę.
undo_batch: Cofnij całą partię
undo_batch_confirm: Cofnąć wszystkie zmiany z tej partii? Cofa się całość albo nic.
batch_undone: Cofnięto partię.
undo_stale: "„:field” zmieniło się od tego czasu: :author zmienił je :time. Cofnięcie nadpisałoby tę zmianę. Nic nie cofnięto."
undo_loop: "Cofnięcie utworzyłoby pętlę przekierowań: :path. Nic nie cofnięto."
undo_gone: ":type, którego dotyczy ta zmiana, już nie istnieje. Nic nie cofnięto."
undo_done: Ta zmiana jest już cofnięta.
undo_unsupported: Tej zmiany nie można tu cofnąć. Nic nie cofnięto.
record: Rekord
kind_rule: Reguła adresu
kind_redirect: Przekierowanie
kind_robots: robots.txt
kind_settings: Ustawienia SEO
kind_notfound: Wpis dziennika 404
jobs:
generate_entries: 'SEO: generowanie wpisów'
effective:
@@ -308,3 +326,34 @@ api:
bot_unknown: To nie jest bot AI z ekranu robots.txt.
value_invalid: Ta wartość jest tu nieprawidłowa.
type_unknown: Nie zarejestrowano typu treści o tym kluczu.
batch_too_large: "Partia może mieć najwyżej :max zmian."
change_not_found: Rekord wskazany w tej zmianie nie istnieje.
op_unknown: "Nieznana operacja „:op”."
conflict_noindex_published: "„:url” to opublikowana strona z map witryny; noindex usunie ją z wyników wyszukiwania."
transfer:
title: Import i eksport
delimiter: Separator CSV
delimiter_comma: Przecinek (,)
delimiter_semicolon: Średnik (;), jak w polskim Excelu
delimiter_comment: Pliki są w UTF-8 ze znacznikiem BOM, więc Excel poprawnie otwiera polskie znaki.
widget: Pliki
preview: Podgląd importu
applied:
one: "Zastosowano :count zmianę jako jedną partię. Możesz ją cofnąć w Historii zmian."
few: "Zastosowano :count zmiany jako jedną partię. Możesz je cofnąć w Historii zmian."
many: "Zastosowano :count zmian jako jedną partię. Możesz je cofnąć w Historii zmian."
other: "Zastosowano :count zmiany jako jedną partię. Możesz je cofnąć w Historii zmian."
stale: Dane zmieniły się po podglądzie. Wyświetl podgląd pliku ponownie przed zastosowaniem.
unreadable: Nie udało się odczytać pliku jako CSV. Sprawdź separator i czy pierwszy wiersz zawiera nazwy kolumn.
missing_column: "W pliku brakuje kolumny „:column”. Pobierz eksport, aby zobaczyć oczekiwane kolumny."
dataset_unknown: Wybierz przekierowania, reguły adresów lub SEO rekordów.
too_large: Ten plik jest większy niż 60 KB. Podziel go na mniejsze pliki lub zaimportuj przez MCP.
too_large_mcp: Ten plik jest większy niż 900 KB. Zaimportuj go w mniejszych częściach.
export_too_large: Ten eksport jest za duży, by pobrać go tutaj. Wyeksportuj go przez MCP, strona po stronie.
refused_blocks: Popraw odrzucone wiersze w pliku i ponownie wyświetl podgląd. Nic nie zostanie zastosowane, dopóki jakiś wiersz jest odrzucony.
token_invalid: Ten podgląd jest już nieważny. Wyświetl podgląd pliku ponownie.
row_loop: "Tworzy pętlę przekierowań: :path"
row_chain: "Tworzy łańcuch: :path"
row_canonical: "Adres kanoniczny przekierowuje na „:target”"
row_rule: "Pod tym adresem nie ma rekordu: zostanie utworzona reguła adresu"
row_invalid: ":field: :message"