WP Manifestindependent plugin directory
manifest / ecommerce / px-shop-core

PX Shop Core self-updates

by Pixelers · github.com/pixeler-sk/px-shop-core

★ 0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/pixeler-sk/px-shop-core/archive/refs/heads/main.zip

Ships its own WordPress updater (built-in updater), so new versions show up under Dashboard → Updates.

Zdieľané WooCommerce funkcie pre Pixeler eshopy. Prezentácia žije v téme — tento plugin dodáva len funkcionalitu (neutrálny px-* markup, dáta, REST). Každá funkcia je samostatný modul, dá sa vypnúť a téma sa jej pýta cez class_exists().

  • Vyžaduje WooCommerce (okrem modulov content a consent_mode); WordPress ≥ 6.0, PHP ≥ 7.4.
  • Inštalácia a aktualizácie: RELEASING.md — releases cez tagy, weby sa aktualizujú cez Plugin Update Checker.
  • Changelog po verziách: readme.txt.
  • Licencia: GPL v2 or later.

Obsah

Moduly a nastavenia

Každý modul sa dá vypnúť vo WooCommerce → Nastavenia → PX Shop. Vypnutý modul sa vôbec nenačíta — neregistruje hooky, REST routy, admin obrazovky ani WP-CLI príkazy, takže class_exists( 'PX_Wishlist' ) ostáva pre tému spoľahlivým testom dostupnosti. Uložené dáta zostávajú aj po vypnutí (obľúbené, prihlásenia na naskladnenie, GPSR polia) a po zapnutí sa vrátia.

Stav modulov je jedna option px_shop_core_modules; kým ju nikto neuloží, platia predvolené hodnoty z registra. Register je v includes/modules.php:

