Blog · strony www, usługi programistyczne

Import treści HTML do Gutenberga – 3 pułapki, które zjadły mi wpis

2026-08-07

Skąd ten wpis

Napisałam sobie własny importer treści do WordPressa – wrzucam plik z gotowym HTML-em, a skrypt tworzy z niego wpis: po polsku i po angielsku naraz, z ustawionym SEO i kategoriami. Wygodne, bo treść piszę gdzie indziej, a import to jedno kliknięcie.

Brzmi prosto. I działało – do momentu, aż zaczęło robić dziwne rzeczy. Wpis wyglądał dobrze w edytorze, ale na stronie był pusty. Polskie znaki zamieniły się w krzaki. A raz angielska wersja nadpisała polską i został jeden wpis zamiast dwóch.

Każda z tych trzech rzeczy to osobna pułapka – i każda ma konkretną przyczynę. Poniżej opisuję je po kolei, bo jeśli kiedykolwiek wrzucasz surowy HTML do WordPressa (importerem, skryptem, przez API), prędzej czy później trafisz na którąś.

Pułapka 1: treść widać w edytorze, ale na froncie jest pusto

Najbardziej mylący objaw. Otwierasz wpis w kokpicie – treść jest, cała. Wchodzisz na stronę – pod tytułem biała pustka. Zero tekstu.

Przyczyna: edytor bloków (Gutenberg) oczekuje, że treść jest opakowana w komentarze bloków, na przykład <!-- wp:paragraph -->. Kiedy wrzucasz surowy HTML bez tych komentarzy, edytor traktuje go jako „nieoczekiwaną treść” – pokazuje w bloku odzyskiwania, więc w kokpicie widać. Ale na froncie funkcja the_content() przepuszcza treść przez parser bloków, a ten przy HTML-u bez markerów potrafi zwrócić pustkę.

Rozwiązanie: owinąć treść w jeden blok wp:html. To oficjalny blok Gutenberga na dowolny HTML – renderuje się jeden do jednego, nic nie gubi.

// Przed zapisem: jesli tresc nie ma zadnych markerow blokow,
// owin ja w blok wp:html - inaczej front pokaze pustke.
if ( strpos( $html, '<!-- wp:' ) === false ) {
    $html = "<!-- wp:html -->\n" . $html . "\n<!-- /wp:html -->";
}

Warunek strpos jest ważny: sprawdza, czy treść już nie ma bloków. Dzięki temu nie owijam podwójnie czegoś, co bloki już ma – a przy okazji nie psuję innych typów wpisów, które renderują się inaczej.

Pułapka 2: krzaki zamiast polskich znaków

Treść się pokazała, ale zamiast „treści” widać „treÅci”, zamiast „Cześć” jest „CzeÅÄ”. Klasyka.

To nie jest problem WordPressa – to problem pliku, z którego importujesz. Plik jest zapisany w jednym kodowaniu, a odczytywany jako inne. Najczęściej: treść jest w UTF-8, a coś po drodze czyta ją jako Windows-1250 (albo odwrotnie). Bajty się nie zgadzają, więc polskie znaki rozjeżdżają się w charakterystyczne dwuznaki.

Jak rozpoznać: jeśli widzisz „Å”, „Ä”, „Ô w miejscu ogonków – to prawie na pewno UTF-8 potraktowany jako Windows-1250. To sygnatura tej konkretnej pomyłki.

Jak naprawić: upewnij się, że plik źródłowy jest zapisany w UTF-8 (bez BOM). W VS Code widać kodowanie w prawym dolnym rogu – klik, „Zapisz z kodowaniem”, UTF-8. Na Windowsie łatwo o pomyłkę, bo część edytorów domyślnie zapisuje w Windows-1250.

Szybki test, czym naprawdę jest plik, bez otwierania go w edytorze:

file nazwa-pliku.md
# wynik powinien zawierac "UTF-8 text"
# jesli pokazuje "ISO-8859" albo "Non-ISO extended-ASCII" - masz zle kodowanie

