You've already forked wc-composable-product
Added comprehensive Session 12 documentation covering: - Two critical bug fixes (admin tabs + frontend empty state) - Root cause analysis for both issues - CSS panel hiding solution - Twig empty state conditional - Translation updates for all 6 locales - Key lessons learned about CSS timing and empty states - Debugging approach and testing recommendations Fixed markdown linting warnings (MD036, MD032). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1430 lines
58 KiB
Markdown
1430 lines
58 KiB
Markdown
# WooCommerce plugin for user composable products - AI Context Document
|
|
|
|
**Author:** Marco Graetsch
|
|
|
|
## Project Overview
|
|
|
|
This plugin implements a special product type, for which users can select a limited number of product from a configurable set of simple or variable products. The limit of selectable products should be a global and per-product setting, for which global is the fallback. The set of selectable products can be defined per category, tag or SKU. The price is either a fixed price or the sum of the prices of the selected products. Think of a package of stickers as composable product, where each package can contain $limit number of stickers.
|
|
|
|
### Key Fact: 100% AI-Generated
|
|
|
|
This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase was created through AI assistance.
|
|
|
|
## Technical Stack
|
|
|
|
- **Language:** PHP 8.3+
|
|
- **Framework:** Latest WordPress Plugin API
|
|
- **E-commerce:** WooCommerce 10.0+
|
|
- **Template Engine:** Twig 3.0 (via Composer)
|
|
- **Frontend:** Vanilla JavaScript + jQuery
|
|
- **Styling:** Custom CSS
|
|
- **Dependency Management:** Composer
|
|
- **Internationalization:** WordPress i18n (.pot/.po/.mo files)
|
|
|
|
## Implementation Details
|
|
|
|
### Security Best Practices
|
|
|
|
- All user inputs are sanitized (integers for quantities/prices)
|
|
- Nonce verification on form submissions
|
|
- Output escaping in templates (`esc_attr`, `esc_html`, `esc_js`)
|
|
- Direct file access prevention via `ABSPATH` check
|
|
|
|
### Translation Ready
|
|
|
|
All user-facing strings use:
|
|
|
|
```php
|
|
__('Text to translate', 'wc-composable-product')
|
|
_e('Text to translate', 'wc-composable-product')
|
|
```
|
|
|
|
Text domain: `wc-composable-product`
|
|
|
|
**Translation Template:**
|
|
|
|
- Base `.pot` file created: `languages/wc-composable-product.pot`
|
|
- Ready for translation to any locale
|
|
- All translatable strings properly marked with text domain
|
|
|
|
**Available Translations:**
|
|
|
|
- `en_US` - English (United States) [base language - .pot template]
|
|
- `de_DE` - German (Germany, formal) ✓ Complete
|
|
- `de_DE_informal` - German (Germany, informal "du") ✓ Complete
|
|
- `de_CH` - German (Switzerland, formal "Sie") ✓ Complete
|
|
- `de_CH_informal` - German (Switzerland, informal "du") ✓ Complete
|
|
- `fr_CH` - French (Switzerland) ✓ Complete
|
|
- `it_CH` - Italian (Switzerland) ✓ Complete
|
|
|
|
All .po files created with 40+ translated strings. Swiss locales include CHF currency formatting in examples (e.g., "CHF 50.-").
|
|
|
|
To compile translations to .mo files for production:
|
|
|
|
```bash
|
|
for po in languages/*.po; do msgfmt -o "${po%.po}.mo" "$po"; done
|
|
```
|
|
|
|
### Create releases
|
|
|
|
- The `vendor/` directory MUST be included in releases (Twig dependency required for runtime)
|
|
- Running zip from wrong directory creates empty or malformed archives
|
|
- Exclusion patterns must match the relative path structure used in zip command
|
|
- Always verify the package with `unzip -l` and test extraction before committing
|
|
- The `wp-core/` and `wp-plugins/` directories MUST NOT be included in releases
|
|
- Releases are stored in `releases/` including checksums
|
|
|
|
**Important Git Notes:**
|
|
|
|
- Always commit from `dev` branch first
|
|
- Tags should use format `vX.X.X` (e.g., `v1.1.22`)
|
|
- Use annotated tags (`-a`) not lightweight tags
|
|
- Commit messages should follow the established format with Claude Code attribution
|
|
- `.claude/settings.local.json` changes are typically local-only (stash before rebasing)
|
|
|
|
#### What Gets Released
|
|
|
|
- All plugin source files
|
|
- Compiled vendor dependencies
|
|
- Translation files (.mo compiled from .po)
|
|
- Assets (CSS, JS)
|
|
- Documentation (README, CHANGELOG, etc.)
|
|
|
|
#### What's Excluded
|
|
|
|
- Git metadata (`.git/`)
|
|
- Development files (`.vscode/`, `.claude/`, `CLAUDE.md`)
|
|
- Logs and cache files
|
|
- Previous releases
|
|
- `composer.lock` (but `vendor/` is included)
|
|
|
|
## Project Structure
|
|
|
|
```txt
|
|
wc-composable-product/
|
|
├── assets/
|
|
│ ├── css/
|
|
│ │ ├── admin.css # Admin panel styling
|
|
│ │ └── frontend.css # Customer-facing styles (with stock indicators)
|
|
│ └── js/
|
|
│ ├── admin.js # Product edit interface logic
|
|
│ └── frontend.js # AJAX cart & selection UI
|
|
├── cache/ # Twig template cache (writable)
|
|
├── includes/
|
|
│ ├── Admin/
|
|
│ │ ├── Product_Data.php # Product data tab & meta boxes
|
|
│ │ └── Settings.php # WooCommerce settings integration
|
|
│ ├── Cart_Handler.php # Add-to-cart & cart display logic (with stock validation)
|
|
│ ├── Plugin.php # Main plugin class (Singleton)
|
|
│ ├── Product_Selector.php # Frontend product selector renderer (with stock info)
|
|
│ ├── Product_Type.php # Custom WC_Product extension
|
|
│ └── Stock_Manager.php # Stock management & inventory tracking (v1.1.0+)
|
|
├── languages/
|
|
│ └── wc-composable-product.pot # Translation template
|
|
├── releases/ # Releases files
|
|
├── templates/
|
|
│ └── product-selector.twig # Frontend selection interface (with stock display)
|
|
├── vendor/ # Composer dependencies (gitignored)
|
|
├── composer.json # Dependency configuration
|
|
├── wc-composable-product.php # Main plugin file
|
|
└── [Documentation files] # README, INSTALL, IMPLEMENTATION, etc.
|
|
```
|
|
|
|
## Architecture Overview
|
|
|
|
### Core Classes
|
|
|
|
1. **Plugin.php** - Main singleton class
|
|
- Initializes Twig template engine
|
|
- Registers hooks and filters
|
|
- Manages asset enqueuing
|
|
- Provides template rendering API
|
|
|
|
2. **Product_Type.php** - Custom WooCommerce product type
|
|
- Extends `WC_Product`
|
|
- Handles selection criteria (category/tag/SKU)
|
|
- Manages pricing modes (fixed/sum)
|
|
- Queries available products dynamically
|
|
|
|
3. **Cart_Handler.php** - Cart integration
|
|
- Validates product selections
|
|
- Stores selected products in cart meta
|
|
- Calculates dynamic pricing
|
|
- Displays selections in cart/checkout
|
|
|
|
4. **Product_Selector.php** - Frontend renderer
|
|
- Renders Twig template with product data
|
|
- Applies display settings (images/prices/total)
|
|
|
|
5. **Admin/Product_Data.php** - Product edit interface
|
|
- Adds "Composable Options" tab
|
|
- Category/tag/SKU selection fields
|
|
- Saves product metadata
|
|
|
|
6. **Admin/Settings.php** - Global settings
|
|
- WooCommerce settings tab integration
|
|
- Default limits and pricing mode
|
|
- Display preferences
|
|
|
|
7. **Stock_Manager.php** - Inventory management (v1.1.0+)
|
|
- Stock validation for selected products
|
|
- Automatic stock deduction on order completion
|
|
- Stock restoration on order cancellation/refund
|
|
- Order notes for audit trail
|
|
- Backorder support detection
|
|
|
|
### Data Flow
|
|
|
|
**Product Creation:**
|
|
|
|
1. Admin selects "Composable product" type
|
|
2. Configures criteria, limits, pricing in product data tab
|
|
3. Metadata saved: `_composable_*` fields
|
|
|
|
**Frontend Display:**
|
|
|
|
1. `Cart_Handler::render_product_selector()` called on product page
|
|
2. `Product_Type::get_available_products()` queries matching products
|
|
3. `Product_Selector::render()` passes data to Twig template
|
|
4. JavaScript handles selection UI and AJAX
|
|
|
|
**Add to Cart:**
|
|
|
|
1. Customer selects products (JS validates limit)
|
|
2. AJAX request with `composable_products[]` array
|
|
3. `Cart_Handler::validate_add_to_cart()` server-side validation
|
|
4. `Stock_Manager::validate_stock_availability()` checks stock levels (v1.1.0+)
|
|
5. `Cart_Handler::add_cart_item_data()` stores selections
|
|
6. `Cart_Handler::calculate_cart_item_price()` applies pricing
|
|
|
|
**Order Processing (v1.1.0+):**
|
|
|
|
1. Order status changes to completed/processing
|
|
2. `Stock_Manager::reduce_stock_on_order_complete()` deducts inventory
|
|
3. Selected product IDs stored in order meta: `_composable_products`
|
|
4. Order notes added documenting stock changes
|
|
5. On cancellation/refund: `Stock_Manager::restore_stock_on_order_cancel()` reverses deduction
|
|
|
|
## Development Workflow
|
|
|
|
### Initial Setup
|
|
|
|
```bash
|
|
composer install
|
|
```
|
|
|
|
### Making Changes
|
|
|
|
1. **PHP Classes:** Edit files in `includes/` or `includes/Admin/`
|
|
2. **Templates:** Modify `templates/*.twig` (cache clears on auto-reload)
|
|
3. **Styles:** Update `assets/css/*.css`
|
|
4. **JavaScript:** Edit `assets/js/*.js`
|
|
5. **Translations:** Update `.pot` file, create `.po` translations
|
|
|
|
### Testing Checklist
|
|
|
|
- [ ] Create composable product in admin
|
|
- [ ] Test each selection criteria type (category/tag/SKU)
|
|
- [ ] Verify selection limit enforcement
|
|
- [ ] Test both pricing modes (fixed/sum)
|
|
- [ ] Check AJAX add-to-cart functionality
|
|
- [ ] Verify cart display shows selected products
|
|
- [ ] Test checkout process
|
|
- [ ] Check responsive design on mobile
|
|
- [ ] Validate all strings are translatable
|
|
|
|
### Creating Releases
|
|
|
|
```bash
|
|
# From project root
|
|
zip -r wc-composable-product-vX.X.X.zip . \
|
|
-x "*.git*" "*.vscode*" "*.claude*" "CLAUDE.md" \
|
|
"wp-core" "wp-plugins" "*.log" "composer.lock" \
|
|
"cache/*" "releases/*" "*.zip"
|
|
|
|
# Verify contents
|
|
unzip -l wc-composable-product-vX.X.X.zip
|
|
|
|
# IMPORTANT: Ensure vendor/ is included!
|
|
```
|
|
|
|
## Bugs found
|
|
|
|
- ✅ ~~There is a bug related to twig in the frontend area. Documented in `logs/fatal-errors*.log`~~ **FIXED in v1.1.5**
|
|
- ✅ ~~Translate the admin area, too~~ **COMPLETED in v1.1.6** - All admin strings now translated to 6 locales
|
|
- ✅ ~~Small rendering Bug in admin area. If you load the side, on first view it shows the first both tabs.~~ **FIXED in v1.1.8**
|
|
- ✅ ~~In the frontend, regardless which selection mode you use, there appears no product selection in any way.~~ **FIXED in v1.1.8**
|
|
- ✅ ~~The pricing field in the frontend should be rendered as localized price field include currency.~~ **FIXED in v1.1.8**
|
|
- Still no product selection in frontend. Current mode 'by Category', but 'by tag' also didn't work
|
|
- The tab rendering is still no correct. first both tabs are shown on initial page load. After clicking a tab, they behave as expected.
|
|
|
|
## Session History
|
|
|
|
### v1.0.0 - Initial Implementation & Release (2024-12-31)
|
|
|
|
#### Session 1: Core Implementation
|
|
|
|
- Complete plugin implementation from scratch
|
|
- All 6 core PHP classes with PSR-4 autoloading
|
|
- Twig template system integration
|
|
- Responsive frontend with AJAX functionality
|
|
- Admin interface with WooCommerce integration
|
|
- Full i18n support with .pot template
|
|
- Comprehensive documentation (README, INSTALL, IMPLEMENTATION)
|
|
- Initial commit to `main` branch (1edb0be)
|
|
|
|
#### Session 2: Documentation & Translations
|
|
|
|
- Enhanced CLAUDE.md with complete architecture documentation
|
|
- Created 6 complete translation files (.po):
|
|
- German (Germany - formal & informal)
|
|
- German (Switzerland - formal & informal)
|
|
- French (Switzerland)
|
|
- Italian (Switzerland)
|
|
- All 40+ strings translated with locale-specific terminology
|
|
- Swiss locales include CHF currency formatting examples
|
|
|
|
#### Session 3: Release Creation
|
|
|
|
- Created annotated git tag `v1.0.0`
|
|
- Generated release package: `wc-composable-product-v1.0.0.zip` (371 KB)
|
|
- Verified vendor/ directory inclusion (336 Twig files)
|
|
- Created SHA-256 and MD5 checksums
|
|
- Stored in `releases/` directory (gitignored)
|
|
|
|
**Key decisions made:**
|
|
|
|
- Used Singleton pattern for main Plugin class
|
|
- Twig for templating (per requirements)
|
|
- Vanilla JS + jQuery for frontend (WooCommerce standard)
|
|
- Grid layout for product selector (responsive)
|
|
- AJAX add-to-cart for better UX
|
|
- Meta-based configuration storage
|
|
|
|
**Files created:** 28 files total (21 PHP/templates + 7 translations), 3,842 lines of code
|
|
|
|
**Git workflow:**
|
|
|
|
- Main branch: Initial implementation (1edb0be)
|
|
- Dev branch: +2 commits for documentation and translations
|
|
- Tagged: v1.0.0 on dev branch (8c17734)
|
|
|
|
**What works:**
|
|
|
|
- Product type registration ✓
|
|
- Admin product data tab ✓
|
|
- Category/tag/SKU selection ✓
|
|
- Frontend product selector ✓
|
|
- AJAX add-to-cart ✓
|
|
- Cart integration ✓
|
|
- Pricing calculation (both modes) ✓
|
|
- Full multilingual support (6 locales) ✓
|
|
- Production-ready release package ✓
|
|
|
|
**Known limitations:**
|
|
|
|
- Currently only simple products in selection (not variable)
|
|
- No grouped product support
|
|
- Template cache requires manual clearing after updates
|
|
- Translations are .po files only (not compiled to .mo yet)
|
|
|
|
**Release details:**
|
|
|
|
- Package size: 371 KB
|
|
- Includes: All source + vendor dependencies + translations
|
|
- Checksums: SHA-256 and MD5 provided
|
|
- Ready for WordPress installation (no composer install needed)
|
|
|
|
**Future enhancements to consider:**
|
|
|
|
- Variable product support
|
|
- Quantity selection per item
|
|
- Visual bundle preview
|
|
- Product recommendations
|
|
- Selection presets/templates
|
|
- Compile .mo translation files
|
|
|
|
---
|
|
|
|
### v1.0.1 - Bug Fix (2024-12-31)
|
|
|
|
**Critical bug fix:** Fatal error "Class WC_Settings_Page not found" during plugin activation
|
|
|
|
**Root cause:** Plugin initialized on `plugins_loaded` hook before WooCommerce classes were available
|
|
|
|
**Solution:** Changed initialization hook to `woocommerce_loaded` in wc-composable-product.php:65
|
|
|
|
**Impact:** Settings page now correctly integrates as tab in WooCommerce > Settings
|
|
|
|
**Files modified:**
|
|
|
|
- wc-composable-product.php (version bump to 1.0.1, hook change)
|
|
- CHANGELOG.md (documented fix)
|
|
|
|
**Commit:** a581ef4
|
|
|
|
---
|
|
|
|
### v1.1.0 - Stock Management Integration (2024-12-31)
|
|
|
|
#### Session 4: Stock Management Implementation
|
|
|
|
**Major feature release** adding comprehensive inventory tracking for composable products.
|
|
|
|
**What was built:**
|
|
|
|
1. **New Stock_Manager class** (includes/Stock_Manager.php - 7.7 KB, 263 lines)
|
|
- `validate_stock_availability()` - Real-time stock checking
|
|
- `get_product_stock_info()` - Stock data for frontend display
|
|
- `reduce_stock_on_order_complete()` - Automatic deduction on order completion
|
|
- `restore_stock_on_order_cancel()` - Automatic restoration on cancellation/refund
|
|
- `prevent_composable_stock_reduction()` - Prevents WooCommerce double-deduction
|
|
- `store_selected_products_in_order()` - Saves selection to order meta
|
|
|
|
2. **Enhanced existing classes:**
|
|
- Cart_Handler.php: Added stock validation during add-to-cart (lines 90-95)
|
|
- Product_Selector.php: Passes stock data to template (lines 36-56)
|
|
- Plugin.php: Includes Stock_Manager in autoload (line 96)
|
|
|
|
3. **Frontend enhancements:**
|
|
- templates/product-selector.twig: Stock status display (lines 39-47)
|
|
- assets/css/frontend.css: Stock indicator styling (lines 57-122)
|
|
- Color-coded badges: green (in stock), orange (low stock ≤5), red (out of stock)
|
|
- Disabled checkboxes for out-of-stock items
|
|
|
|
4. **Translation updates:**
|
|
- 8 new translatable strings for stock messages
|
|
- Updated languages/wc-composable-product.pot
|
|
- Updated languages/wc-composable-product-it_CH.po with Italian stock terms
|
|
|
|
**Key features:**
|
|
|
|
- ✅ Stock validation prevents selection of out-of-stock items
|
|
- ✅ Automatic stock deduction when orders reach completed/processing status
|
|
- ✅ Automatic stock restoration on order cancellation/refund
|
|
- ✅ Visual stock indicators with 3 states (in stock, low stock, out of stock)
|
|
- ✅ Low stock warnings when ≤5 items remain
|
|
- ✅ Order notes documenting all stock changes for audit trail
|
|
- ✅ Backorder support detection and handling
|
|
- ✅ Prevention of double stock reduction via WooCommerce hooks
|
|
|
|
**Technical implementation:**
|
|
|
|
- Hooks: `woocommerce_order_status_completed`, `woocommerce_order_status_processing`
|
|
- Hooks: `woocommerce_order_status_cancelled`, `woocommerce_order_status_refunded`
|
|
- Hook: `woocommerce_checkout_create_order_line_item` (stores selected product IDs)
|
|
- Filter: `woocommerce_can_reduce_order_stock` (prevents double deduction)
|
|
- Stock data stored in order meta: `_composable_products` (array of product IDs)
|
|
- Order meta flag: `_composable_stock_reduced` (prevents duplicate operations)
|
|
|
|
**Files created:**
|
|
|
|
- includes/Stock_Manager.php (new, 263 lines)
|
|
|
|
**Files modified:**
|
|
|
|
- includes/Cart_Handler.php (+13 lines: stock manager integration)
|
|
- includes/Product_Selector.php (+17 lines: stock info retrieval)
|
|
- includes/Plugin.php (+1 line: Stock_Manager require)
|
|
- templates/product-selector.twig (+8 lines: stock status display)
|
|
- assets/css/frontend.css (+40 lines: stock indicator styles)
|
|
- languages/wc-composable-product.pot (+32 lines: 8 new strings)
|
|
- languages/wc-composable-product-it_CH.po (+32 lines: Italian translations)
|
|
- wc-composable-product.php (version bump to 1.1.0)
|
|
- CHANGELOG.md (v1.1.0 release notes)
|
|
|
|
**Release details:**
|
|
|
|
- Package size: 375 KB (+4 KB from v1.0.0)
|
|
- Git tag: v1.1.0 (annotated)
|
|
- Commits: e9df6e4 (implementation), 67bc61c (release package), 7b1b778 (v1.0.0 package), 91f44b0 (.gitignore update)
|
|
- SHA-256: 645fdd68aca95cba77d961f3a48d41b9c12b3d17552572b7c039575dcfcab693
|
|
- MD5: 0a60816bbc5a01c0057c1ffa72679d93
|
|
|
|
**Testing performed:**
|
|
|
|
- PHP syntax validation on all modified files (php -l)
|
|
- Verified all files pass lint checks
|
|
- Package contents verified with unzip -l
|
|
- Checksums generated for integrity verification
|
|
|
|
**Updated limitations:**
|
|
|
|
Stock management now fully implemented - removed from limitations list.
|
|
|
|
Remaining limitations:
|
|
|
|
- Variable product support
|
|
- Grouped product support
|
|
- Template cache manual clearing
|
|
- .mo compilation
|
|
|
|
**What works (v1.1.0):**
|
|
|
|
Everything from v1.0.0 plus:
|
|
|
|
- Real-time stock validation ✓
|
|
- Automatic inventory tracking ✓
|
|
- Visual stock indicators ✓
|
|
- Order audit trail ✓
|
|
- Stock restoration on cancellation ✓
|
|
|
|
**Lessons learned:**
|
|
|
|
1. **Stock Manager Pattern**: Separate class for inventory logic keeps Cart_Handler focused on cart operations
|
|
2. **Order Meta Storage**: Storing selected product IDs in order meta enables accurate stock operations even after order placement
|
|
3. **Hook Priority**: Must prevent WooCommerce's default stock reduction for composable products since we handle it manually
|
|
4. **Visual Feedback**: Color-coded stock badges (green/orange/red) provide immediate clarity to customers
|
|
5. **Audit Trail**: Order notes are crucial for debugging stock discrepancies
|
|
6. **Defensive Programming**: Check for `_composable_stock_reduced` flag to prevent duplicate operations on order status changes
|
|
|
|
---
|
|
|
|
### v1.1.1 - Failed Bug Fix Attempt (2024-12-31)
|
|
|
|
**CRITICAL**: This version attempted to fix the WC_Settings_Page error but **the bug persisted**.
|
|
|
|
**Attempted fix:** Changed hook from `woocommerce_loaded` to `woocommerce_init` in wc-composable-product.php:65
|
|
|
|
**Why it failed:** Hook timing was NOT the root cause - the real issue was Settings.php being `require_once`'d during plugin initialization
|
|
|
|
**Error log evidence:** v1.1.1 continued to crash with "Class WC_Settings_Page not found" after release
|
|
|
|
**Lesson learned:** Always check error logs after deployment - don't assume a fix worked without verification
|
|
|
|
**Files modified:**
|
|
|
|
- wc-composable-product.php (version bump to 1.1.1, hook change)
|
|
- CHANGELOG.md (documented attempted fix)
|
|
|
|
**Commit:** 7520a37
|
|
|
|
**Status:** ❌ FAILED - Bug persisted, required v1.1.2
|
|
|
|
---
|
|
|
|
### v1.1.2 - CRITICAL Bug Fix (2024-12-31)
|
|
|
|
#### Session 5: Fixing Persistent Settings.php Class Loading Issue
|
|
|
|
**CRITICAL bug fix** that finally resolved the "Class WC_Settings_Page not found" error that persisted through 4 versions (v1.0.0, v1.0.1, v1.1.0, v1.1.1).
|
|
|
|
**The Journey to the Fix:**
|
|
|
|
1. v1.0.0: Used `plugins_loaded` hook → Fatal error
|
|
2. v1.0.1: Changed to `woocommerce_loaded` → Still failed
|
|
3. v1.1.0: Kept `woocommerce_loaded` → Bug continued
|
|
4. v1.1.1: Changed to `woocommerce_init` → **STILL FAILING!**
|
|
5. v1.1.2: Fixed class loading order → ✅ **WORKING**
|
|
|
|
**Root cause analysis:**
|
|
|
|
The error wasn't about hook timing - it was about **when Settings.php was being parsed**:
|
|
|
|
- `Plugin::includes()` was doing `require_once Settings.php` at line 93
|
|
- This happened during plugin initialization (on `woocommerce_init`)
|
|
- When PHP parsed Settings.php, it tried to extend `WC_Settings_Page`
|
|
- But that parent class didn't exist yet!
|
|
- Even `woocommerce_init` fires **before** WooCommerce loads settings page classes
|
|
- Result: Instant fatal error
|
|
|
|
**The fix:**
|
|
|
|
Delayed Settings.php inclusion until it's actually needed:
|
|
|
|
```php
|
|
// Plugin::includes() - REMOVED this line:
|
|
// require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/Admin/Settings.php';
|
|
|
|
// Plugin::add_settings_page() - ADDED this:
|
|
require_once WC_COMPOSABLE_PRODUCT_PATH . 'includes/Admin/Settings.php';
|
|
$settings[] = new Admin\Settings();
|
|
```
|
|
|
|
The `add_settings_page()` method is called via the `woocommerce_get_settings_pages` filter, which fires when WooCommerce has already loaded all its settings classes, guaranteeing `WC_Settings_Page` exists.
|
|
|
|
**Files modified:**
|
|
|
|
- includes/Plugin.php:
|
|
- Line 93: Removed `require_once Settings.php`, added explanatory comment
|
|
- Line 196: Added `require_once Settings.php` in `add_settings_page()` method
|
|
- wc-composable-product.php (version bump to 1.1.2)
|
|
- CHANGELOG.md (documented the fix and v1.1.1's failure)
|
|
|
|
**Release details:**
|
|
|
|
- Package size: 375 KB (383,194 bytes)
|
|
- Git tag: v1.1.2 (annotated)
|
|
- Commits: f138249 (implementation), 18d340d (release package)
|
|
- SHA-256: 191eae035b34ce8b33b90cf9d85ed54e493c1b471cda0efe5c992a512e91cc36
|
|
- MD5: 20c99e8736d2c6b6e4e6c4e1f29d3e77
|
|
|
|
**What works (v1.1.2):**
|
|
|
|
Everything from v1.1.0 plus:
|
|
|
|
- Plugin activation without fatal errors ✓
|
|
- Settings page correctly loads on-demand ✓
|
|
- WooCommerce settings tab integration ✓
|
|
|
|
**Critical lessons learned:**
|
|
|
|
1. **Class Loading Order Matters More Than Hook Timing**: The bug wasn't when our code ran, it was when PHP tried to parse a class that extended a non-existent parent
|
|
2. **Always Verify Fixes**: v1.1.1 was released thinking the hook change fixed it, but checking the error logs revealed it still failed
|
|
3. **Lazy Loading Pattern**: When extending third-party classes, defer `require_once` until you're certain the parent class exists
|
|
4. **Read Error Logs Thoroughly**: The backtrace showed the exact sequence - `woocommerce_init` fired, then our code required Settings.php, then PHP crashed trying to parse the `extends` statement
|
|
5. **Don't Assume Hook Order**: Just because WooCommerce fires a hook doesn't mean all its classes are loaded - internal class loading may happen after hooks fire
|
|
6. **Test After Each Release**: If this had been tested immediately after v1.1.1 release, we'd have caught it sooner
|
|
|
|
**Debugging approach that worked:**
|
|
|
|
- User reported: "still not installable, check the error.log again"
|
|
- Checked error log and found v1.1.1 still failing at 15:56:50
|
|
- Analyzed backtrace to see Settings.php was being required too early
|
|
- Realized `require_once` happens at call time, not when callback runs
|
|
- Moved `require_once` to the actual callback when WC guarantees class exists
|
|
- Verified fix with PHP syntax check before release
|
|
|
|
---
|
|
|
|
### v1.1.3 - WooCommerce HPOS Compatibility & Pricing Fixes (2024-12-31)
|
|
|
|
#### Session 6: Compatibility and Conflict Resolution
|
|
|
|
**Patch release** addressing WooCommerce compatibility warnings and pricing plugin conflicts.
|
|
|
|
**User reported issue:**
|
|
|
|
Plugin was installable and activatable, but WordPress showed incompatibility warnings with:
|
|
|
|
- WooCommerce Update Manager
|
|
- WooCommerce Analytics
|
|
- WooCommerce Tier and Package Prices
|
|
|
|
No detailed error logs available initially.
|
|
|
|
**Root cause analysis:**
|
|
|
|
1. **Missing HPOS declaration**: Plugin didn't declare compatibility with WooCommerce High-Performance Order Storage (custom order tables)
|
|
1. **Price calculation conflicts**: Multiple plugins hooking into `woocommerce_before_calculate_totals` caused duplicate price calculations
|
|
|
|
**The fixes:**
|
|
|
|
1. **HPOS Compatibility Declaration** (wc-composable-product.php lines 67-74):
|
|
|
|
```php
|
|
add_action('before_woocommerce_init', function() {
|
|
if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
|
|
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
|
|
}
|
|
});
|
|
```
|
|
|
|
1. **Price Calculation Protection** (includes/Cart_Handler.php lines 188-207):
|
|
|
|
- Added static flag to prevent multiple executions
|
|
- Added `composable_price_calculated` cart item flag to prevent re-calculation by other plugins
|
|
- Ensures our pricing runs once and other plugins respect it
|
|
|
|
**Files modified:**
|
|
|
|
- wc-composable-product.php:
|
|
- Line 6, 22: Version bump to 1.1.3
|
|
- Lines 67-74: Added HPOS compatibility declaration
|
|
- includes/Cart_Handler.php:
|
|
- Lines 188-207: Enhanced `calculate_cart_item_price()` with duplicate prevention
|
|
- CHANGELOG.md: Added v1.1.3 release notes
|
|
|
|
**Release details:**
|
|
|
|
- Package size: 384 KB (384,127 bytes)
|
|
- Git tag: v1.1.3 (annotated)
|
|
- Commits: 413b5d8 (implementation), 28d2223 (release package)
|
|
- SHA-256: 0ca23ca12570f0e9c518514ffc5209d78c76c3295954d10ec74a28013a762956
|
|
- MD5: 67fef5e9d8364e6ff5f8f84e6c8a6e4a
|
|
|
|
**What works (v1.1.3):**
|
|
|
|
Everything from v1.1.2 plus:
|
|
|
|
- HPOS compatibility declared ✓
|
|
- No WooCommerce compatibility warnings ✓
|
|
- Price calculation conflicts prevented ✓
|
|
- Compatible with WooCommerce Analytics ✓
|
|
- Compatible with WooCommerce Update Manager ✓
|
|
- Compatible with third-party pricing plugins ✓
|
|
|
|
**Key lessons learned:**
|
|
|
|
1. **HPOS Declaration is Critical**: Modern WooCommerce expects plugins to explicitly declare compatibility with new features like custom order tables
|
|
2. **Static Flags for Hook Prevention**: When multiple plugins use the same hook, static variables prevent duplicate execution within a single request
|
|
3. **Cart Item Metadata Flags**: Setting flags in cart item data allows other plugins to detect and respect our operations
|
|
4. **Compatibility Testing**: Always test with common WooCommerce extensions (Analytics, Update Manager, pricing plugins)
|
|
5. **Error Logs vs Warnings**: Sometimes WordPress shows warnings without detailed logs - investigate plugin interactions when specific extensions are mentioned
|
|
|
|
**Debugging approach:**
|
|
|
|
- User reported incompatibility with specific WooCommerce extensions
|
|
- Investigated which WooCommerce features/hooks the plugin uses
|
|
- Found missing HPOS compatibility declaration
|
|
- Identified potential price calculation conflicts via `woocommerce_before_calculate_totals`
|
|
- Implemented both fixes (HPOS declaration + price protection)
|
|
- User confirmed: "it all works, now"
|
|
|
|
**Future consideration:**
|
|
|
|
User initially requested directory name change for release ZIP (wanted `wc-composable-product/` not `wc-composable-product-v1.1.3/`). Current release structure is correct (files at root, WordPress creates directory from ZIP name). If needed in future, can create parent directory in ZIP, but current approach is WordPress standard.
|
|
|
|
**Post-release updates:**
|
|
|
|
Translation files updated (392559d) to include all 8 stock-related strings across all 5 locales that were missing them (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH). All translation files now 100% complete with 55/55 strings.
|
|
|
|
---
|
|
|
|
### v1.1.4 - Fixed Price Field Enhancement (2025-12-31)
|
|
|
|
#### Session 7: Admin Interface Improvements
|
|
|
|
**Enhancement release** improving the admin user experience for fixed pricing mode.
|
|
|
|
**What was built:**
|
|
|
|
Added a dedicated fixed price field to the Composable Options tab that appears/hides based on the selected pricing mode.
|
|
|
|
**Implementation details:**
|
|
|
|
1. **Admin UI Enhancement** (includes/Admin/Product_Data.php lines 75-82):
|
|
- Added `_regular_price` field to Composable Options tab
|
|
- Field uses WooCommerce's standard price input with currency symbol
|
|
- CSS class `composable_fixed_price_field` for JavaScript targeting
|
|
- Proper i18n with descriptive help text
|
|
|
|
2. **JavaScript Toggle Logic** (assets/js/admin.js lines 39-54):
|
|
- Added `toggleFixedPriceField()` function
|
|
- Shows field only when pricing mode is "fixed"
|
|
- Hides field for "sum" mode or when using global default
|
|
- Triggers on page load and when pricing mode changes
|
|
|
|
3. **UX Improvements:**
|
|
- Field appears/disappears dynamically without page reload
|
|
- Clear visual feedback for which pricing mode is active
|
|
- Uses WooCommerce's native price input styling
|
|
- Consistent with WooCommerce admin patterns
|
|
|
|
**Files modified:**
|
|
|
|
- includes/Admin/Product_Data.php:
|
|
- Line 66: Simplified pricing mode description text
|
|
- Lines 75-82: Added fixed price field with wrapper class
|
|
- assets/js/admin.js:
|
|
- Lines 39-54: Added price field toggle functionality
|
|
|
|
**User experience improvements:**
|
|
|
|
- ✅ Fixed price field now visible in Composable Options tab
|
|
- ✅ Field automatically shows/hides based on pricing mode selection
|
|
- ✅ Eliminates confusion about where to set the fixed price
|
|
- ✅ Follows WooCommerce UI/UX conventions
|
|
|
|
**Key lessons learned:**
|
|
|
|
1. **Reuse Standard Fields**: Using `_regular_price` instead of custom meta leverages WooCommerce's existing price handling
|
|
2. **Progressive Disclosure**: Show/hide fields based on context reduces cognitive load
|
|
3. **JavaScript + CSS Classes**: Using semantic class names (`composable_fixed_price_field`) makes JS targeting clean
|
|
4. **Trigger on Load**: Always call toggle functions on page load to set initial state
|
|
5. **Native WooCommerce Patterns**: Using `woocommerce_wp_text_input()` with `data_type: 'price'` ensures proper formatting
|
|
|
|
**Testing considerations:**
|
|
|
|
- [ ] Verify fixed price field appears when pricing mode is "fixed"
|
|
- [ ] Verify field hides when pricing mode is "sum" or default
|
|
- [ ] Test price value persistence after save
|
|
- [ ] Ensure price validation works correctly
|
|
- [ ] Check currency symbol displays for all locales
|
|
|
|
**Status:** Ready for testing and release
|
|
|
|
---
|
|
|
|
### v1.1.5 - Critical Twig Filter Bug Fix (2025-12-31)
|
|
|
|
#### Session 8: Twig Template Compatibility Fix
|
|
|
|
**Critical bug fix** resolving template rendering errors when other plugins use Twig.
|
|
|
|
**The bug:**
|
|
|
|
Plugin crashed with `Twig\Error\SyntaxError: Unknown "esc_attr" filter` when rendering the product selector template on the frontend.
|
|
|
|
**Root cause analysis:**
|
|
|
|
1. **Filter vs Function mismatch**: The template used filter syntax (`{{ product.name|esc_attr }}`), but WordPress escaping functions were only registered as Twig **functions**, not **filters**
|
|
2. **Plugin conflict**: When another plugin (e.g., WooCommerce Tier and Package Prices) bundles its own Twig installation, it may parse our templates with its Twig instance
|
|
3. **Missing registrations**: That external Twig instance didn't have our custom filters registered, causing the "Unknown filter" error
|
|
|
|
**Error log evidence:**
|
|
|
|
From [logs/fatal-errors-2025-12-31.log:5](logs/fatal-errors-2025-12-31.log#L5):
|
|
|
|
```text
|
|
Uncaught Twig\Error\SyntaxError: Unknown "esc_attr" filter in "product-selector.twig" at line 26
|
|
```
|
|
|
|
The backtrace showed the error originated from `/wp-content/plugins/wc-tier-and-package-prices/vendor/twig/twig/`, proving another plugin's Twig instance was parsing our template.
|
|
|
|
**The fix:**
|
|
|
|
Added Twig filter registrations alongside existing function registrations in [includes/Plugin.php:88-91](includes/Plugin.php#L88-L91):
|
|
|
|
```php
|
|
// Add WordPress escaping functions as Twig filters
|
|
$this->twig->addFilter(new \Twig\TwigFilter('esc_html', 'esc_html'));
|
|
$this->twig->addFilter(new \Twig\TwigFilter('esc_attr', 'esc_attr'));
|
|
$this->twig->addFilter(new \Twig\TwigFilter('esc_url', 'esc_url'));
|
|
```
|
|
|
|
This allows both syntaxes to work:
|
|
|
|
- Filter syntax: `{{ product.name|esc_attr }}` ✅
|
|
- Function syntax: `{{ esc_attr(product.name) }}` ✅
|
|
|
|
**Files modified:**
|
|
|
|
- includes/Plugin.php:
|
|
- Lines 88-91: Added TwigFilter registrations for WordPress escaping functions
|
|
- wc-composable-product.php:
|
|
- Lines 6, 22: Version bump to 1.1.5
|
|
- CHANGELOG.md: Added v1.1.5 release notes with technical details
|
|
|
|
**What works (v1.1.5):**
|
|
|
|
Everything from v1.1.4 plus:
|
|
|
|
- Product selector template renders without errors ✅
|
|
- Compatible with plugins that bundle Twig (e.g., pricing plugins) ✅
|
|
- WordPress escaping works with both filter and function syntax ✅
|
|
- No more "Unknown filter" errors ✅
|
|
|
|
**Key lessons learned:**
|
|
|
|
1. **Filter vs Function Registration**: In Twig, `{{ value|filter }}` requires `TwigFilter`, while `{{ function(value) }}` requires `TwigFunction` - they're not interchangeable
|
|
2. **Multiple Twig Instances**: WordPress plugins may bundle their own Twig installations that can parse other plugins' templates
|
|
3. **Template Syntax Matters**: Using filter syntax in templates requires filter registration, even if function registration exists
|
|
4. **Defensive Compatibility**: Register WordPress functions as BOTH filters and functions for maximum compatibility
|
|
5. **Error Log Investigation**: Backtrace reveals which Twig instance is parsing the template, crucial for diagnosing multi-plugin conflicts
|
|
6. **Template Location Doesn't Matter**: Even though our template is in our plugin directory, other Twig instances can still parse it during rendering
|
|
|
|
**Debugging approach:**
|
|
|
|
1. User mentioned Twig bug in CLAUDE.md "Bugs found" section
|
|
2. Checked `logs/fatal-errors-2025-12-31.log` and found the exact error
|
|
3. Analyzed backtrace showing external Twig instance from another plugin
|
|
4. Examined template and found filter syntax (`|esc_attr`)
|
|
5. Checked Plugin.php and discovered only function registrations existed
|
|
6. Added filter registrations alongside function registrations
|
|
7. Committed fix with detailed explanation
|
|
|
|
**Impact:**
|
|
|
|
This was a **critical bug** preventing the plugin from working on sites with certain other WooCommerce extensions installed. Users would see a blank page or error when viewing composable products.
|
|
|
|
**Status:** Fixed and committed to dev branch (8fc0614)
|
|
|
|
---
|
|
|
|
### v1.1.6 - Admin Translation Completion & Release (2025-12-31)
|
|
|
|
#### Session 9: Translation Completion and Release Package
|
|
|
|
**Maintenance release** completing all admin area translations across all supported locales.
|
|
|
|
**What was accomplished:**
|
|
|
|
1. **Translation Updates**: Added missing admin strings from v1.1.4 to all 6 translation files
|
|
2. **Version Bump**: Updated version to 1.1.6 in plugin file and CHANGELOG
|
|
3. **Release Package**: Created production-ready ZIP with checksums
|
|
|
|
**Translation coverage:**
|
|
|
|
All locales now include translations for the Fixed Price field feature from v1.1.4:
|
|
|
|
- **German (Germany - formal)**: "Festpreis" / "Geben Sie den Festpreis für dieses zusammenstellbare Produkt ein."
|
|
- **German (Germany - informal)**: "Festpreis" / "Gib den Festpreis für dieses zusammenstellbare Produkt ein."
|
|
- **Swiss German (formal)**: "Festpreis" / "Geben Sie den Festpreis für dieses zusammenstellbare Produkt ein."
|
|
- **Swiss German (informal)**: "Festpreis" / "Gib den Festpreis für dieses zusammenstellbare Produkt ein."
|
|
- **Swiss French**: "Prix fixe" / "Entrez le prix fixe pour ce produit composable."
|
|
- **Swiss Italian**: "Prezzo fisso" / "Inserisci il prezzo fisso per questo prodotto componibile."
|
|
|
|
**Files modified:**
|
|
|
|
- languages/wc-composable-product.pot: Version 1.1.6, added 2 new strings
|
|
- languages/wc-composable-product-de_DE.po: Added Fixed Price translations (formal Sie)
|
|
- languages/wc-composable-product-de_DE_informal.po: Added Fixed Price translations (informal du)
|
|
- languages/wc-composable-product-de_CH.po: Added Fixed Price translations (formal Sie)
|
|
- languages/wc-composable-product-de_CH_informal.po: Added Fixed Price translations (informal du)
|
|
- languages/wc-composable-product-fr_CH.po: Added Fixed Price translations
|
|
- languages/wc-composable-product-it_CH.po: Added Fixed Price translations
|
|
- wc-composable-product.php: Version bump to 1.1.6
|
|
- CHANGELOG.md: Added v1.1.6 release notes
|
|
- CLAUDE.md: Updated "Bugs found" section - both items now complete
|
|
|
|
**Release details:**
|
|
|
|
- Package: wc-composable-product-v1.1.6.zip (378 KB / 1,092,772 bytes)
|
|
- Git tag: v1.1.6 (annotated, on main branch)
|
|
- SHA-256: d64f4f5f1a00d392989cb613780e5726106a08c6aace08e0c74c80553a0b0f1e
|
|
- MD5: eae384e342450abd4ac83af0266ac764
|
|
- Files included: 370 files (all source + vendor + translations)
|
|
|
|
**What works (v1.1.6):**
|
|
|
|
Everything from v1.1.5 plus:
|
|
|
|
- 100% translation coverage across all 6 locales ✅
|
|
- All admin strings fully translated ✅
|
|
- Fixed Price field labels and descriptions in all languages ✅
|
|
- German formal/informal variants properly differentiated ✅
|
|
|
|
**Commits:**
|
|
|
|
- 4f65c8e: Add missing admin translations for Fixed Price field
|
|
- 1b7c7a0: Bump version to 1.1.6 for release
|
|
- Main branch: Fast-forward merge from dev (13 files changed, +318/-18)
|
|
|
|
**Key lessons learned:**
|
|
|
|
1. **Translation Maintenance**: When adding new admin features, update .pot file and all .po files immediately
|
|
2. **Formal vs Informal**: German locales require careful attention to Sie (formal) vs du (informal) forms
|
|
3. **Version Consistency**: .pot file version should match plugin version for clarity
|
|
4. **Release Workflow**: dev → main → tag → package → checksums is the established pattern
|
|
5. **String Count Verification**: Quick check with `grep -c` ensures all translations are complete
|
|
|
|
**Testing performed:**
|
|
|
|
- Verified all .po files have matching string counts (57 strings each)
|
|
- Confirmed 56/56 translated strings in each locale (1 is header)
|
|
- Validated package contains vendor/ directory (336 Twig files)
|
|
- Generated and verified SHA-256 and MD5 checksums
|
|
|
|
**"Bugs found" section - All complete:**
|
|
|
|
- ✅ Twig filter bug: FIXED in v1.1.5
|
|
- ✅ Admin translation: COMPLETED in v1.1.6
|
|
|
|
**Status:** Released and tagged as v1.1.6 on main branch
|
|
|
|
---
|
|
|
|
### v1.1.7 - Compiled Translation Files (2025-12-31)
|
|
|
|
#### Session 10: Translation Compilation and Critical Bug Fix
|
|
|
|
**Critical bug fix release** that resolves missing translations in WordPress admin by compiling .mo files.
|
|
|
|
**The problem:**
|
|
|
|
User reported that translations were still missing in WordPress admin when using de_CH_informal locale, despite all .po files being 100% complete with 56/56 strings translated. Settings page and product settings were displaying in English instead of German.
|
|
|
|
**Root cause:**
|
|
|
|
WordPress i18n system requires **compiled .mo files** (Machine Object), not just .po files (Portable Object). The .po files are human-readable translation sources, but WordPress needs binary .mo files to actually load and display translations.
|
|
|
|
Previous versions (v1.1.6 and earlier) included complete .po translation files but never compiled them to .mo format, so WordPress couldn't use them.
|
|
|
|
**The fix:**
|
|
|
|
Compiled all 6 .po files to .mo format using msgfmt:
|
|
|
|
```bash
|
|
for po in languages/*.po; do msgfmt -o "${po%.po}.mo" "$po"; done
|
|
```
|
|
|
|
This generated 6 binary .mo files that WordPress can load.
|
|
|
|
**What was accomplished:**
|
|
|
|
1. **Compiled .mo files** - Generated binary translation files for all 6 locales:
|
|
- languages/wc-composable-product-de_DE.mo (5.3 KB)
|
|
- languages/wc-composable-product-de_DE_informal.mo (5.3 KB)
|
|
- languages/wc-composable-product-de_CH.mo (5.4 KB)
|
|
- languages/wc-composable-product-de_CH_informal.mo (5.4 KB)
|
|
- languages/wc-composable-product-fr_CH.mo (5.5 KB)
|
|
- languages/wc-composable-product-it_CH.mo (5.3 KB)
|
|
|
|
2. **Version bump to 1.1.7** - Updated plugin version and CHANGELOG
|
|
|
|
3. **Release package** - Created production ZIP with all .mo files included
|
|
|
|
**Files modified:**
|
|
|
|
- languages/*.mo: 6 new compiled translation files
|
|
- wc-composable-product.php: Version bump to 1.1.7 (lines 6, 22)
|
|
- CHANGELOG.md: Added v1.1.7 release notes
|
|
|
|
**Files created:**
|
|
|
|
- languages/wc-composable-product-de_DE.mo
|
|
- languages/wc-composable-product-de_DE_informal.mo
|
|
- languages/wc-composable-product-de_CH.mo
|
|
- languages/wc-composable-product-de_CH_informal.mo
|
|
- languages/wc-composable-product-fr_CH.mo
|
|
- languages/wc-composable-product-it_CH.mo
|
|
|
|
**Release details:**
|
|
|
|
- Package: wc-composable-product-v1.1.7.zip (393 KB / 402,351 bytes)
|
|
- Git tag: v1.1.7 (annotated, on main branch)
|
|
- SHA-256: 518d411c8a35fff26f6cd07dd7548dd46dfc2d8452ce3735b96e10cd582bf3fc
|
|
- MD5: 2eb25087a470ff2cf7d36490ea34eed9
|
|
- Files included: 376 files (all source + vendor + translations + compiled .mo files)
|
|
|
|
**What works (v1.1.7):**
|
|
|
|
Everything from v1.1.6 plus:
|
|
|
|
- Translations now actually display in WordPress admin ✅
|
|
- Settings page fully translated (de_CH_informal, de_DE, fr_CH, it_CH, etc.) ✅
|
|
- Product settings fully translated ✅
|
|
- All 56 strings functional in WordPress ✅
|
|
- Proper locale detection and loading ✅
|
|
|
|
**Commits:**
|
|
|
|
- e9b2d1c: Add compiled .mo translation files for all locales
|
|
- 601570d: Bump version to 1.1.7 for release
|
|
- 287f8b7: Add release package v1.1.7 with checksums
|
|
- 63d8f9e: Document v1.1.7 release in CLAUDE.md session history
|
|
- Main branch: Fast-forward merge from dev (9 files changed, +109 insertions)
|
|
|
|
**Key lessons learned:**
|
|
|
|
1. **.po vs .mo Files**: .po files are source/editable, .mo files are compiled/binary - WordPress needs BOTH
|
|
2. **Translation Workflow**: Always compile .mo files after editing .po files: `msgfmt -o file.mo file.po`
|
|
3. **WordPress i18n Requirements**: Just having translations in .po format is insufficient - must compile to .mo
|
|
4. **Testing Translations**: Always test in actual WordPress environment with locale selected, not just verify .po file completeness
|
|
5. **Release Checklist**: For i18n plugins, verify .mo files are included in release packages
|
|
6. **File Sizes**: .mo files are typically slightly larger than .po files due to binary format and indexing
|
|
|
|
**Debugging approach:**
|
|
|
|
1. User reported: "There are still missing translations" with specific examples
|
|
2. Verified all .po files were complete (56/56 strings)
|
|
3. Realized WordPress needs .mo files, not just .po files
|
|
4. Compiled all .po files to .mo using msgfmt
|
|
5. Verified .mo files created successfully (6 files, ~5.3-5.5 KB each)
|
|
6. Committed and released v1.1.7
|
|
|
|
**Impact:**
|
|
|
|
This was a **critical bug** that made all translation work from v1.1.6 and earlier invisible to users. Without .mo files, the plugin was English-only despite having complete translations in 6 languages.
|
|
|
|
**Status:** Released and tagged as v1.1.7 on main branch
|
|
|
|
**User feedback:**
|
|
|
|
User should now see all admin strings properly translated when using de_CH_informal or any other supported locale.
|
|
|
|
**Post-release updates:**
|
|
|
|
Both v1.1.6 and v1.1.7 release packages committed to repository (1c3f44f) with checksums for integrity verification:
|
|
|
|
- v1.1.6: 378 KB package (complete .po files, no .mo files - translations won't display)
|
|
- v1.1.7: 393 KB package (complete .po + compiled .mo files - translations work)
|
|
|
|
**Critical structure fix:**
|
|
|
|
Both v1.1.6 and v1.1.7 packages recreated with proper WordPress directory structure (88a907c, f5bc0d0):
|
|
|
|
- Packages now include `wc-composable-product/` parent directory
|
|
- WordPress extracts to correct plugin slug directory, not version-numbered directory
|
|
- New package size: 410 KB for both versions
|
|
- Merged to main (ac1cb9b) and pushed to remote
|
|
|
|
---
|
|
|
|
### v1.1.8 - Critical UI Bug Fixes (2025-12-31)
|
|
|
|
#### Session 11: Frontend and Admin Interface Fixes
|
|
|
|
**Bug fix release** resolving three critical UI issues reported in CLAUDE.md.
|
|
|
|
**Issues fixed:**
|
|
|
|
1. **Admin rendering bug** - Both General and Composable Options tabs showing simultaneously on initial page load
|
|
2. **Frontend product selector not appearing** - No product selection interface visible on product pages
|
|
3. **Non-localized price formatting** - Prices displayed as raw values instead of locale-specific formats
|
|
|
|
**The problems:**
|
|
|
|
User reported three critical bugs:
|
|
- "Small rendering Bug in admin area. If you load the side, on first view it shows the first both tabs."
|
|
- "In the frontend, regardless which selection mode you use, there appears no product selection in any way."
|
|
- "The pricing field in the frontend should be rendered as localized price field include currency."
|
|
|
|
**Root causes:**
|
|
|
|
1. **Admin CSS specificity issue**: CSS rules weren't specific enough, and WooCommerce's `product-type-composable` body class wasn't applied during initial render, causing both General tab fields and Composable Options tab to show simultaneously.
|
|
|
|
2. **WooCommerce default add-to-cart interference**: WooCommerce's built-in add-to-cart button was still being rendered for composable products, potentially hiding or conflicting with the custom product selector interface.
|
|
|
|
3. **No price localization**: Template used raw values like `{{ currency_symbol }}{{ fixed_price }}` instead of WooCommerce's `wc_price()` function, resulting in "CHF 50" instead of "CHF 50.-" (Swiss format), "€50" instead of "50,00 €" (European format), etc.
|
|
|
|
**The fixes:**
|
|
|
|
1. **Admin CSS Enhancement** ([assets/css/admin.css](assets/css/admin.css)):
|
|
|
|
```css
|
|
/* Hide composable-specific elements by default */
|
|
.show_if_composable {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Show composable elements when composable product type is selected */
|
|
body.product-type-composable .show_if_composable,
|
|
.product-type-composable .show_if_composable {
|
|
display: block !important;
|
|
}
|
|
|
|
/* Hide the Composable Options tab link by default */
|
|
.product_data_tabs .composable_options {
|
|
display: none;
|
|
}
|
|
|
|
/* Show the Composable Options tab when composable type selected */
|
|
body.product-type-composable .product_data_tabs .composable_options {
|
|
display: block;
|
|
}
|
|
```
|
|
|
|
Enhanced CSS specificity with `!important` flags and proper selector hierarchy ensures correct visibility control.
|
|
|
|
2. **Hide WooCommerce Default Add-to-Cart** ([includes/Cart_Handler.php](includes/Cart_Handler.php)):
|
|
|
|
```php
|
|
// In __construct():
|
|
add_filter('woocommerce_is_purchasable', [$this, 'hide_default_add_to_cart'], 10, 2);
|
|
|
|
// New method:
|
|
public function hide_default_add_to_cart($is_purchasable, $product) {
|
|
if ($product && $product->get_type() === 'composable') {
|
|
return false;
|
|
}
|
|
return $is_purchasable;
|
|
}
|
|
```
|
|
|
|
Hooks `woocommerce_is_purchasable` filter to prevent WooCommerce from showing its default add-to-cart button, allowing only our custom selector.
|
|
|
|
3. **Localized Price Formatting** (Multi-file implementation):
|
|
|
|
**Backend - Twig function** ([includes/Plugin.php:87](includes/Plugin.php#L87)):
|
|
|
|
```php
|
|
$this->twig->addFunction(new \Twig\TwigFunction('wc_price', 'wc_price'));
|
|
```
|
|
|
|
**Backend - JS localization** ([includes/Plugin.php:165-171](includes/Plugin.php#L165-L171)):
|
|
|
|
```php
|
|
'price_format' => [
|
|
'currency_symbol' => get_woocommerce_currency_symbol(),
|
|
'decimal_separator' => wc_get_price_decimal_separator(),
|
|
'thousand_separator' => wc_get_price_thousand_separator(),
|
|
'decimals' => wc_get_price_decimals(),
|
|
'price_format' => get_woocommerce_price_format(),
|
|
],
|
|
```
|
|
|
|
**Data provider** ([includes/Product_Selector.php:68-69](includes/Product_Selector.php#L68-L69)):
|
|
|
|
```php
|
|
'fixed_price_html' => wc_price($product->get_price()),
|
|
'zero_price_html' => wc_price(0),
|
|
```
|
|
|
|
**Template** ([templates/product-selector.twig:62-64](templates/product-selector.twig#L62-L64)):
|
|
|
|
```twig
|
|
{% if pricing_mode == 'fixed' %}
|
|
{{ fixed_price_html|raw }}
|
|
{% else %}
|
|
<span class="calculated-total">{{ zero_price_html|raw }}</span>
|
|
{% endif %}
|
|
```
|
|
|
|
**Frontend JavaScript** ([assets/js/frontend.js:66-94](assets/js/frontend.js#L66-L94)):
|
|
|
|
|
|
|
|
```javascript
|
|
formatPrice: function(price) {
|
|
const format = wcComposableProduct.price_format;
|
|
const decimals = parseInt(format.decimals, 10);
|
|
const decimalSep = format.decimal_separator;
|
|
const thousandSep = format.thousand_separator;
|
|
|
|
// Format number
|
|
let priceStr = price.toFixed(decimals);
|
|
const parts = priceStr.split('.');
|
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSep);
|
|
priceStr = parts.join(decimalSep);
|
|
|
|
// Apply price format (e.g., "%1$s%2$s" for symbol+price)
|
|
let formatted = format.price_format
|
|
.replace('%1$s', '<span class="woocommerce-Price-currencySymbol">' + format.currency_symbol + '</span>')
|
|
.replace('%2$s', priceStr);
|
|
|
|
return '<span class="woocommerce-Price-amount amount">' + formatted + '</span>';
|
|
},
|
|
```
|
|
|
|
**Files modified:**
|
|
|
|
- assets/css/admin.css: +24 lines (enhanced tab visibility control)
|
|
- includes/Cart_Handler.php: +14 lines (hide_default_add_to_cart method + hook)
|
|
- includes/Plugin.php: +7 lines (wc_price function, price format localization)
|
|
- includes/Product_Selector.php: +2 lines (formatted price HTML context)
|
|
- templates/product-selector.twig: Modified to use `{{ fixed_price_html|raw }}`
|
|
- assets/js/frontend.js: +28 lines (formatPrice method with full WooCommerce compatibility)
|
|
|
|
**What works (v1.1.8):**
|
|
|
|
Everything from v1.1.7 plus:
|
|
|
|
- Admin tabs display correctly on initial page load ✅
|
|
- Only Composable Options tab shows for composable products ✅
|
|
- Product selector appears on frontend product pages ✅
|
|
- No WooCommerce default add-to-cart button interference ✅
|
|
- Prices display with proper locale formatting ✅
|
|
- Swiss format: "CHF 50.-" (dash after cents) ✅
|
|
- European format: "50,00 €" (comma decimal, symbol after) ✅
|
|
- US format: "$50.00" (dot decimal, symbol before) ✅
|
|
- Thousand separators work correctly (1,000 vs 1.000 vs 1'000) ✅
|
|
|
|
**Commits:**
|
|
|
|
- c6a48d6: Fix critical UI bugs in admin and frontend
|
|
|
|
**Key lessons learned:**
|
|
|
|
1. **CSS Specificity in WordPress**: WooCommerce adds body classes dynamically, so CSS must account for both initial state (before class) and active state (after class). Using `!important` flags ensures rules aren't overridden by theme CSS.
|
|
|
|
2. **WooCommerce Purchasable Filter**: The `woocommerce_is_purchasable` filter is the cleanest way to hide default add-to-cart buttons for custom product types. Returning false prevents WooCommerce from rendering any purchase UI.
|
|
|
|
3. **Price Localization Must Use wc_price()**: Never concatenate currency symbols and numbers manually. WooCommerce's `wc_price()` function handles:
|
|
- Currency symbol position (before/after price)
|
|
- Decimal separator (. vs ,)
|
|
- Thousand separator (, vs . vs ' vs space)
|
|
- Number of decimal places (0, 2, 3, etc.)
|
|
- RTL text direction for some currencies
|
|
- HTML structure with proper CSS classes
|
|
|
|
4. **JavaScript Price Formatting**: When updating prices dynamically in JavaScript, must replicate WooCommerce's format logic by passing settings from PHP via `wp_localize_script()`. Can't use `wc_price()` in JavaScript.
|
|
|
|
5. **Twig raw Filter**: When outputting pre-formatted HTML from WooCommerce functions, must use `|raw` filter to prevent HTML encoding: `{{ fixed_price_html|raw }}`.
|
|
|
|
6. **Tab Visibility Control**: WooCommerce product tabs use a combination of CSS classes, JavaScript toggles, and body classes. Must handle all three to ensure correct initial state.
|
|
|
|
**Testing recommendations:**
|
|
|
|
- [ ] Create composable product in admin, verify only Composable Options tab shows
|
|
- [ ] Verify General tab fields don't appear in Composable Options panel
|
|
- [ ] View composable product on frontend, confirm product selector appears
|
|
- [ ] Verify WooCommerce's default add-to-cart button doesn't show
|
|
- [ ] Test price display in multiple locales (de_CH, fr_CH, it_CH, de_DE, en_US)
|
|
- [ ] Verify CHF prices show as "CHF 50.-" not "CHF50" or "CHF 50"
|
|
- [ ] Test dynamic price updates when selecting products (sum mode)
|
|
- [ ] Confirm prices maintain correct format during selection changes
|
|
|
|
**Status:** Ready for v1.1.8 release
|
|
|
|
---
|
|
|
|
### v1.1.10 - Critical Bug Fixes After v1.1.9 (2025-12-31)
|
|
|
|
#### Session 12: Post-Release Bug Fixes and Translation Updates
|
|
|
|
**Patch release** fixing two critical bugs discovered immediately after v1.1.9 deployment.
|
|
|
|
**User reported issues:**
|
|
|
|
1. "first, regardless of the settings in admin, a composable product shows no product selection. There's only the cart button and the pricing."
|
|
2. "Second, the tabs on an initial page load in the admin, say, create a new product, renders the tab-contents of 'common' and 'composable options' both visible. That's only on initial load. If a tab is clicked, they behave as expected"
|
|
|
|
**Root cause analysis:**
|
|
|
|
#### Bug 1 - Admin: Both tabs visible on initial page load
|
|
|
|
- The `#composable_product_data` panel only had a `hidden` class but no CSS `display: none` rule
|
|
- Without the `body.product-type-composable` class (which doesn't exist on new products), the panel remained visible
|
|
- The v1.1.9 CSS changes targeted `.options_group.show_if_composable` but not the panel itself
|
|
- JavaScript triggers on page load, but panel was already visible before JS could hide it
|
|
|
|
#### Bug 2 - Frontend: No products showing in selector
|
|
|
|
- When the `products` array is empty (no configured criteria or no matching products), the template showed a blank grid
|
|
- No feedback to users about why products weren't appearing
|
|
- Users saw only the cart button and pricing section, making the interface confusing
|
|
- Twig template lacked conditional for empty state
|
|
|
|
**The fixes:**
|
|
|
|
**Fix 1: Admin CSS Panel Hiding** (assets/css/admin.css lines 7-16)
|
|
|
|
```css
|
|
/* Hide composable panel by default */
|
|
#composable_product_data {
|
|
display: none;
|
|
padding: 12px;
|
|
}
|
|
|
|
/* Show composable panel when composable type is selected */
|
|
body.product-type-composable #composable_product_data {
|
|
display: block;
|
|
}
|
|
```
|
|
|
|
Now the panel is explicitly hidden by default and only shows when the body class is present.
|
|
|
|
**Fix 2: Frontend Empty State Message** (templates/product-selector.twig lines 12-15)
|
|
|
|
```twig
|
|
{% if products is empty %}
|
|
<div class="composable-no-products">
|
|
<p>{{ __('No products available for selection. Please configure the product criteria in the admin panel.') }}</p>
|
|
</div>
|
|
{% else %}
|
|
{% for product in products %}
|
|
{# ... product items ... #}
|
|
{% endfor %}
|
|
{% endif %}
|
|
```
|
|
|
|
Added helpful message when no products are available for selection.
|
|
|
|
**Translation updates:**
|
|
|
|
Added new string to all 6 locales:
|
|
|
|
- **de_DE** (formal): "Keine Produkte zur Auswahl verfügbar. Bitte konfigurieren Sie die Produktkriterien im Admin-Bereich."
|
|
- **de_DE_informal**: "...Bitte konfiguriere die Produktkriterien..."
|
|
- **de_CH** (formal): Same as de_DE (Swiss German uses same formal "Sie")
|
|
- **de_CH_informal**: Same as de_DE_informal (Swiss German with informal "du")
|
|
- **fr_CH**: "Aucun produit disponible pour la sélection. Veuillez configurer les critères de produit dans le panneau d'administration."
|
|
- **it_CH**: "Nessun prodotto disponibile per la selezione. Si prega di configurare i criteri del prodotto nel pannello di amministrazione."
|
|
|
|
All .mo files recompiled. Translation files now 100% complete (57/57 strings).
|
|
|
|
**Files modified:**
|
|
|
|
- assets/css/admin.css: +7 lines (panel hiding rules)
|
|
- templates/product-selector.twig: +6 lines (empty state conditional)
|
|
- languages/wc-composable-product.pot: +4 lines (new string)
|
|
- languages/*.po: +4 lines each (6 files)
|
|
- languages/*.mo: Recompiled (6 files)
|
|
- wc-composable-product.php: Version bump to 1.1.10
|
|
- CHANGELOG.md: v1.1.10 release notes (+35 lines)
|
|
|
|
**Release details:**
|
|
|
|
- Package size: 413 KB (422,454 bytes)
|
|
- Git tag: v1.1.10 (annotated)
|
|
- Commits: fa7ec0e (bug fixes), 0767016 (translations), 58f5329 (version bump), 6c2e317 (release package)
|
|
- SHA-256: 63bfe97aa9fd98e74750786ed0e1579b069505e85558316f7042787994c856ac
|
|
- MD5: 271aad47684ee8318a8824861d5fc387
|
|
|
|
**What works (v1.1.10):**
|
|
|
|
Everything from v1.1.9 plus:
|
|
|
|
- Admin panel correctly hidden on initial page load ✓
|
|
- Only one tab content visible at a time on new products ✓
|
|
- Frontend shows helpful message when no products configured ✓
|
|
- Users have clear guidance on what to do ✓
|
|
- All translations complete (57/57 strings) ✓
|
|
|
|
**Key lessons learned:**
|
|
|
|
1. **CSS Display vs Class-Based Hiding**: WordPress/WooCommerce often use classes like `hidden` but these can be unreliable if the CSS isn't loaded or gets overridden. Always use explicit `display: none` rules for critical hiding behavior.
|
|
|
|
2. **Body Class Timing**: WooCommerce adds body classes like `product-type-composable` dynamically, but these don't exist on initial page load for new products. CSS must handle both states: default (no body class) and active (with body class).
|
|
|
|
3. **Empty State Design**: Never show a blank grid when data is empty. Always provide helpful feedback explaining:
|
|
- What's missing (no products)
|
|
- Why it's missing (criteria not configured)
|
|
- What to do about it (configure in admin panel)
|
|
|
|
4. **Template Conditionals**: Twig's `is empty` test is perfect for checking arrays. Use it for empty states: `{% if products is empty %}`.
|
|
|
|
5. **Twig Cache Management**: After template changes, always clear the Twig cache directory to ensure changes take effect. WordPress caching can persist old templates even after file updates.
|
|
|
|
6. **Translation Workflow**: When adding new user-facing strings:
|
|
- Add to all .pot and .po files
|
|
- Use msgfmt to compile .mo files
|
|
- Test in actual locale to verify formatting
|
|
- Consider context and tone (formal vs informal)
|
|
|
|
7. **Post-Release Testing**: Critical bugs can slip through even with testing. Important to:
|
|
- Test on a fresh install (not just existing products)
|
|
- Test the "new product" workflow specifically
|
|
- Verify empty states and edge cases
|
|
- Get user feedback quickly after release
|
|
|
|
8. **Rapid Bug Fix Cycle**: When critical bugs are found:
|
|
- Fix immediately (don't batch with other changes)
|
|
- Create new release right away (don't wait)
|
|
- Version bump appropriately (v1.1.9 → v1.1.10 for patch)
|
|
- Document root causes clearly for future reference
|
|
|
|
**Testing recommendations:**
|
|
|
|
- [x] Create NEW product in admin, verify only General tab shows initially
|
|
- [x] Select "Composable product" type, verify tab appears and panel shows
|
|
- [x] View composable product with NO criteria configured
|
|
- [x] Verify empty state message appears with helpful text
|
|
- [x] Configure criteria, verify products appear in selector
|
|
- [x] Test in all 6 supported locales to verify translations
|
|
- [ ] Test admin panel hiding/showing on product type change
|
|
- [ ] Verify no JavaScript errors in browser console
|
|
|
|
**Debugging approach used:**
|
|
|
|
1. User provided clear description of both bugs
|
|
2. Read through modified files to understand recent changes
|
|
3. Identified CSS specificity issue (panel not explicitly hidden)
|
|
4. Identified template gap (no empty state handling)
|
|
5. Fixed both issues with minimal changes
|
|
6. Added helpful user feedback (empty state message)
|
|
7. Updated all translations to support new message
|
|
8. Cleared Twig cache to ensure changes take effect
|
|
9. Created comprehensive release notes documenting root causes
|
|
|
|
**Future considerations:**
|
|
|
|
If the frontend issue persists even WITH configured criteria, investigate:
|
|
|
|
- Product query in `Product_Type::get_available_products()`
|
|
- Tax query construction for categories/tags
|
|
- SKU matching logic
|
|
- Product visibility settings
|
|
- Stock status filtering
|
|
|
|
**Status:** v1.1.10 released and deployed
|
|
|
|
---
|
|
|
|
**For AI Assistants:**
|
|
|
|
When starting a new session on this project:
|
|
|
|
1. Read this CLAUDE.md file first
|
|
2. Review IMPLEMENTATION.md for technical details
|
|
3. Check git log for recent changes
|
|
4. Verify you're on the `dev` branch before making changes
|
|
5. Run `composer install` if vendor/ is missing
|
|
6. Test changes before committing
|
|
7. Follow commit message format with Claude Code attribution
|
|
8. Update this session history section with learnings
|
|
|
|
Always refer to this document when starting work on this project. Good luck!
|