Filter Na čo
px_shop_core_modules pridá vlastný modul site pluginu — dostane prepínač, sekciu nastavení a načítanie zadarmo; bez názvu a popisu, tie idú do filtra nižšie
px_shop_core_module_labels názov a popis modulu pre obrazovku nastavení (__() až tu — register sa číta na plugins_loaded, kde je preklad predčasný)
px_shop_core_module_off_reason vlastné vysvetlenie pri module, ktorého prepínač nemá účinok (namiesto „nastavené v kóde")
px_shop_core_module_on pripne stav modulu v kóde (weby vedené cez git); nastavenia to napíšu ako „nastavené v kóde"

Moduly s vlastnou sekciou nastavení majú v PX Shop vlastnú záložku.

Prehľad modulov

Modul Kľúč Predvolene Čo robí
Omnibus omnibus zap. história cien, najnižšia cena za 30 dní pri zľave
Platnosť akcie sale_dates zap. dokedy platí zľavnená cena — dátum alebo odpočet
Jednotková cena unit_price zap. cena za 1 kg / 1 l / 1 m / 1 ks vedľa predajnej ceny (zákon 108/2024 § 6)
GPSR gpsr zap. bezpečnosť výrobku: výrobca, zodpovedná osoba v EÚ, pôvod, upozornenia, dokumentácia
Firemné údaje company_fields vyp. IČO/DIČ/IČ DPH v pokladni, RPO/ARES, VIES, reverse charge, vývoz mimo EÚ
Live search search zap. REST pixeler/v1/search pre šepkávač v hlavičke
Brand tab brand_tab zap. tab „O značke" z popisu termu product_brand
Wishlist wishlist zap. obľúbené (user meta / cookie), REST, shortcode
Compare compare zap. porovnanie až 4 produktov (cookie), REST, shortcode
Waitlist waitlist zap. stráženie dostupnosti s double opt-in a 4 e-mailmi
Veľkostné tabuľky size_guide zap. tabuľky veľkostí na produkte alebo zdedené z kategórie
Súvisiace kategórie related_cats zap. doplnky nastavené raz na kategórii, dedia sa po strome
Shipping bar shipping_bar zap. dáta pre lištu „do dopravy zadarmo chýba…"
Katalógový režim catalog zap. (režim sám vyp.) výklad bez cien a košíka
Obrázky atribútov attribute_image zap. obrázok na terme atribútu pa_* pre swatche a filtre
Content bloky content zap. skrytý CPT pre bannery, USP pásy a iné bloky kreslené témou
Súhlas s cookies consent vyp. vlastná CMP: lišta, blokovanie, stránka zásad, Consent Mode v2
Google Consent Mode v2 consent_mode vyp. signály pre Google popri free Complianze
Staré slugy termov old_term_slugs zap. 301 zo starej URL kategórie/značky/štítku po zmene slugu (aj slugu rodiča)
Médiá produktov pre MCP media_abilities vyp. Abilities API pre MCP konektor: obrázky a galéria produktu, alt text

Omnibus (omnibus)

Smernica Omnibus: pri zľavnenom produkte sa ukazuje najnižšia cena za posledných 30 dní. Plugin si históriu cien zapisuje sám pri uložení produktu, takže web, ktorý zapne modul dnes, má o mesiac úplné dáta.

Zápis „pri zobrazení detailu" pod page cache takmer nebeží — PHP sa spustí len pri cache miss. Preto je mechanizmom denný cron px_omnibus_scan (03:20 miestneho času), ktorý prejde všetko v zľave a dopíše, čo chýba. Zobrazenie ostáva ako záchrana.

Cenová báza

História sa zapisuje z get_price( 'edit' ) — z ceny uloženej na produkte, bez filtrov na woocommerce_product_get_price. Je to jediná povolená báza, vo všetkých zapisovateľoch naraz.

Plugin plošných zliav (Global Shop Discount a spol.) sa na ten filter vešia s prioritou PHP_INT_MAX vždy, keď platí ! is_admin() || DOING_AJAX. Uloženie v administrácii teda zľavnené nie je, ale zobrazenie na fronte aj request WP-Cronu áno — wp-cron.php nie je admin. Miešanie dvoch báz v jednej histórii robí z výpočtu streaku v get_lowest_price() nezmysel.

Rovnakú bázu číta aj get_lowest_price() (get_price( 'edit' ), get_regular_price( 'edit' )) — inak by sa história na fronte nezhodovala so žiadnym záznamom a výpočet „odkedy platí súčasná cena" by spadol na „od teraz" pri každom produkte.

'edit' je navyše správny predmet tvrdenia: 30-dňové minimum hovorí o cene produktu, nie o ohlásenej plošnej kampani, ktorá je samostatný konštrukt s vlastným ohlásením. Web, ktorý to vidí inak, mení to, čo sa zobrazuje (px_omnibus_lowest_price), nie to, čo sa zapisuje.

Sken

Čo Ako
Najnižšia cena PX_Omnibus::get_lowest_price( $product ) (float), ::get_html( $product )
História PX_Omnibus::get_history( $product_id )
Ručný beh PX_Omnibus::scan() — vráti počet produktov s novým záznamom
Čo berie onsale = 1 z wc_product_meta_lookup; ID, ktoré od minulého behu zo zľavy vypadli (px_omnibus_scan_seen); produkty s _sale_price_dates_* v okne ±3 dni
Čo neberie variabilných a zoskupených rodičov (nemajú vlastnú cenu a WooCommerce im ukladá viac riadkov _price naraz), koncepty a kôš vrátane variácií po rodičovi v koši
Prevádzka dávky po 200, jeden dotaz na dávku, bez načítania produktových objektov; zapisuje len tam, kde sa cena pohla. Na libike ~4 300 produktov za desatiny sekundy
Koniec akcie bez dátumov obchod, kde zľavy robí import, _sale_price_dates_* nemá — sken si preto pamätá zoznam ID z posledného behu a ďalší beh prejde aj to, čo z onsale vypadlo
Presné ID zvonka wc_after_products_starting_sales / wc_after_products_ending_sales, wc_product_start/end_scheduled_sale → PX_Omnibus::record_ids(). Bonus, nie mechanizmus — obchod bez dátumov akcie ich nevystrelí
Prečo nie woocommerce_scheduled_sales je to akcia Action Schedulera; plný prechod katalógu v cudzej fronte by po action_scheduler_failure_period (300 s) označil hostiteľskú akciu za zlyhanú
Variácie v dátach variácie cestuje px_omnibus_html; do price_html sa pripája len ak nie je prázdny
Filtre px_omnibus_display (vypne výpis, história vzniká ďalej — web, ktorý cenu ukazuje iným pluginom, neskôr prejde bez diery v dátach), px_omnibus_lowest_price, px_omnibus_variation_price_html, px_omnibus_scan_limit (0 = bez stropu, aplikuje sa na hotový zlúčený zoznam), px_omnibus_scan_ids (druhý parameter = podmnožina práve v zľave)

Platnosť akcie (sale_dates)

WooCommerce koniec akcie pozná (_sale_price_dates_to), ale nikde ho neukáže. Modul dátum vyhľadá (pri variabilnom produkte podľa poslednej zľavnenej variácie) a podá téme ako timestamp.

  • Nastavenia: zobrazenie na kartách a na detaile zvlášť, prah pre živý odpočet v hodinách (0 = len dátum), „skryť, ak je koniec ďalej než N dní", koniec kampane pre celý obchod (pre zľavy robené cenovými filtrami bez dátumu).
  • PX_Sale_Dates::get_end( $product ) / px_sale_end() — timestamp alebo 0; ::get_html() záložné vykreslenie; ::show( 'single'|'loop' ).
  • Vo woocommerce_available_variation cestuje px_sale_end.
  • Filter px_sale_end_date má posledné slovo (dátum dodá aj potlačí návratom 0).

Jednotková cena (unit_price)

Označovanie cenami (smernica 98/6/ES; zákon 108/2024 § 2 písm. h) a § 6) vyžaduje pri tovare predbalenom podľa množstva okrem predajnej ceny aj jednotkovú cenu za kilogram, liter, meter, m², m³ alebo inú jednotku množstva (kus pri tovare predávanom podľa počtu). Modul robí presne toto a nič navyše — žiadne „za 100 ml", žiadne marketingové „za praciu dávku".

  • Dáta: na produkte alebo variácii pod cenou pole Obsah balenia (množstvo) a Jednotka (ml, l, g, kg, m, m2, m3, pc), meta _px_unit_qty / _px_unit. Variácia bez údaja dedí rodiča. Hromadne: WooCommerce CSV import (stĺpce Meta: _px_unit_qty, Meta: _px_unit), PX_Unit_Price::set( $product, $qty, $unit ) alebo WP-CLI.
  • Výpočet: wc_get_price_to_display() ÷ množstvo prepočítané na základ (kg, l, m, m², m³, ks) — teda aktívna cena vrátane akcie a v daňovom režime, v akom obchod ceny zobrazuje. Keď by zaokrúhlenie obchodu dalo 0,00, pridá desatinné miesta (najviac 4).
  • Zákonné výnimky drží modul: nekreslí sa, keď by sa jednotková cena rovnala predajnej (§ 6 ods. 1 — 1 ks, presne 1 l/1 kg) a pri baleniach s menovitým množstvom najviac 50 g / 50 ml (§ 6 ods. 3 písm. a); filter px_unit_price_small_pack_exempt). Súbory rôznych tovarov za jednu cenu (§ 6 ods. 3 písm. b) sa riešia tak, že sa im údaj nevyplní. Variabilný rodič nekreslí nič — riadok patrí k cene variácie.
  • Výstup: PX_Unit_Price::get_html( $product ) → <span class="px-unit-price"><span class="screen-reader-text">Jednotková cena: </span>12,50 € / 1 l</span>; dáta ::get() / px_unit_price(). Predvolene sa vešia pod cenu v archíve (woocommerce_after_shop_loop_item_title) aj na detaile (woocommerce_single_product_summary); téma s vlastným miestom vráti false z filtra px_unit_price_display. Vo woocommerce_available_variation cestuje px_unit_price_html, live search vracia unit_price pri každom produkte.
  • Filtre: px_unit_price_units, px_unit_price_bases, px_unit_price (výsledok pred vykreslením), px_unit_price_visible (skryté ceny pre hostí — jadro samo rešpektuje PX_Guest_Prices::isHidden(); riadok prezrádza predajnú cenu, tak sa vypína aj v live search a vo variácii).
  • Kód jednotky sa pri čítaní normalizuje (L → l, ks → pc), takže CSV import s ľudsky napísanou jednotkou funguje; neznámy kód = bez riadku.
  • Vedomé rozhodnutia: variabilný rodič bez riadku (karta ukazuje rozsah cien, riadok patrí k cene variácie); lepiaca lišta na mobile a košík bez riadku (duplikát ceny zo súhrnu, ponuka je už označená).
  • Kód, ktorý produkt ukladá sám (importér pred prvým save()), použije PX_Unit_Price::assign( $product, $qty, $unit ) bez uloženia.
  • WP-CLI: wp px unit-price set <id> <qty> <unit>, clear <id>, list [--missing] [--format=csv].

