Programujeme letenky

Vývojáři GOL IBE v C.E.E. Group Travelport
Programátorský web
Background image - Futurama office park

WordPress plugin pro GOL-IBE

Tomáš Srb, 12. 11. 2019

Co jsme už měli

Provozujeme systém nabízející letenky a umožňující jejich nákup… zjednodušeně řečeno eshop. Jenže ho neprovozujeme pro sebe, ale pro řádově stovky jiných společností a každá z nich by se pochopitelně ráda odlišila od konkurence. Naštěstí většinou stačí nastavit pár barev, nahradit pár obrázků a voilá - nový osobitý design je na světě. Někteří zákazníci (rozuměj společnosti prodávající letenky) by rádi začlenili „eshop“ přímo do svého firemního webu. Naštěstí ne celý, stačí tzv. první krok - vstupní formulář, kde lze zadat odkud a kam kolik osob kdy chce letět - to je taková fasáda naší aplikace (nabízí se srovnání s pověstnou špičkou ledovce, ale nebuďme malicherní). Protože si přání našich zákazníků vážíme nade vše, máme hned dva způsoby jak jim vyhovět:

Co bychom chtěli

Zjistili jsme, že nemalé procento zákazníků provozuje weby na redakčním systému WordPress a tak jsme se jim rozhodli vyjít vstříc - vytvoříme plugin do WordPressu, který si prodeje chtivý obchodník stáhne z pluginstóru, nastaví pár triviálních parametrů a bude hotovo. Pochopitelně jsme se do takového dobrodružství nepustili sami - obrátili jsme se na společnost, která se implementací WordPressu zabývá na plný úvazek (nebo aspoň víc než my). Zodpovědným pracovníkům z vedení i programátorům jsme předvedli stávající formulář (ten co nabízíme ke stažení co zip) a zahrnuli je záplavou požadavků nový plugin:

Na oplátku nám sdělili, že:

Co jsme dostali

Odborníci se bez meškání pustili do práce a brzy nám zaslali plod svého snažení - zip, který po načtení do instalace WordPressu přidal do hlavního menu stránku s konfigurací (nastavení barev některých prvků, průhlednost celého formuláře, kódem jazyka a url webu, na který se odešlou získaná data) a stručnou poznámkou, že pokud do publikované stránky vložíme magický řetězec tzv. shortcode, tak se na jeho místě objeví kýžený formulář. A opravdu, když jsme do vzorové stránky uvedený shortcode vložili, formulář se zjevil a fungoval! Drobný potíž se projevil, když jsme formulář vložili do prvku pro zobrazování obrázků na pozadí - využíval absolutního pozicování, s kterým se špatně vyrovnával použitý modul jQuery-ui, ale to se nám povedlo opravit úpravou daného modulu (kontrolní otázka: všiml si laskavý čtenář, že takové řešení je ideově pochybené a nemůže dlouho vydržet?).

Co nás překvapilo

Aktualizaci pluginu v pluginstóru jsme chtěli provádět automaticky, aby zapadala do našeho vývojového procesu a tak jsme registraci pluginu nesvěřili odborníkům, ale hrdě oznámili, že to zařídíme sami. První krok byl jasný - přečetli jsme si, jak se to dělá. WordPress spravuje úložiště pluginů v systému správy verzí Subversion (známém též pod zkratkou „svn“)... to pro nás není žádná výzva - svn jsme již na jednom projektu používali (takové mezipřistání na cestě od cvs do gitu). Je to verzovací systém, bylo by tedy pěkné mít celou historii - požádali jsme tedy odborníky o dump projektu pluginu z jejich verzovacího systému. Přiznávám, těšil jsem se s dychtivostí voyera, že uvidím genezi díla krok po kroku… obdrželi jsme tentýž zip jako při předchozím předávání. Nevadí, stejně jsme se dočetli, že svn repozitář nemá sloužit k vývoji, ale jen k distribuci pluginu k uživatelům, neboť se při každém commitu přegenerovávají instalační balíčky a aktivnější vývojářský tým by brzy přetížil infrastrukturu úložiště.

