" greeting, etc.) is meant to add its own * shell/hydration pair here too rather than growing a second, undocumented * convention (see par. 5 below). * * This file has no dependency on any Breakdance runtime class and only touches * FluentCart classes behind a guard, so it loads standalone (same discipline as * Contracts.php/Tokens.php/ControlPresets.php, see docs/element-authoring.md). * * --------------------------------------------------------------------------- * 1. Why this exists (the G0H finding, extended) * --------------------------------------------------------------------------- * G0H (ElementRenderer::miniCart(), assets/mini-cart-hydrate.js) already * established the pattern for the golf-0 compat-alias Mini Cart: FluentCart's * own renderers bake the CURRENT visitor's live session state directly into * shortcode HTML, so a full-page cache plugin storing that HTML would leak one * visitor's cart to every later visitor of the same cached page. The fix is * "cacheable shell now, hydrate client-side a moment later", never * "DONOTCACHEPAGE the whole site because a header widget exists". * * FC_Mini_Cart (this fork, WP8) needed the SAME shell/hydrate pattern for a * second, richer surface: the offcanvas/drawer renderer, * `FluentCart\App\Services\Renderer\CartDrawerRenderer` (verified * `app/Services/Renderer/CartDrawerRenderer.php:10`, already a Contracts.php * entry from the G2K kernel-commit). Verifying that renderer against the live * sparkcore-shop source turned up a NEW leak beyond the one G0H already fixed * for the dropdown badge: * * - `CartDrawerRenderer::renderFooter()` (CartDrawerRenderer.php:151-167) calls * `$this->cartRenderer->renderTotal()`. * - `CartRenderer::renderTotal()` (CartRenderer.php:349-357) does NOT read * `$this->cartItems` (the array actually passed into the renderer's * constructor) at all. It unconditionally calls * `CartCheckoutHelper::make()->getItemsAmountSubtotal(true, true)`, which * reads the CURRENT request's live session cart * (`CartCheckoutHelper::getItems()` -> `CartHelper::getCart()`), completely * independent of the constructor argument. * - This is a real, exploitable divergence from its sibling renderer: * `MiniCartRenderer::renderMiniCart()` (MiniCartRenderer.php:52) DOES guard * this exact case: `empty($this->cartItems) ? Helper::toDecimal(0, true) : * CartCheckoutHelper::make()->getItemsAmountSubtotal(true, true)`. Passing an * empty cart array to `MiniCartRenderer` is genuinely neutral; passing an * empty cart array to `CartDrawerRenderer` is NOT neutral for the footer * total node, even though every other node in that same render (item count, * line-item list, badge) correctly reflects the empty array passed in. * * `neutralizeDrawerTotalShell()` below closes that one specific gap, the same * way `ElementRenderer::neutralizeMiniCartShell()` (a file this fork does not * own, see docs/element-authoring.md par. "gedeeld") already closes the * dropdown badge/price gap for the golf-0 alias. Everything else in a * `CartDrawerRenderer([], ['item_count' => 0, 'open_cart' => false])->render()` * call is already neutral by construction: item count and the header/badge * counter come from the `item_count` config key (not live session data), and * `CartRenderer::renderList([])` on an empty array renders FluentCart's own * real, valid "cart is empty" state (`renderEmpty()`, * `.fluent-cart-cart-empty-content`), not a placeholder we invented. * * --------------------------------------------------------------------------- * 2. Hydration route (shared mechanism with G0H, same route name on purpose) * --------------------------------------------------------------------------- * FC_Mini_Cart hydrates through the exact same live-fetch mechanism G0H already * verified and shipped for the compat-alias Mini Cart: FluentCart's own public * bootstrap singleton, `window.fluentCartCart` (`FluentCartApp.js`, assigned on * `DOMContentLoaded`), specifically: * - `getCart()`: GET `{ajaxurl}?action=fluent_cart_checkout_routes& * fc_checkout_action=fluent_cart_cart_status`, returns the live * `cart_data` array (`CartResource::getStatus()`, api/Resource/ * FrontendResource/CartResource.php:412-429). Also dispatches * `fluentCartNotifyCartDrawerItemChanged` (already a Contracts.php * js-event entry). * - `updateCartTotalPrice(cartData)`: FluentCart's own currency-aware * subtotal formatter, targeting `[data-fluent-cart-cart-total-price]`. * The route name is kept identical to `ElementRenderer::MINI_CART_HYDRATION_ROUTE` * ('fluentcart-own-bootstrap-v1') on purpose: it is the same underlying * mechanism applied to a second element, not a competing one. Both must be * bumped together if the mechanism itself ever changes; see * `tests/cart/run-cart.php` for the cross-file literal assertion (this fork * cannot edit ElementRenderer.php, so the check is a source-text comparison, * not a shared constant). * * cart_data items carry FluentCart's own pre-formatted `line_total_formatted` * string (`CartHelper::generateCartItemFromVariation()`, * app/Helpers/CartHelper.php:53) alongside raw `title`/`post_title`/ * `featured_media`/`view_url`/`quantity`. `assets/cart-hydration.js` renders * drawer line items directly from those fields with zero currency math or * price formatting of its own (the one arithmetic operation anywhere in this * fork's JS is `cartData.length` for the item count, mirroring * `MiniCartShortcode::render()`'s own `count($cartData)`, exactly like G0H's * `mini-cart-hydrate.js` already does) -- no commerce logic is reimplemented, * per the binding rule. * * --------------------------------------------------------------------------- * 3. The known FluentCart inline-localize leak (documented, not fixable here) * --------------------------------------------------------------------------- * The G0H analysis (assets/mini-cart-hydrate.js docblock) already found that * FluentCart's own frontend bootstrap does NOT re-fetch the cart on load; the * only thing it paints at init time comes from an inline localized script, * `window.fluentcart_drawer_vars.cart_item_count`, baked into the page at * request time by FluentCart itself * (`app/Modules/Templating/AssetLoader.php:406-410`: * `'cart_item_count' => $isInstantCheckout ? 0 : ($cart ? count($cart->cart_data ?? []) : 0)`). * * That inline `