GPSR (gpsr)

Nariadenie o všeobecnej bezpečnosti výrobkov (GPSR, EÚ 2023/988). Produkt má v administrácii záložku Bezpečnosť a na fronte produktový tab s rovnakým obsahom.

Každý údaj má práve jedno miesto:

Kde Polia Prečo
Značka (Produkty → Značky, product_brand) výrobca, zodpovedná osoba v EÚ opakujú sa na každom produkte značky; na produkte sa ukazujú len na čítanie s odkazom na značku, produkt s iným výrobcom dostane inú značku
Produkt krajina pôvodu, bezpečnostné upozornenia, URL bezpečnostnej dokumentácie líšia sa kus od kusu
  • PX_GPSR::get_data( $product ) vráti zlúčené dáta pre vlastnú šablónu.
  • Filtre: px_gpsr_fields (sada polí; web, ktorý má niektoré inde, ho vypustí), px_gpsr_entity_fields (ktoré polia patria značke).
  • Meta kľúče _px_gpsr_* na produkte, px_gpsr_* na terme značky.

Firemné údaje (company_fields)

Predvolene vypnutý — zapnúť až keď je preč plugin, ktorý polia riešil doteraz (WPify Woo ic_dic: kým beží, modul sa nespustí a napíše prečo).

  • Polia v pokladni: políčko „Nakupujem na firmu" za menom, pod ním názov firmy, IČO, DIČ, IČ DPH. Pravidlá povinnosti (podľa políčka alebo vyplneného názvu firmy; IČO povinné len v krajinách s registrom — SK, CZ, filter px_company_country_has_ic), kontrola formátu (IČO 8 číslic, SK DIČ 10, SK IČ DPH SK+10, EÚ prefix krajiny; SK DIČ a IČ DPH si musia odpovedať).
  • Rovnaké meta kľúče ako WPify Woo: _billing_ic, _billing_dic, _billing_dic_dph na objednávke, bez podčiarkovníka na zákazníkovi — výmena pluginu bez migrácie.
  • Doplnenie z registra: RPO (ŠÚ SR) pre SK, ARES (MF ČR) pre CZ, zadarmo bez kľúča. Tlačidlo „Načítať z registra" doplní názov a sídlo. Zaniknuté subjekty sa neponúkajú.
  • VIES: vypnuté / overiť a upozorniť / neplatné číslo zastaví objednávku. Výpadok VIES objednávku nikdy nezastaví.
  • Prenesenie daňovej povinnosti a vývoz mimo EÚ, každé samostatne. Rozhodujúca krajina podľa WooCommerce „Vypočítať daň podľa". Predvolene len s IČ DPH potvrdeným vo VIES („Vyžadovať doklad"). Dôvod sa ukladá (_px_vat_exempt_reason), ukazuje v admine a loguje.
  • Cache (firma týždeň, platné IČ DPH deň, neplatné hodinu), REST endpointy pixeler/v1/company/lookup a .../vat s limitom na IP (px_company_rate_limit, 30).
  • Údaje idú do admin objednávky, profilu zákazníka, formátovanej adresy, e-mailov a cez sf_client_data do SuperFaktúry (jej vlastný firemný blok sa vypne).
  • Šablóny: px_company_order_details( $order ), px_company_vat_reason( $order ). JS len prepína hidden a nasadzuje px-company-* triedy.
  • Ďalšie filtre: px_company_force_company_field, px_company_use_dic_dph, px_company_vat_destination, px_company_vat_decision, px_company_register_countries, px_company_details.