Nie próbuj naprawiać krzaków ręcznie w treści, znak po znaku. To syzyfowa praca i zawsze coś zostanie. Napraw kodowanie u źródła i zaimportuj plik jeszcze raz.

Pułapka 3: jedna wersja językowa nadpisuje drugą

Ta była najbardziej podstępna. Mój importer robi dwie wersje wpisu naraz – polską i angielską – i spina je przez Polylang. Import wygląda, jakby się udał. A na stronie jest jeden wpis, nie dwa. Polska treść zniknęła, została angielska.

Przyczyna leżała w tym, jak importer szukał, czy wpis już istnieje. Sprawdzał po adresie (slug), ale bez rozróżnienia języka. Kolejność zdarzeń:

  • Tworzy wpis polski – OK, powstaje nowy.
  • Bierze się za angielski. Szuka, czy taki adres już jest. I znajduje – wpis polski, bo adresy były zbyt podobne.
  • Uznaje „już istnieje, zaktualizuję” – i nadpisuje polski wpis angielską treścią.

Efekt: jeden wpis pod jednym numerem, z angielską treścią, a polska przepadła.

Rozwiązanie: przy szukaniu istniejącego wpisu odrzucać trafienie, które należy do innego języka. Jak znaleziony wpis jest polski, a ja właśnie importuję angielski – to nie jest ten wpis, traktuj jak „nie istnieje” i twórz nowy.

// Po znalezieniu wpisu po slugu: odrzuc, jesli jest w innym jezyku.
// Bez tego import EN nadpisalby wpis PL (ten sam, zbyt podobny slug).
if ( $id && $lang && function_exists( 'pll_get_post_language' ) ) {
    $found_lang = pll_get_post_language( $id );
    if ( $found_lang && $found_lang !== $lang ) {
        return 0; // inny jezyk = to nie ten wpis, niech powstanie nowy
    }
}

Do tego druga rzecz, tym razem nie w kodzie, tylko w danych: adresy obu wersji muszą się od siebie różnić. „cattle-farm” po angielsku i „hodowla-bydla” po polsku – różne, jest dobrze. Ten sam adres dla obu języków to proszenie się o kłopoty, niezależnie od tego, jak sprytny jest importer.

Czego się nauczyłam

Wszystkie trzy pułapki mają wspólny mianownik: WordPress i Gutenberg mają swoje założenia co do tego, jak treść wygląda i skąd przychodzi. Kiedy wpychasz dane bokiem – surowym HTML-em, z zewnętrznego pliku, w dwóch językach naraz – te założenia potrafią się zemścić w miejscu, którego się nie spodziewasz.

Dlatego przy imporcie treści zawsze sprawdzam teraz trzy rzeczy, zanim uznam, że działa: czy front pokazuje to samo co edytor, czy polskie znaki są całe, i czy każda wersja językowa to osobny wpis. Trzy spojrzenia, minuta roboty – a oszczędzają godzinę zastanawiania się, gdzie się podziała treść.

Jeszcze słowo o bezpieczeństwie

Taki importer działa w panelu administracyjnym, więc jego wywołanie warto ograniczyć do zalogowanych osób z odpowiednimi uprawnieniami (current_user_can) i zabezpieczyć formularz tokenem nonce – u mnie robi to check_admin_referer. Jeśli treść piszę sama, z własnych plików, to tyle wystarczy. Gdyby importer miał kiedyś przyjmować HTML z zewnątrz (od kogoś innego), doszłaby jeszcze jedna rzecz: przepuszczenie treści przez wp_kses_post, żeby odsiać niebezpieczne skrypty. Świadomie tego nie robię, bo wp_kses wycina też rzeczy, których w swoich wpisach potrzebuję (obrazki w base64, ukośniki w ścieżkach) – ale to decyzja na własne, zaufane źródło, nie na cudze.

Wróć do wszystkich wpisów

Masz projekt do omówienia?

Napisz, o co chodzi - nie musi być technicznie, od tego jestem ja. Odezwę się i powiem wprost, czy i jak mogę pomóc.