Jenže abychom získali přístup k úložišti, musíme náš balíček nahrát přes jednoduché webové rozhraní k manuální kontrole… zřejmě ověření zda neobsahuje nějaký backdoor, těžbu kryptoměn či co já vím - v našem případě jistě pouhá formalita… omyl - sotva jsem klikl na tlačítko „Odeslat“, vyskočila na mne hláška „každý plugin musí obsahovat readme.txt“ (pochopitelně v angličtině, ale screenshot jsem si neudělal a má anglická paměť je ještě děravější než česká). Zrovna jsem řešil několik jiných úkolů, tak jsem o tvorbu readme.txt požádal kolegu, který akci pokrýval z organizačního hlediska - ostatně, co tam tak může napsat… k čemu plugin slouží, co je nutné nastavit a něco o naší firmě. Po pár dnech kolega dodal sofistikovaný dokument čítající několik kapitol a odkazy na screenshoty. Doprovodný dopis obsahoval i odkazy na pokyny ze strany WordPressu, jak má readme.txt vypadat, a na online nástroj k jeho validaci! Z uvedeného vyplynulo, že screenshoty mají být součástí balíčku (v adresáři assets), stejně jako všechny ostatní obrázky… hleďme, součástí pluginu mohou být obrázky… no, teď už to předělávat nebudeme, času je málo a už se chceme věnovat něčemu jinému. Znovu nahrávám plugin. Úspěch! Dostává se mi informace, že plugin byl přijat do fronty čekajících k manuální kontrole a že je druhý v pořadí.

Co nás vyděsilo

Uběhly asi tři dny napjatého očekávání, když dorazil obsáhlý email vypočítávající nedostatky našeho drobečka:

To vše doplněné zdrcujícími důkazy ve formě ukázek z našeho kódu - za všechny příklad snitizace a validace vstupních dat:

Some examples from your plugin:

GOL-IBE-Search-Form/GOL-IBE-Search-Form-settings-handler.php - the way you're sanitizing there isn't correct

if (isset($_POST['button_color'])) {

$regex = "/^#(?:[0-9a-fA-F]{3}){1,2}$/m";

if (preg_match($regex, $_POST['button_color'])) {

$settings['button_color'] = $_POST['button_color'];

}

}

That should be using sanitize_hex_color()

Your validations are great, but you also need to remember to sanitize properly just in case.

Co jsme s tím udělali

Zvážili jsme eventualitu předání připomínek původním tvůrcům našeho miláčka (glum, glum), ale z různých důvodů jsme se rozhodli popsané excesy napravit vlastními silami.

Vzali jsme to pěkně z gruntu:


Výtky týkající se přímého přístupu k souborům pluginu, nepoužívání Nonces a editace pluginu uživatelem se vztahovaly k práci s konfigurací. Původní řešení vypadalo zhruba takto:


Problém tkvěl jednak v tom, že plugin zapisoval sám do sebe (config.json), druhak v tom, že soubory save.php a css.php stály zcela mimo WordPress - žádná kontrola práv, žádná možnost přístupu k validačním a escapovacím funkcím WordPressu.

Řešení spočívalo v převodu nastavení na api nabízená WordPressem k tomuto účelu:

Tím odpadla nutnost kontrolovat oprávnění a používat nonces ve formuláři konfigurace, protože to vše api vyřešilo za nás - soubor save.php pochopitelně zmizel. Následně i soubor css.php, protože z něj nebylo dostupné options api potřebné k načtení nastavení - zkusili jsme jej ještě zachránit předáváním parametrů přes GET, ale neprošlo to a konfigurovatelné styly nakonec vkládáme v tagu <style> přímo v html (mimochodem, věděli jste, že takto vložené selektory mají menší prioritu než stejné vložené v externím stylesheetu?).

Options api se nakonec hodilo i na ukládání nastavení načítané z naší aplikace.