REST GET pixeler/v1/search?term=… vráti produkty a zodpovedajúce kategórie pre šepkávač v hlavičke. Rešpektuje „skryť vypredané", vynecháva kategórie bez viditeľných produktov a položka nesie in_stock.

Známy rozdiel: počet za „Zobraziť všetkých N výsledkov" sedí s archívom, ale nie nutne so stránkou výsledkov vyhľadávania. Šepkávač vypredané skrýva (keď je voľba zapnutá), kým WooCommerce na stránke výsledkov aplikuje len exclude-from-search — hide_out_of_stock_items rieši v get_tax_query(), ktorý beží na archívoch, nie na vyhľadávaní. Weby, ktoré chcú rovnaké čísla, si vypredané skrývajú vlastným pre_get_posts (tak to má libike).

Filtre: px_shop_core_search_limit (8), px_shop_core_search_cat_limit (4), px_shop_core_search_query_args, px_shop_core_search_code_ids, px_shop_core_search_code_limit, px_shop_core_search_response.

Vyhľadávanie podľa SKU a EAN

Hľadá sa aj podľa SKU (_sku) a EAN/GTIN (_global_unique_id) — v šepkávači aj na štandardnej stránke výsledkov (?s=…&post_type=product, cez filter posts_search). WordPress prehľadáva len názov, perex a obsah; nalepený kód dielu ani pípnutý čiarový kód dovtedy nevrátili nič. Zhoda variácie sa mapuje na rodiča — variácia nemá vlastnú stránku a na krabici býva jej SKU.

