You've already forked wc-tier-and-package-prices
Release version 1.2.9 - Translation and deletion fixes
## Bugfixes
1. **Price Header Not Translated**
- Fixed translation function placement in printf statements
- Changed from printf(__()) to printf(esc_html__())
- Headers now display in administrator's configured language
2. **Placeholder HTML Entity Encoding**
- Currency symbols were showing as HTML entities (e.g., €)
- Removed translation filter from concatenated placeholder strings
- Placeholders are example values that should not be translated
3. **Variation Pricing Still Not Deletable (Regression)**
- Despite v1.2.8 fix, edge cases remained due to conditional branching
- Refactored save logic to be more defensive
- Always initializes arrays, then unconditionally updates or deletes
- Guarantees proper cleanup regardless of POST data structure
## Technical Details
- Updated all 6 table headers: printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), ...)
- Removed |__() filter from Twig placeholder concatenations
- Refactored save_variation_pricing_fields() with simplified logic:
* Initialize arrays at start
* Populate only if valid POST data exists
* Always perform update (if !empty) or delete (if empty)
- Added is_array() check for extra safety
## Changed Files
- includes/class-wc-tpp-product-meta.php
- templates/admin/tier-row.twig
- templates/admin/package-row.twig
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
74
CHANGELOG.md
74
CHANGELOG.md
@@ -5,6 +5,80 @@ All notable changes to WooCommerce Tier and Package Prices will be documented in
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.2.9] - 2025-12-30
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Price Header Not Translated**: The "Price (%s)" header in admin tables was not being properly translated because the translation function was placed incorrectly within the printf statement. Changed from `printf(__('Price (%s)', ...), ...)` to `printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), ...)` to ensure proper translation while maintaining the currency placeholder functionality.
|
||||||
|
|
||||||
|
- **Placeholder HTML Entity Encoding Issue**: Currency symbols in price input placeholders were being displayed as HTML entities (e.g., "€" instead of "€") because the concatenated string was being passed through the translation filter which was encoding special characters. Removed the unnecessary translation filter from concatenated placeholder strings since they are example values that should not be translated.
|
||||||
|
|
||||||
|
- **Variation Pricing Still Not Deletable (Regression from v1.2.8)**: Despite the fix in v1.2.8, variation pricing data was still not being properly deleted in all scenarios. The issue was with the conditional logic structure - the code had separate `if/else` branches that could fail in edge cases. Restructured the save logic to be more defensive: initialize arrays at the start, populate only if valid POST data exists, then unconditionally perform either update (if not empty) or delete (if empty). This guarantees proper cleanup regardless of POST data structure.
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
|
||||||
|
**Translation Fix**:
|
||||||
|
|
||||||
|
- Changed all 6 instances of `printf(__('Price (%s)', 'wc-tier-package-prices'), ...)`
|
||||||
|
- To: `printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), ...)`
|
||||||
|
- The `__()` function now receives the text domain parameter correctly
|
||||||
|
- Added `esc_html` for proper output escaping
|
||||||
|
|
||||||
|
**Placeholder Encoding Fix**:
|
||||||
|
|
||||||
|
- Changed tier-row.twig placeholder from: `{{ ('e.g., 9.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }}`
|
||||||
|
- To: `{{ 'e.g., 9.99 ' ~ currency_symbol }}`
|
||||||
|
- Changed package-row.twig placeholder from: `{{ ('e.g., 99.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }}`
|
||||||
|
- To: `{{ 'e.g., 99.99 ' ~ currency_symbol }}`
|
||||||
|
- Removed translation filter from concatenated example values to prevent HTML entity encoding
|
||||||
|
|
||||||
|
**Variation Save Logic Refactor**:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Old pattern (v1.2.8):
|
||||||
|
if (isset($_POST['wc_tpp_tiers'][$loop])) {
|
||||||
|
$tiers = array();
|
||||||
|
// ... populate tiers ...
|
||||||
|
if (!empty($tiers)) {
|
||||||
|
update_post_meta(...);
|
||||||
|
} else {
|
||||||
|
delete_post_meta(...);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete_post_meta(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// New pattern (v1.2.9):
|
||||||
|
$tiers = array();
|
||||||
|
if (isset($_POST['wc_tpp_tiers'][$loop]) && is_array($_POST['wc_tpp_tiers'][$loop])) {
|
||||||
|
// ... populate tiers ...
|
||||||
|
}
|
||||||
|
// Always perform update or delete based on final state
|
||||||
|
if (!empty($tiers)) {
|
||||||
|
update_post_meta(...);
|
||||||
|
} else {
|
||||||
|
delete_post_meta(...);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Eliminated conditional branching that could miss edge cases
|
||||||
|
- Added explicit `is_array()` check for extra safety
|
||||||
|
- Guaranteed that one of update_post_meta() or delete_post_meta() is always called
|
||||||
|
- Applied to both `save_variation_pricing_fields()` for tier and package pricing
|
||||||
|
|
||||||
|
**User Impact**:
|
||||||
|
|
||||||
|
- Price headers now display in the administrator's configured language
|
||||||
|
- Currency symbols display correctly without HTML encoding in placeholders
|
||||||
|
- Variation pricing deletion now works reliably in all scenarios
|
||||||
|
- Database remains clean with no orphaned empty arrays
|
||||||
|
|
||||||
|
### Changed Files
|
||||||
|
|
||||||
|
- `includes/class-wc-tpp-product-meta.php` - Fixed translation function calls in 6 table headers; refactored save_variation_pricing_fields() logic for tiers and packages
|
||||||
|
- `templates/admin/tier-row.twig` - Removed translation filter from placeholder concatenation
|
||||||
|
- `templates/admin/package-row.twig` - Removed translation filter from placeholder concatenation
|
||||||
|
|
||||||
## [1.2.8] - 2025-12-30
|
## [1.2.8] - 2025-12-30
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
10
CLAUDE.md
10
CLAUDE.md
@@ -355,8 +355,8 @@ Update version in 3 places:
|
|||||||
**CRITICAL:** The zip command must be run from the **parent directory** of the plugin folder to create proper archive structure.
|
**CRITICAL:** The zip command must be run from the **parent directory** of the plugin folder to create proper archive structure.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# From parent directory (/home/magdev/workspaces/node)
|
# From parent directory (/home/magdev/workspaces/php)
|
||||||
cd /home/magdev/workspaces/node
|
cd /home/magdev/workspaces/php
|
||||||
|
|
||||||
# Create zip excluding dev files - note the correct path structure
|
# Create zip excluding dev files - note the correct path structure
|
||||||
zip -r wc-tier-and-package-prices/releases/wc-tier-and-package-prices-X.X.X.zip wc-tier-and-package-prices/ \
|
zip -r wc-tier-and-package-prices/releases/wc-tier-and-package-prices-X.X.X.zip wc-tier-and-package-prices/ \
|
||||||
@@ -787,6 +787,12 @@ Roadmap for the upcoming development.
|
|||||||
|
|
||||||
2. ~~Already stored tier and package prices on the children of a variable product are still available after deletion. Looks like the storage mechanism has an error. This occurs only on the child product, not on the parent product.~~ ✅ **FIXED in v1.2.8** - Fixed save logic in both `save_tier_package_fields()` and `save_variation_pricing_fields()` methods. Root cause: Empty arrays were being saved via `update_post_meta()` instead of being deleted. Changed logic from "save on isset, delete otherwise" to "filter entries, then save if not empty, delete if empty". Added `if (!empty($tiers))` and `if (!empty($packages))` checks before calling `update_post_meta()`. Now properly calls `delete_post_meta()` when all pricing entries are removed, preventing empty arrays from persisting in database.
|
2. ~~Already stored tier and package prices on the children of a variable product are still available after deletion. Looks like the storage mechanism has an error. This occurs only on the child product, not on the parent product.~~ ✅ **FIXED in v1.2.8** - Fixed save logic in both `save_tier_package_fields()` and `save_variation_pricing_fields()` methods. Root cause: Empty arrays were being saved via `update_post_meta()` instead of being deleted. Changed logic from "save on isset, delete otherwise" to "filter entries, then save if not empty, delete if empty". Added `if (!empty($tiers))` and `if (!empty($packages))` checks before calling `update_post_meta()`. Now properly calls `delete_post_meta()` when all pricing entries are removed, preventing empty arrays from persisting in database.
|
||||||
|
|
||||||
|
##### Bugs in v1.2.8
|
||||||
|
|
||||||
|
1. The Price header in admin tables while configuring tier and package prices is not translated. Also the placeholder on the form elements for prices has the wrong encoding, the special characters on the placeholder are show in html-entity encoding.
|
||||||
|
|
||||||
|
2. The tier and package prices for children of a variable product are still not deletable. After storing the product, the previously deleted rows are back again.
|
||||||
|
|
||||||
##### Planned Enhancements for v1.2.9+
|
##### Planned Enhancements for v1.2.9+
|
||||||
|
|
||||||
1. Create different, selectable templates for tierprices and packages to use in the frontend. Make the new templates selectable globally on the settings-page, not per product.
|
1. Create different, selectable templates for tierprices and packages to use in the frontend. Make the new templates selectable globally on the settings-page, not per product.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "magdev/wc-tier-package-prices",
|
"name": "magdev/wc-tier-package-prices",
|
||||||
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
|
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
|
||||||
"version": "1.2.8",
|
"version": "1.2.9",
|
||||||
"type": "wordpress-plugin",
|
"type": "wordpress-plugin",
|
||||||
"license": "GPL-2.0-or-later",
|
"license": "GPL-2.0-or-later",
|
||||||
"authors": [
|
"authors": [
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
||||||
<th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
<th><?php printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
||||||
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -85,7 +85,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
||||||
<th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
<th><?php printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
||||||
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -149,7 +149,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
||||||
<th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
<th><?php printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
||||||
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -195,7 +195,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
||||||
<th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
<th><?php printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
||||||
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -287,7 +287,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
||||||
<th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
<th><?php printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
||||||
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -310,7 +310,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
||||||
<th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
<th><?php printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
||||||
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -472,8 +472,8 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save tier pricing for this variation
|
// Save tier pricing for this variation
|
||||||
if (isset($_POST['wc_tpp_tiers'][$loop])) {
|
$tiers = array();
|
||||||
$tiers = array();
|
if (isset($_POST['wc_tpp_tiers'][$loop]) && is_array($_POST['wc_tpp_tiers'][$loop])) {
|
||||||
foreach ($_POST['wc_tpp_tiers'][$loop] as $tier) {
|
foreach ($_POST['wc_tpp_tiers'][$loop] as $tier) {
|
||||||
if (!empty($tier['min_qty']) && !empty($tier['price'])) {
|
if (!empty($tier['min_qty']) && !empty($tier['price'])) {
|
||||||
$tiers[] = array(
|
$tiers[] = array(
|
||||||
@@ -487,19 +487,17 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
usort($tiers, function($a, $b) {
|
usort($tiers, function($a, $b) {
|
||||||
return $a['min_qty'] - $b['min_qty'];
|
return $a['min_qty'] - $b['min_qty'];
|
||||||
});
|
});
|
||||||
// Only save if we have valid tiers, otherwise delete
|
}
|
||||||
if (!empty($tiers)) {
|
// Always update or delete based on whether we have valid tiers
|
||||||
update_post_meta($variation_id, '_wc_tpp_tiers', $tiers);
|
if (!empty($tiers)) {
|
||||||
} else {
|
update_post_meta($variation_id, '_wc_tpp_tiers', $tiers);
|
||||||
delete_post_meta($variation_id, '_wc_tpp_tiers');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
delete_post_meta($variation_id, '_wc_tpp_tiers');
|
delete_post_meta($variation_id, '_wc_tpp_tiers');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save package pricing for this variation
|
// Save package pricing for this variation
|
||||||
if (isset($_POST['wc_tpp_packages'][$loop])) {
|
$packages = array();
|
||||||
$packages = array();
|
if (isset($_POST['wc_tpp_packages'][$loop]) && is_array($_POST['wc_tpp_packages'][$loop])) {
|
||||||
foreach ($_POST['wc_tpp_packages'][$loop] as $package) {
|
foreach ($_POST['wc_tpp_packages'][$loop] as $package) {
|
||||||
if (!empty($package['qty']) && !empty($package['price'])) {
|
if (!empty($package['qty']) && !empty($package['price'])) {
|
||||||
$packages[] = array(
|
$packages[] = array(
|
||||||
@@ -513,12 +511,10 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
usort($packages, function($a, $b) {
|
usort($packages, function($a, $b) {
|
||||||
return $a['qty'] - $b['qty'];
|
return $a['qty'] - $b['qty'];
|
||||||
});
|
});
|
||||||
// Only save if we have valid packages, otherwise delete
|
}
|
||||||
if (!empty($packages)) {
|
// Always update or delete based on whether we have valid packages
|
||||||
update_post_meta($variation_id, '_wc_tpp_packages', $packages);
|
if (!empty($packages)) {
|
||||||
} else {
|
update_post_meta($variation_id, '_wc_tpp_packages', $packages);
|
||||||
delete_post_meta($variation_id, '_wc_tpp_packages');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
delete_post_meta($variation_id, '_wc_tpp_packages');
|
delete_post_meta($variation_id, '_wc_tpp_packages');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<input type="text"
|
<input type="text"
|
||||||
name="{{ name_prefix }}[{{ index|esc_attr }}][price]"
|
name="{{ name_prefix }}[{{ index|esc_attr }}][price]"
|
||||||
value="{{ package.price|default('')|esc_attr }}"
|
value="{{ package.price|default('')|esc_attr }}"
|
||||||
placeholder="{{ ('e.g., 99.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }}"
|
placeholder="{{ 'e.g., 99.99 ' ~ currency_symbol }}"
|
||||||
class="short wc_input_price">
|
class="short wc_input_price">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<input type="text"
|
<input type="text"
|
||||||
name="{{ name_prefix }}[{{ index|esc_attr }}][price]"
|
name="{{ name_prefix }}[{{ index|esc_attr }}][price]"
|
||||||
value="{{ tier.price|default('')|esc_attr }}"
|
value="{{ tier.price|default('')|esc_attr }}"
|
||||||
placeholder="{{ ('e.g., 9.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }}"
|
placeholder="{{ 'e.g., 9.99 ' ~ currency_symbol }}"
|
||||||
class="short wc_input_price">
|
class="short wc_input_price">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Plugin Name: WooCommerce Tier and Package Prices
|
* Plugin Name: WooCommerce Tier and Package Prices
|
||||||
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices
|
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices
|
||||||
* Description: Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices
|
* Description: Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices
|
||||||
* Version: 1.2.8
|
* Version: 1.2.9
|
||||||
* Author: Marco Graetsch
|
* Author: Marco Graetsch
|
||||||
* Author URI: https://src.bundespruefstelle.ch/magdev
|
* Author URI: https://src.bundespruefstelle.ch/magdev
|
||||||
* Text Domain: wc-tier-package-prices
|
* Text Domain: wc-tier-package-prices
|
||||||
@@ -23,7 +23,7 @@ if (!defined('ABSPATH')) {
|
|||||||
|
|
||||||
// Define plugin constants
|
// Define plugin constants
|
||||||
if (!defined('WC_TPP_VERSION')) {
|
if (!defined('WC_TPP_VERSION')) {
|
||||||
define('WC_TPP_VERSION', '1.2.8');
|
define('WC_TPP_VERSION', '1.2.9');
|
||||||
}
|
}
|
||||||
if (!defined('WC_TPP_PLUGIN_DIR')) {
|
if (!defined('WC_TPP_PLUGIN_DIR')) {
|
||||||
define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||||
|
|||||||
Reference in New Issue
Block a user