Po uvedených úpravách jsme šťastně prošli kontrolou ze strany WordPressu, žel s poněkud nefunkčním produktem - nefungovaly kalendáře pro zadávání data odletů jednotlivých fází cesty a nastavování barev také nešlo jak bychom si přáli. Stáli jsme tak před dilematem, zda použít získaný přístup k repozitáři a publikovat plugin tak jak je, nebo dále ladit a vychytávat mouchy. Demokraticky jsme si jednohlasně odhlasovali vydání v aktuálním stavu. Plugin se v nabídce dostupných objevil prakticky ihned, instalace a aktivace byly dílem okamžiku, velmi příjemný pocit. Žel, práce zdaleka není hotova a je třeba neprodleně zahájit práce na nové verzi… prostě opravit ty kalendáře.


Výchozí teorie byla, že dialogy pro výběr data jsou kvůli chybějící úpravě jQuery-ui špatně napozicované a mizí někde mimo obraz. Prověřili jsme i možnost opatchovat příslušnou metodu za běhu… ale nevypadalo to schůdně - měla asi 100 řádek značně nesrozumitelného kódu. Pak jsme si všimli, že primární problém je možná jinde - stránka při načítání vyhazovala do konzole poněkud záhadnou hlášku že „itemList is undefined“. Nezachycená výjimka padala odkudsi z hlubin minifikovaného jQuery-ui s několika vrstvami anonymních funkcí na zásobníku volání. Naštěstí nás intuice a mravenčí práce s debuggerem brzy přivedla k zakopanému psu: pro rozšíření možností stylování tagu <select>, nahrazuje jej jQuery-ui konstrukcí z divů a spanů, která emuluje funkci původního prvku. Některé selecty našeho formuláře mají v době inicializace prázdný seznam položek (tzv. <option>) a s tím se verze jQuery-ui distribuovaná s WordPressem nedokázala vyrovnat - námi používaná neměla námitek. Stačilo do každého prázdného selectu v html vložit jednu prázdnou option a hleďme, kalendáře jsou na světě. Tedy… pokud je formulář umístěn na „frai plac“ v jednoduché stránce. Při vložení do prvku s obrázkem na pozadí se formulář zdeformuje, z kalendářů jsou vidět jen části a modální dialog pro zadávání počtů cestujících vypadá jak po křivici. Náš krásný formulář je zřejmě utiskován a dokonce i ořezáván!

Padl návrh umístit kalendáře doprostřed formuláře, aby nebyly ořezány, ale neprošel.

Místo toho jsme zkusili celý formulář před inicializací vyjmout z omezujícího elementu a znovu vložit do stránky za něj. Vyžádalo si to další refaktoring javascriptových modulů, protože jejich inicializaci bylo třeba odložit až za přesun, ale výsledek stál za to. Přestože to je strašná řezničina… přeháním, jeden $.detach() a jeden $.after() není zas tak hrozný. Navíc se provádí jen když je detekována přítomnost omezujícího prvku a původnímu obalujícímu elementu je vnucen rozměr, jako by v něm formulář stále byl.


Zbývalo už jen vyřešit potíže s barvami - původní návrh umožňoval nastavit barvu:

Z toho plynuly hned dvě úzce provázané potíže:

Naštěstí jsme se už dozvěděli, že plugin vlastní obrázky obsahovat smí, odstranili jsme ze stylesheetu dataurl ikonek, vytvořili jsme černou a bílou verzi svg obrázků a na ty jsme odkázali konfiguračním stylesheetem vkládaným do html. To umožnilo nastavovat barvu popisku tlačítek v mezích černá/bílá, což odpovídá možnostem nastavování barvy textu formuláře. Někdo by navrhl vkládat ikonky jako img do html a tím umožnit jejich stylování… jenže tou dobou už jsme měli pluginu plné zuby a celí šťastní jsme vydali verzi 1.1.0, která už je snad použitelná.

Udělat to lépe… to jde vždy - důležité je odhadnout tu hranici, kdy je třeba práci na projektu uzavřít a posunout se dál, k dalším výzvám :-).


Co jsem tím chtěl říct

tl;dr: Provozujeme weby na prodej letenek, někteří zákazníci mají web na wordpressu, publikovali jsme vstupní formulář eshopu jako plugin do wordpressu.