Pravidlo Prečo
SKU presne + podľa predpony od 4 znakov predpona SKU nesie význam (SKU variácie predlžuje rodičovské), ale „RAM" nesmie ťahať pol katalógu
EAN/GTIN len presne pevná dĺžka, čiastočný kód nič neznamená — a stĺpec nemá index, takže sa k nemu bežné slovo vôbec nedostane
presná zhoda potláča prefixové keď kód nesie konkrétny produkt, rodina s rovnakým začiatkom SKU je šum; presné SKU tak vráti jeden výsledok — a pri jednom výsledku presmeruje WooCommerce (wc_template_redirect(), filter woocommerce_redirect_single_search_result) rovno na produkt, ale len keď je v URL post_type=product, teda z formulára v hlavičke
prefixové zhody sa radia podľa dĺžky SKU keď ich je viac než strop, rez nesmie padnúť podľa ID produktu — päťdesiat najstarších z päťsto neodpovedá na nič. Prežije to, čo je najbližšie napísanému
variácia → rodič, GROUP BY, strop žiadne dotazy v cykle, jeden $wpdb->prepare dotaz na otázku

Zdroj dát je wc_product_meta_lookup (riadok na produkt, sku indexované, ten istý zdroj, na akom beží vyhľadávanie v administrácii WooCommerce). SKU a EAN sa pýtajú dvoma dotazmi, nie jedným OR: index má len sku, a spoločná podmienka by oň optimalizátor pripravil — každá číselná fráza (aj bežné číselné SKU) by potom znamenala full scan lookup tabuľky. Najprv teda beží indexovaná otázka na SKU a global_unique_id sa pýta, až keď nič nevrátila.

Lookup je cache, ktorú WooCommerce prepisuje pri uložení produktu — importér zapisujúci _sku priamo cez update_post_meta() ju vie nechať pozadu. Keď kód, o ktorom vieš, že existuje, vyhľadávanie nenájde, správna oprava je WooCommerce → Stav → Nástroje → Obnoviť vyhľadávacie tabuľky produktov. Dohľadanie priamo v postmeta v kóde ostáva, ale beží len pod WP-CLI (PX_Search::product_ids_matching_code() z wp eval) — je to sken každého _sku a _global_unique_id riadku bez indexu, čo verejné vyhľadávacie pole spúšťať nesmie: crawler s vymyslenými kódmi by ho vyvolal na každý request. Slúži na overenie, či je lookup pozadu.

Stĺpec global_unique_id pribudol vo WooCommerce 9.1; na starších inštaláciách sa EAN cez front end nenájde (prítomnosť stĺpca sa zisťuje raz za týždeň, transient s číslom verzie WooCommerce v kľúči).

