CLAUDE.md 7.6 KB

CLAUDE.md - Studiou WC Free Photo Product

Project Overview

WordPress/WooCommerce plugin called studiou-wc-free-photo-product (QDR - Studiou WC Free Photo Product) v1.3.4. Allows publishing special variable products where customers upload custom raw images to be printed for a price.

Initial Requirements

  1. WP (6.9.4) / WooCommerce (10.6.2) plugin with variants and custom raw image upload for photo printing. Frontend shows product with upload box and preview on the WooCommerce product detail page.
  2. Product configuration area: Media Category for uploaded files, Maximum raw file size. Shortcode to embed product on any page.
  3. Uploaded raw files stored in WP media library with a specific media category per product. Chunked upload to bypass PHP upload size limits. Media files linked with product.
  4. Admin summary page: list of uploaded files, download as ZIP (bulk), order number, delete, change state (downloaded, ready, solved).
  5. HPOS support and localization (English, Czech).

Tech Stack

  • PHP 8.2+, WordPress 6.9.4+, WooCommerce 10.0+
  • Hard plugin dependency: Product Variant Table for WooCommerce (pvtfw, folder slug product-variant-table-for-woocommerce) — renders variants as a custom <table class="variant"> with per-row qty inputs and its own pvtfw_woocommerce_ajax_add_to_cart handler. Declared via the Requires Plugins: header and enforced at runtime by Studiou_WC_Free_Photo_Product::is_pvtfw_active().
  • jQuery for frontend/admin JS
  • WordPress AJAX (admin-ajax.php) for chunked upload
  • WC session storage for passing file data between upload and add-to-cart (theme-agnostic)
  • Custom DB table via dbDelta for file tracking
  • Custom taxonomy studiou_media_category on attachment post type

Architecture

  • Main class: Studiou_WC_Free_Photo_Product in root plugin file
  • Constants prefix: STUDIOU_WCFPP_
  • CSS class prefix: studiou-fpp-
  • JS namespace: studiouWcfppFront (frontend), studiouWcfpp (admin)
  • Nonces: studiou-wcfpp-front-nonce (frontend), studiou-wcfpp-nonce (admin)
  • Text domain: studiou-wc-free-photo-product

Key Classes

  • Studiou_WC_FPP_DB - Custom table CRUD, media category taxonomy
  • Studiou_WC_FPP_Product - WC product data tab, product meta
  • Studiou_WC_FPP_Upload - Chunked upload AJAX, file assembly, attachment creation
  • Studiou_WC_FPP_Single_Product - Upload area on product page (woocommerce_single_product_summary priority 35), WC session storage, server-side validation (woocommerce_add_to_cart_validation priority 1) + safety net (woocommerce_add_to_cart removes items without photo)
  • Studiou_WC_FPP_Shortcode - [studiou_free_photo_product id="X"] renders product with WC native variation form
  • Studiou_WC_FPP_Cart - Reads file data from WC session via woocommerce_add_cart_item_data, replaces cart/order item thumbnails (classic cart via woocommerce_cart_item_thumbnail, block cart via woocommerce_product_get_image_id/woocommerce_product_variation_get_image_id), order item meta, order linking
  • Studiou_WC_FPP_Admin - Summary page with filters/pagination, status management, ZIP download, file download with auto-status-change, bulk actions with confirmation dialogs
  • Studiou_WC_FPP_Pricing - Per-variation quantity discount tiers. Admin editor on woocommerce_variation_options_pricing, save via woocommerce_save_product_variation, runtime discount via woocommerce_before_calculate_totals (priority 20) using set_price() on the cart item's product instance. Tier meta stored as a PHP array in _studiou_fpp_qty_tiers on each product_variation post. Feature is gated to FPP-enabled parent products. Frontend: pre-selection notice on woocommerce_single_product_summary@25, inline tier-data JSON emitted via wp_footer as window.studiouFppTierData, consumed by initVariantTableTiers() in assets/js/frontend.js to wire up per-row qty→price updates on the pvtfw variant table. Native WC variations_form handling is kept as a fallback path.

Frontend Flow

Product page is rendered by the Product Variant Table for WooCommerce (pvtfw) plugin, which is a hard dependency. Each variation appears as a <tr> in <table class="variant"> with its own input.qty, .woocommerce-Price-amount, and .pvtfw_variant_table_cart_btn[data-variant="{variation_id}"]. There is no WC native form.variations_form on the page.

  1. Upload area rendered below variation table via woocommerce_single_product_summary (priority 35)
  2. On page load, existing session state restored (preview, gallery image)
  3. Customer uploads photo via chunked AJAX
  4. On upload complete, JS stores file ref in WC session
  5. Product gallery image replaced with uploaded photo preview; restored on remove
  6. Customer selects variant and clicks any Add to Cart button (buttons look normal — no client-side disabling)
  7. woocommerce_add_to_cart_validation (priority 1) checks WC session — blocks with error notice if no photo
  8. woocommerce_add_to_cart safety net — removes item if it was added without photo data
  9. woocommerce_add_cart_item_data reads file data from WC session (session NOT cleared — same photo reusable for multiple variants)
  10. Cart thumbnails: classic cart via woocommerce_cart_item_thumbnail, block cart via woocommerce_product_get_image_id/woocommerce_product_variation_get_image_id
  11. Order admin: thumbnail via woocommerce_admin_order_item_thumbnail, download link via woocommerce_after_order_itemmeta (auto-marks status as "downloaded")
  12. On checkout, file record linked to order via woocommerce_checkout_order_processed (classic), woocommerce_store_api_checkout_order_processed (block), woocommerce_thankyou (fallback)
  13. Session only cleared when user clicks Remove
  14. Admin photo files view: download marks "ready" -> "downloaded", bulk actions with confirmations, pagination preserves filters
  15. Quantity discount UI: the pricing class emits window.studiouFppTierData in wp_footer; initVariantTableTiers() finds each row by data-variant, appends a tier strip under the price cell, and binds input.qty / .qty-count clicks to recompute the displayed price. Server-side woocommerce_before_calculate_totals (priority 20) remains authoritative and calls set_price() on each matching cart item regardless of how it was added.

Coding Conventions

  • Follow patterns from sibling plugin studiou-wc-product-cat-manage
  • All PHP files start with if (!defined('WPINC')) { die; }
  • AJAX handlers: verify nonce, check capabilities, try/catch with WP_DEBUG logging
  • Output buffer cleanup in AJAX handlers
  • Use wp_send_json_success / wp_send_json_error for all AJAX responses
  • All user-facing strings wrapped in __() or esc_html_e() with text domain
  • Sanitize all inputs: absint(), sanitize_text_field(), sanitize_file_name()
  • Escape all outputs: esc_html(), esc_attr(), esc_url(), wp_kses_post()

Build / Deploy Notes

  • Generate .mo from .po: php languages/generate-mo.php or wp i18n make-mo languages/
  • Plugin creates DB table on activation via register_activation_hook
  • Activation hook requires explicit require_once for DB class (runs before plugins_loaded)
  • Temp chunks stored in wp-content/uploads/studiou-fpp-chunks/ (cleaned on deactivation)
  • Assembled files stored in wp-content/uploads/YYYY/MM/free-photo/

Version Bumping

When changing JS or CSS files, always bump the plugin version in the main PHP file header AND the STUDIOU_WCFPP_VERSION constant to bust browser cache. Also bump the version string in languages/studiou-wc-free-photo-product.pot and languages/studiou-wc-free-photo-product-cs_CZ.po header, and update the v… reference at the top of this CLAUDE.md.