Na stránke výsledkov sa nájdené ID pripájajú do klauzuly posts_search (OR ID IN (…)), takže post_type, post_status, ochrana heslom aj tax_query na product_visibility z hlavného dopytu platia ďalej — viditeľnosť sa nefiltruje druhýkrát. Podmienka na post_password sa pred OR odtrhne a prilepí späť za neho; ak po tom v reťazci ostane čo i len zmienka o post_password, alebo fragment nemá presne tvar AND (…), ktorý stavia jadro, filter dotaz nechá nezmenený. Žiadny best effort — polovične pochopený WHERE je presne to, čím sa vo výsledkoch začnú objavovať heslom chránené a nepublikované príspevky. Vedľajšie dopyty (widgety, súvisiace produkty) a administrácia — tá má vlastné vyhľadávanie podľa SKU od WooCommerce — ostávajú nedotknuté. Hlavné vyhľadávanie bez post_type WordPress rieši ako any, takže kód nájde produkt aj z bežného vyhľadávacieho poľa.

Helper PX_Search::product_ids_matching_code( $term, $limit ) je verejný. Filter px_shop_core_search_code_ids( $ids, $term, $limit ) je pre obchod, ktorý drží čísla dielov inde (vlastný meta kľúč, dodávateľská tabuľka) — zapojí sa sem namiesto toho, aby si vyhľadávanie písal odznova.

Odpoveď je 60 sekúnd v transiente a posiela Cache-Control: public, max-age=60. Endpoint je verejný a bez rate limitu, pri šepkávači ide request na každé písmeno — bez cache by opakovaná fráza púšťala WP_Query aj get_terms() nanovo. Cachuje sa stav pred px_shop_core_search_response, ten filter beží aj nad cachovanými dátami (nech je lacný a bez údajov viazaných na konkrétneho návštevníka).

Kľúč nesie normalizovanú frázu (malé písmená, zúžené medzery — „Prilba" a „prilba" je tá istá otázka), jazyk, a všetko, od čoho závisí price_html: menu, nastavenie „zobrazovať ceny s daňou / bez dane" a oslobodenie od DPH (WC()->customer->get_is_vat_exempt(), ktoré prepína aj modul firemných údajov tohto pluginu). Bez toho by prvý návštevník s iným daňovým kontextom otrávil ceny všetkým na 60 sekúnd. Prihlásenému zákazníkovi sa necachuje vôbec a zapisuje sa len s perzistentnou object cache (wp_using_ext_object_cache()) — inak by každá neznáma fráza z verejného endpointu znamenala dva riadky v wp_options a crawler by ich narobil státisíce. Fráza je obmedzená na 64 znakov. viewAll sa do cache nedáva: nesie pôvodné písanie návštevníka.

Dôsledok: na webe bez object cache serverová cache šepkávača nie je a zostáva len hlavička Cache-Control, ktorá opakované písmená zachytí v prehliadači a na CDN. Kto chce cache aj na serveri, zapne Redis.

px_shop_core_search_query_args( $args, $term ) dostane WP_Query argumenty skôr, než dotaz pobeží. Slúži na to, aby si web preložil časť frázy na atribútový filter — e-shop s rozmermi chce z „matrac 90x200" spraviť tax_query nad pa_rozmer a fulltextu nechať len „matrac", čo obyčajné LIKE nad post_title nikdy netrafí. Názvy atribútov patria site pluginu, plugin ich neháda. Ak filter skráti $args['s'], nech ho nechá orezaný — vyhľadanie kategórie beží nad tou istou hodnotou (a pri prázdnej sa preskočí), takže spotrebované tokeny neprepadnú do zhody na názov kategórie.

Brand tab (brand_tab)

Produktový tab „O značke" z popisu termu product_brand + logo značky. PX_Brand_Tab::get_brands_with_description( $product ) pre vlastné vykreslenie.

Wishlist (wishlist)

Obľúbené produkty: prihlásený zákazník v user meta, hosť v cookie px_wishlist; pri prihlásení sa cookie zlúči do účtu.

Čo Ako
REST GET px-shop-core/v1/wishlist, POST px-shop-core/v1/wishlist/toggle
Helpery px_wishlist_ids(), px_wishlist_has( $id ), px_wishlist_count(), px_wishlist_url()
Stránka shortcode [px_wishlist], stránka v nastaveniach (px_wishlist_page_id)
Filtre px_wishlist_url, px_products_grid_classes
CLI wp px wishlist migrate [--dry-run] (prevod z Woodmartu)

Compare (compare)

Porovnanie až štyroch produktov, cookie px_compare.

Čo Ako
REST GET px-shop-core/v1/compare, POST px-shop-core/v1/compare/toggle
Helpery px_compare_ids(), px_compare_has( $id ), px_compare_count(), px_compare_url()
Stránka shortcode [px_compare] (tabuľka atribútov), stránka v nastaveniach (px_compare_page_id)
Filtre px_compare_attribute_rows, px_compare_url

Waitlist (waitlist)

Stráženie dostupnosti vypredaného produktu (aj variácie).

  • Double opt-in — adresa sa počíta až po kliknutí v e-maile; odhlásenie jedným odkazom v každej správe maže adresu zo všetkých produktov.
  • Štyri e-maily ako bežné WooCommerce triedy (WooCommerce → Nastavenia → E-maily, šablóny woocommerce/emails/px-waitlist-*.php): potvrdenie adresy, potvrdené prihlásenie, naskladnenie, notifikácia pre admina.
  • Formulár PX_Waitlist::render_form( $product ) / ::get_form_html(), REST POST px-shop-core/v1/waitlist.
  • Metabox na produkte so zoznamom prihlásených, PX_Waitlist::count().
  • Akcie: px_waitlist_subscribed, px_waitlist_confirmed, px_waitlist_unsubscribed, px_waitlist_back_in_stock. Filtre: px_waitlist_require_confirmation, px_waitlist_show_form.
  • CLI: wp px waitlist migrate [--dry-run] z Woodmartu.

Veľkostné tabuľky (size_guide)

CPT px_size_guide (Produkty → Veľkostné tabuľky) s tabuľkou (riadok na riadok, bunky cez | alebo tabulátor) a voliteľným textom. Produkt si tabuľku vyberie sám, inak zdedí z kategórie; „nezobrazovať" potlačí aj kategóriu.

  • PX_Size_Guide::render() — odkaz + modál (<dialog>), ::get_content_html() samotný obsah, ::has_guide(). Témy so woocommerce_single_product_summary dostanú modál samy.
  • Filter px_size_guide_button_text.
  • CLI: wp px size-guide migrate [--dry-run] z Woodmartu; dočasný most číta woodmart_sguide_select, kým dáta nie sú prevedené.

Doplnky raz na kategórii: Bicykle → Svetlá, Košíky na fľaše. Väzba je jednosmerná a dedí sa po strome (podkategória bez nastavenia sa pýta rodiča; stĺpec Súvisiace v Produkty → Kategórie). Produkty sa berú striedavo z každej kategórie, poradie voliteľné (najpredávanejšie / najnovšie / poradie v kategórii), vypredané a z katalógu vylúčené sa neponúkajú.

  • Cache na množinu kategórií, neplatní ju uloženie kategórie.
  • PX_Related_Cats::get_product_ids( $product_id, $limit ) / px_related_category_ids(), ::get_title().
  • Voliteľne aj v košíku (cross-sell, klasický košík).
  • Filtre: px_related_cats_term_ids, px_related_cats_product_ids, px_related_cats_query_args, px_related_cats_title.

Shipping bar (shipping_bar)

Dáta pre lištu „do dopravy zadarmo chýba X €": px_free_shipping_state() vráti null (doprava zadarmo sa neponúka) alebo [ threshold, remaining, progress 0–100 ]. Prah sa berie z metódy Free shipping v aktívnej zóne (počíta sa ako WooCommerce: zobrazený medzisúčet mínus zľavy); filter px_free_shipping_threshold.

Katalógový režim (catalog)

„Výklad" bez nákupu: režim a skrytie cien sa zapína v nastaveniach modulu, s voliteľným náhradným tlačidlom (text + odkaz, napr. „Dopyt"). Téma používa px_catalog_mode(), px_catalog_hide_price(), px_catalog_button( $product, $classes ); filter px_catalog_button_class.

Obrázky atribútov (attribute_image)

Pole s obrázkom na každom terme atribútu pa_* (pre swatche a filtre), so stĺpcom v zozname termov. PX_Attribute_Image::image_id( $term_id ), ::image( $term_id, $size, $attr ); helpery px_get_term_image_id(), px_get_term_image() sú chránené pred redeklaráciou.

Content bloky (content)

Neverejný CPT px_content pre znovupoužiteľné bloky (bannery, USP pásy) s layoutmi (px_content_layouts, predvolene media-right), zarovnaním a kategóriami. Nezávisí od WooCommerce.

  • Helpery: px_get_content_items( $category, $limit ), px_get_banner( $post ), px_banner( $item, $args ), px_banners( $args ), px_content_template( $name, $args ).
  • Shortcode [px_banner]. Šablóny v templates/content/, prepis v téme cez px_content_locate_template.
  • Video na pozadí (YouTube/Vimeo alebo MP4/WebM z knižnice), odkaz celého bannera a karusel (carousel) od 1.10.0, vyžadujú px-shop-theme 0.6.0+ — podrobne v templates/content/README.md.
  • Filtre: px_content_banner_data, px_content_banner_html, px_content_banner_classes, px_content_text_html, px_content_default_layout, px_content_style_handle, px_content_video_script_handle, px_content_video_consent, px_content_video_cmp, px_content_video_cmp_category, px_content_carousel_handle, px_content_carousel_labels.

Vlastná CMP namiesto Complianzu. Predvolene vypnutá — web, ktorý si necháva externú CMP (CookieYes, Complianz, Cookiebot), ju nechá vypnutú a z modulu sa nenačíta nič. Zapnutý modul sa pri aktívnej cudzej CMP sám nespustí a napíše to v admine; zároveň vypne most consent_mode, lebo signály posiela sám.

Nastavenia: WooCommerce → Nastavenia → PX Shop → Súhlas s cookies (služby, ich ID, verzia zásad, stránka so zásadami).

Čo modul robí:

  • Register služieb je jediný zdroj pravdy — jedna deklarácia v PHP živí lištu, modál, stránku so zásadami aj blokovanie. Vstavaný katalóg: GA4, Google Ads, GTM, Meta Pixel, Microsoft Clarity, Smartsupp, Heureka, YouTube, Google Maps, reCAPTCHA + cookies WordPressu, WooCommerce a PX Shop.
  • Lišta v dvoch vrstvách: Prijať všetko / Odmietnuť všetko / Nastavenia rovnakej váhy, žiadne predznačené políčka, žiadna cookie wall; v modále prepínače per kategória aj per služba.
  • Súhlas v cookie px_consent (verzia zásad, čas, ID, kategórie, služby), platnosť 182 dní. Zmena Verzie zásad = lišta sa spýta znova. PHP cookie nečíta — výstup je rovnaký pre všetkých, page cache nemá čo pokaziť.
  • Blokovanie skriptov cez type="text/plain"; pri odvolaní sa zmažú známe cookies a stránka sa načíta znova.
  • Google (GA4, Ads, GTM) sa neblokuje, riadi sa signálmi Consent Mode v2: consent default denied v <head>, consent update na px:consent, dataLayer event px_consent_update. Meta Pixel dostáva fbq('consent').
  • Vložené videá a mapy dostanú zástupný box s tlačidlom; YouTube cez youtube-nocookie.com; bez JS ostáva odkaz na pôvodný obsah.
  • Podstránka so shortcodom [px_cookie_policy] vzniká pri zapnutí ako koncept.

Kontrakt pre tému a site plugin:

Čo Ako
Je funkcia dostupná? class_exists( 'PX_Consent' ) && PX_Consent::active()
Odkaz „Nastavenia cookies" data-px-consent-settings na tlačidle, alebo window.pxConsent.open()
Reakcia na voľbu document.addEventListener( 'px:consent', e => e.detail.categories )
Vlastný skript tretej strany `