You've already forked wc-tier-and-package-prices
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74cc56005e | |||
| 136eed2bdd | |||
| 178f86f3e6 | |||
| 7286459ff2 | |||
| cbe758267e | |||
| 74c14581f1 | |||
| 0dbe18d954 | |||
| 63c8137f4e | |||
| b2efb89d59 | |||
| 0f5779dc56 | |||
| d71f2c01dc | |||
| 82c8008fed | |||
| 4871b7957d | |||
| f7508a3d9c | |||
| 23f81ce58c | |||
| 9e96ff3321 | |||
| cf11cb5bd1 | |||
| f26574aa4b | |||
| 348158050e | |||
| d7a61f29b4 | |||
| 10a1f94a31 | |||
| ae946683b3 | |||
| 71ea40598f | |||
| e5aca708cc | |||
| a73ff4926f | |||
| 78101baf88 | |||
| d99ece71e4 | |||
| e4747130e4 | |||
| e4ecc2c0be | |||
| e2e40538df | |||
| 67e11d3030 | |||
| 2de6a92784 | |||
| 47f2ed771b | |||
| 880983a879 | |||
| 937e80fce3 | |||
| 9c5e3c85ae | |||
| 3d47ee63d8 | |||
| 04eba21521 | |||
| ef314e36bc | |||
| 415f39e826 | |||
| 6733ca5f98 | |||
| 00c5b87aac |
0
.claude/commands/.gitignore
vendored
Normal file
0
.claude/commands/.gitignore
vendored
Normal file
@@ -1,5 +0,0 @@
|
||||
# Finish the current session
|
||||
|
||||
Update the CLAUDE.md according to your needs and what you've learned in this session.
|
||||
|
||||
Create a commit and push this file to branch `dev`
|
||||
@@ -21,7 +21,28 @@
|
||||
"Bash(php -l:*)",
|
||||
"Bash(git push:*)",
|
||||
"Bash(git checkout:*)",
|
||||
"Bash(git rebase:*)"
|
||||
"Bash(git rebase:*)",
|
||||
"Bash(git merge:*)",
|
||||
"Bash(git stash:*)",
|
||||
"Bash(for po in languages/*.po)",
|
||||
"Bash('wc-tier-and-package-prices/*.log' )",
|
||||
"Bash('wc-tier-and-package-prices/.claude/*' )",
|
||||
"Bash('wc-tier-and-package-prices/CLAUDE.md' )",
|
||||
"Bash('wc-tier-and-package-prices/releases/*' )",
|
||||
"Bash('wc-tier-and-package-prices/node_modules/*' )",
|
||||
"Bash('wc-tier-and-package-prices/.DS_Store' )",
|
||||
"Bash('wc-tier-and-package-prices/Thumbs.db' )",
|
||||
"Bash('wc-tier-and-package-prices/.vscode/*' )",
|
||||
"Bash('wc-tier-and-package-prices/.idea/*' )",
|
||||
"Bash('wc-tier-and-package-prices/*.sublime-*' )",
|
||||
"Bash('wc-tier-and-package-prices/notes.*' )",
|
||||
"Bash('wc-tier-and-package-prices/logs/*' )",
|
||||
"Bash('wc-tier-and-package-prices/templates/cache/*' )",
|
||||
"Bash('wc-tier-and-package-prices/composer.lock' )",
|
||||
"Bash('*/wordpress/*')",
|
||||
"Bash(echo:*)",
|
||||
"Skill(fix-session)",
|
||||
"Skill(fix-session:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
11
.gitignore
vendored
Normal file → Executable file
11
.gitignore
vendored
Normal file → Executable file
@@ -29,3 +29,14 @@ notes.*
|
||||
# OS
|
||||
.DS_Store
|
||||
._*
|
||||
|
||||
# local code
|
||||
wordpress
|
||||
core
|
||||
wp-core
|
||||
|
||||
# Releases (not tracked in git)
|
||||
/releases/
|
||||
|
||||
# Claude Code local settings
|
||||
.claude/settings.json
|
||||
|
||||
362
CHANGELOG.md
362
CHANGELOG.md
@@ -5,6 +5,368 @@ 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/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.3.1] - 2026-01-27
|
||||
|
||||
### Changed
|
||||
|
||||
- **Switched to SecureLicenseClient**: Upgraded from basic `LicenseClient` to `SecureLicenseClient` with HMAC-SHA256 response signature verification for enhanced security against tampering and replay attacks
|
||||
|
||||
- **Added Server Secret Configuration**: New "Server Secret" field in License settings for secure communication with the license server
|
||||
|
||||
### Added
|
||||
|
||||
- **Rate Limit Handling**: Added proper handling of `RateLimitExceededException` with user-friendly messages showing retry wait time
|
||||
- **Signature Verification Error Handling**: Added dedicated handling for `SignatureException` when response signatures fail verification
|
||||
- **URL Validation Error Handling**: Added handling for `InvalidArgumentException` from SSRF protection in the license client
|
||||
|
||||
### Security
|
||||
|
||||
- Response signatures are now verified using HMAC-SHA256 with license-specific derived keys (RFC 5869 HKDF)
|
||||
- The license client now validates server URLs to prevent SSRF attacks (blocks private IP ranges)
|
||||
- HTTP connections require HTTPS unless explicitly allowed for localhost testing
|
||||
|
||||
### Technical Details
|
||||
|
||||
**License Client Upgrade**:
|
||||
|
||||
- Changed from `LicenseClient` to `SecureLicenseClient`
|
||||
- Added `serverSecret` parameter for signature verification
|
||||
- Library updated from `v0.1.0` to `dev-main` with new security features
|
||||
|
||||
**New Exception Handling**:
|
||||
|
||||
- `RateLimitExceededException` - shows retry time to user
|
||||
- `SignatureException` - indicates server secret mismatch
|
||||
- `InvalidArgumentException` - invalid/blocked URL detected
|
||||
|
||||
**New Settings Field**:
|
||||
|
||||
- `wc_tpp_license_server_secret` (password type) for the shared secret
|
||||
|
||||
---
|
||||
|
||||
## [1.3.0] - 2026-01-25
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- **PHP 8.3 Required**: Minimum PHP version increased from 7.4 to 8.3 to support modern dependencies and the license client library. Users on older PHP versions will see an admin notice and the plugin will not load.
|
||||
|
||||
### Added
|
||||
|
||||
- **License Management**: Integrated `magdev/wc-licensed-product-client` library for license validation and activation
|
||||
- New "License" settings tab for entering license server URL and license key
|
||||
- License validation and activation via AJAX with visual feedback
|
||||
- License status display showing active/inactive state, expiration date, and last check time
|
||||
- Cached license status with daily auto-refresh
|
||||
|
||||
- **Settings Page Sub-tabs**: Split the settings page into "General" and "License" tabs using modern WooCommerce patterns
|
||||
- Refactored to use `get_own_sections()` and `get_settings_for_{section}_section()` methods
|
||||
- Improved navigation and organization of settings
|
||||
|
||||
- **PHP Version Check**: Added runtime PHP version validation with admin notice for incompatible servers
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated composer.json to require PHP 8.3+ and added `magdev/wc-licensed-product-client` dependency
|
||||
- Settings class now uses modern WooCommerce settings API patterns
|
||||
|
||||
### Technical Details
|
||||
|
||||
**New Dependencies**:
|
||||
- `magdev/wc-licensed-product-client: ^0.1` (from private repository)
|
||||
- `symfony/http-client: ^7.0` (transitive)
|
||||
- `psr/log: ^3.0`, `psr/cache: ^3.0`, `psr/http-client: ^1.0` (transitive)
|
||||
|
||||
**License Client Integration**:
|
||||
- Uses `LicenseClient` class for API communication
|
||||
- AJAX endpoints: `wc_tpp_validate_license`, `wc_tpp_activate_license`
|
||||
- License status cached in WordPress transient (`wc_tpp_license_status`)
|
||||
|
||||
---
|
||||
|
||||
## [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
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Currency Symbol Missing in Admin Headers and Placeholders**: Table headers in the admin pricing configuration now display "Price (CURRENCY)" instead of just "Price", making it immediately clear which currency is being used. Price input placeholders now show currency symbol (e.g., "e.g., 9.99 $" instead of "e.g., 9.99"), providing better UX for administrators configuring pricing in different currencies.
|
||||
|
||||
- **Variation Pricing Data Not Deleted Properly (Critical)**: When administrators deleted all tier or package pricing entries from a variation (or simple/parent product) and saved, the empty pricing data was still stored in the database instead of being deleted. This caused variations to retain deleted pricing rules. The save logic now properly detects when the filtered pricing arrays are empty after removing invalid entries and deletes the post meta instead of saving empty arrays.
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Currency Symbol Enhancement**:
|
||||
- Updated all table headers to use `printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol())`
|
||||
- Modified `render_tier_row()` and `render_package_row()` methods to pass `currency_symbol` to Twig templates
|
||||
- Updated `render_variation_tier_row()` and `render_variation_package_row()` with same currency symbol parameter
|
||||
- Changed Twig template placeholders from `'e.g., 9.99'` to `('e.g., 9.99 ' ~ currency_symbol)`
|
||||
- Affects all pricing contexts: simple products, variable product parents, and variations
|
||||
|
||||
**Pricing Deletion Fix**:
|
||||
- Modified `save_tier_package_fields()` method (simple/parent products) to check `if (!empty($tiers))` before saving
|
||||
- Modified `save_variation_pricing_fields()` method (variations) with same empty check logic
|
||||
- Changed logic from "save on isset, delete otherwise" to "filter entries, then save if not empty, delete if empty"
|
||||
- Applies to both tier pricing and package pricing for all product types
|
||||
- Root cause was filtering out empty entries but still calling `update_post_meta()` with an empty array
|
||||
|
||||
**User Impact**:
|
||||
- Administrators see currency symbol in all pricing configuration interfaces
|
||||
- Clear indication of which currency prices should be entered in
|
||||
- Deleting all pricing rules now properly removes them from the database
|
||||
- No orphaned pricing data remains after deletion
|
||||
- Works correctly for simple products, variable product parents, and variations
|
||||
|
||||
### Changed Files
|
||||
|
||||
- `includes/class-wc-tpp-product-meta.php` - Added currency symbol to all table headers; updated all render methods to pass currency symbol; fixed empty array deletion logic in both save methods
|
||||
- `templates/admin/tier-row.twig` - Updated placeholder to include currency symbol
|
||||
- `templates/admin/package-row.twig` - Updated placeholder to include currency symbol
|
||||
|
||||
## [1.2.7] - 2025-12-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Variable Product Forms Still Not Showing (Critical)**: The v1.2.6 fix used the wrong WooCommerce hook. The `woocommerce_product_options_pricing` hook only fires for simple products, not variable products. Changed to use `woocommerce_product_options_general_product_data` hook which fires for all product types after the general tab, allowing the code to check product type and conditionally display the parent pricing fields.
|
||||
|
||||
- **Table Headers Still Visible When Empty (Critical)**: The CSS `:has()` pseudo-class approach from v1.2.6 wasn't working reliably across all browsers. Implemented a JavaScript-based solution that adds/removes a `has-rows` class on tables based on whether they contain pricing rules. Headers now hide by default and show only when the table has rows, with JavaScript updating the state when rows are added or removed.
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Variable Product Hook Fix**:
|
||||
- Changed from `woocommerce_product_options_pricing` to `woocommerce_product_options_general_product_data`
|
||||
- The general product data hook fires for all product types
|
||||
- Method still checks `$product->is_type('variable')` to only show for variable products
|
||||
- This ensures forms appear in the correct location in the WordPress admin
|
||||
|
||||
**Table Header Visibility Fix**:
|
||||
- Replaced CSS-only `:has()` solution with JavaScript + CSS class approach
|
||||
- CSS now uses `.wc-tpp-tiers-table.has-rows thead` to show headers
|
||||
- Added `updateTableHeaders()` JavaScript function that checks row count and toggles class
|
||||
- Function is called on page load and after any add/remove row operation
|
||||
- Works reliably across all browsers without requiring modern CSS features
|
||||
|
||||
**User Impact**:
|
||||
- Variable product parent pricing forms now actually appear in the WordPress admin
|
||||
- Table headers properly hide when empty and show when populated
|
||||
- No browser compatibility issues - works in all modern browsers
|
||||
|
||||
### Changed Files
|
||||
|
||||
- `includes/class-wc-tpp-product-meta.php` - Changed hook from `woocommerce_product_options_pricing` to `woocommerce_product_options_general_product_data`
|
||||
- `assets/css/admin.css` - Replaced `:has()` pseudo-class with class-based approach
|
||||
- `assets/js/admin.js` - Added `updateTableHeaders()` function and calls after all row operations; added handlers for variable product parent forms
|
||||
|
||||
## [1.2.6] - 2025-12-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Parent Product Pricing Forms Not Visible (Critical)**: Variable products were missing the pricing configuration forms on the parent product edit page. The v1.2.5 feature for parent product default pricing was implemented in the backend logic (cart calculations and frontend display) but the admin UI to configure these defaults was not added. Now variable product parents have a dedicated "Default Tier & Package Pricing for All Variations" section in the product edit page where administrators can configure default pricing that applies to all variations unless a specific variation overrides it.
|
||||
|
||||
- **Table Headers Not Hiding When Empty**: The CSS selector for hiding table headers when no pricing rules exist was using an incorrect approach. The sibling selector `~` doesn't work when `<thead>` comes before `<tbody>` in the HTML structure. Removed the sibling selector and kept only the `:has()` pseudo-class approach with `!important` flag for proper specificity.
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Parent Product Forms Fix**:
|
||||
- Added new method `add_variable_parent_pricing_fields()` in `WC_TPP_Product_Meta` class
|
||||
- Hooked to `woocommerce_product_options_pricing` action but only displays for variable products
|
||||
- Modified existing `add_tier_pricing_fields()` and `add_package_pricing_fields()` to skip variable products (they now only show for simple products)
|
||||
- Parent product forms use same field names as simple products (`_wc_tpp_tiers`, `_wc_tpp_packages`, `_wc_tpp_restrict_to_packages`)
|
||||
- Data is saved to parent product post meta using existing `save_tier_package_fields()` method
|
||||
- Backend fallback logic from v1.2.5 now has matching admin UI for configuration
|
||||
|
||||
**CSS Selector Fix**:
|
||||
- Removed incorrect `.wc-tpp-tiers-container:empty ~ thead` selector (sibling selector can't target previous elements)
|
||||
- Kept only `.wc-tpp-tiers-table:has(tbody.wc-tpp-tiers-container:empty) thead` with `!important` flag
|
||||
- `:has()` pseudo-class is supported in modern browsers (Chrome 105+, Firefox 121+, Safari 15.4+)
|
||||
|
||||
**User Impact**:
|
||||
- Administrators can now configure default tier/package pricing on variable product parents (feature was non-functional in v1.2.5)
|
||||
- Table headers properly hide when no pricing rules exist, creating cleaner admin interface
|
||||
- No data migration needed - existing configurations remain intact
|
||||
|
||||
### Changed Files
|
||||
|
||||
- `includes/class-wc-tpp-product-meta.php` - Added `add_variable_parent_pricing_fields()` method; modified `add_tier_pricing_fields()` and `add_package_pricing_fields()` to skip variable products
|
||||
- `assets/css/admin.css` - Fixed table header hiding CSS selector; removed incorrect sibling selector; added `!important` flag
|
||||
|
||||
## [1.2.5] - 2025-12-30
|
||||
|
||||
### Added
|
||||
|
||||
- **Parent Product Default Pricing**: Variable products can now define tier and package pricing at the parent product level that applies as defaults to all variations. Individual variations can override these defaults with their own specific pricing. This makes it much easier to set up pricing for products with many variations - set defaults once on the parent, then only customize the variations that need different pricing.
|
||||
|
||||
- **Hide Empty Table Headers**: Table headers for tier and package pricing in the admin area now automatically hide when no pricing rules are defined. This creates a cleaner interface when starting to configure a product, showing only the helpful empty state message and "Add" button.
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Parent Fallback Implementation**:
|
||||
- Modified `WC_TPP_Frontend::get_tier_price()` and `WC_TPP_Frontend::get_package_price()` to fall back to parent product pricing when variation doesn't have its own pricing
|
||||
- Updated `WC_TPP_Cart` to use helper methods `get_packages_with_fallback()` and `is_restriction_enabled()` for consistent parent fallback behavior
|
||||
- All cart validation, quantity restriction, and display methods now support parent product defaults
|
||||
- Fixed cart pricing calls to pass parent `$product_id` instead of `$effective_id` for proper fallback resolution
|
||||
|
||||
**CSS Enhancement**:
|
||||
- Added `:has()` pseudo-class selectors to hide table headers when tbody is empty
|
||||
- Leverages existing empty state message styling for consistent UX
|
||||
|
||||
**Backward Compatibility**:
|
||||
- 100% backward compatible - existing products continue working as before
|
||||
- No database migrations required
|
||||
- Variations with specific pricing take precedence over parent defaults
|
||||
|
||||
### Changed Files
|
||||
|
||||
- `includes/class-wc-tpp-frontend.php` - Added parent fallback logic to `get_tier_price()` and `get_package_price()` methods
|
||||
- `includes/class-wc-tpp-cart.php` - Added helper methods `get_packages_with_fallback()` and `is_restriction_enabled()`; updated all cart methods to support parent fallback; fixed pricing calls to use correct product ID
|
||||
- `assets/css/admin.css` - Added CSS rules to hide table headers when no pricing rules exist
|
||||
|
||||
## [1.2.4] - 2025-12-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Admin Table Borders (Critical)**: Fixed table borders still appearing in v1.2.3 despite borderless styling attempt. WooCommerce's default CSS was overriding `border: none` declarations. Added `!important` flags to all border removal rules and `border-collapse: collapse !important` to force borderless styling. Now all tier/package pricing tables (simple and variable products) display correctly without borders, matching WooCommerce's clean admin UI.
|
||||
|
||||
- **Checkbox and Help Icon Layout**: Fixed help icon positioning and checkbox spacing issues from v1.2.3. The help icon was appearing at the right edge of the container instead of next to the label text. Increased checkbox-label margin from 8px to 12px for better spacing. Changed label layout from float positioning to flexbox (`inline-flex`) to keep help icon directly adjacent to label text. Added inline description hiding when tooltip is shown.
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Root Cause - Table Borders**: WooCommerce's core admin CSS has higher specificity border rules that override simple `border: none` declarations. The solution required:
|
||||
- Adding `!important` to all `border: none` rules targeting tables, th, td, thead, tbody, and tr elements
|
||||
- Adding `border-collapse: collapse !important` to prevent cell borders from being visible
|
||||
- Comprehensive targeting of all table structural elements for complete border removal
|
||||
|
||||
**Root Cause - Help Icon Position**: WooCommerce's default `.woocommerce-help-tip` styling uses `float: right` which positions the icon at the container's right edge. The fix:
|
||||
- Removed float positioning with `float: none`
|
||||
- Changed to `display: inline-block` with `vertical-align: middle`
|
||||
- Wrapped label and help-tip in flexbox container (`display: inline-flex; align-items: center`)
|
||||
- Controlled spacing with precise margins (checkbox: 12px right, help-tip: 6px left)
|
||||
|
||||
### Changed Files
|
||||
|
||||
- `assets/css/admin.css` - Added `!important` flags to all border removal rules; added `border-collapse: collapse`; increased checkbox margin to 12px; converted label layout to flexbox; positioned help-tip with inline-block; added inline description hiding
|
||||
|
||||
## [1.2.3] - 2025-12-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Admin Table Styling**: Applied borderless table styling to all tier/package tables (both simple and variable products). Previously only variation tables had borders removed in v1.2.2. Now all pricing tables in the admin have a consistent borderless appearance matching WooCommerce's clean admin UI style.
|
||||
|
||||
- **Checkbox Styling and Tooltip**: Fixed checkbox styling issues where the help text was displayed inline instead of as a tooltip, and the margin between checkbox and label was too small. Added `desc_tip => true` to the variation restriction checkbox to enable tooltip display. Added CSS rules to increase checkbox-label margin to 8px and hide inline description text when tooltips are used.
|
||||
|
||||
### Changed Files
|
||||
|
||||
- `assets/css/admin.css` - Applied `border: none` to all tier/package table elements; added checkbox margin and description hiding rules
|
||||
- `includes/class-wc-tpp-product-meta.php` - Added `desc_tip => true` parameter to variation checkbox (line 213)
|
||||
|
||||
## [1.2.2] - 2025-12-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Variation UI Styling**: Removed table borders for variation pricing tables to match WooCommerce's borderless variation UI style. Added CSS rules specifically targeting `.wc-tpp-variation-pricing` tables to remove borders while keeping them for simple product tables.
|
||||
|
||||
- **Missing Translations**: Added missing admin template translations for "Min Quantity", "Price", and "Label (optional)" to all language files (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH, it_CH, en_US). These strings were used in the variation admin UI added in v1.2.0 but weren't included in translation files.
|
||||
|
||||
- **Checkbox Rendering**: Fixed variation restriction checkbox rendering issue. The `wc_tpp_restrict_to_packages[]` checkbox in variation pricing fields was using a ternary expression that prevented proper checked state handling. Simplified to direct value assignment for WooCommerce's checkbox function to work correctly.
|
||||
|
||||
### Changed Files
|
||||
|
||||
- `assets/css/admin.css` - Added border removal for variation pricing tables
|
||||
- `includes/class-wc-tpp-product-meta.php` - Fixed checkbox value parameter (line 213)
|
||||
- `languages/*.po` - Added missing translation entries
|
||||
- `languages/*.mo` - Recompiled from updated .po files
|
||||
|
||||
## [1.2.1] - 2025-12-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Admin UI Display**: Fixed table layout in admin product edit screens. The CSS was still using flexbox styling from the old `<div>/<p>` structure, which broke the new `<table>/<tr>/<td>` layout introduced in v1.2.0. Updated `assets/css/admin.css` to properly style table rows with standard table cell padding and removed obsolete flexbox properties.
|
||||
|
||||
- **Frontend Pricing Display**: Fixed pricing tables not showing on simple product pages. Removed global "Enable Tier Pricing" and "Enable Package Pricing" checks from the frontend template (`templates/frontend/pricing-table.twig`). Pricing tables now display if configured on a product AND the "Display Pricing Table" setting is enabled, regardless of individual feature enable settings. Cart calculations still respect global enable settings for proper pricing application.
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Root Cause - Admin UI Bug**: In v1.2.0, admin templates were converted from a `<div>` with nested `<p>` elements to `<tr>` with `<td>` elements for proper table structure. However, the CSS file (`assets/css/admin.css`) was not updated accordingly, leaving flexbox styling (`.wc-tpp-tier-row { display: flex; gap: 15px; ... }`) that conflicted with table display. This caused columns to not align with table headers.
|
||||
|
||||
**Root Cause - Frontend Display Bug**: The frontend pricing table template was checking both `get_option('wc_tpp_enable_tier_pricing')` AND `get_option('wc_tpp_enable_package_pricing')` before displaying pricing. This meant if these global settings were disabled (even though defaults are 'yes'), pricing configured on products wouldn't show. The better UX is: if pricing is configured AND display is enabled, show it. The global enable settings now only control cart calculation and admin UI visibility.
|
||||
|
||||
### Changed Files
|
||||
|
||||
- `assets/css/admin.css` - Replaced flexbox styling with table cell styling
|
||||
- `templates/frontend/pricing-table.twig` - Removed global enable setting checks from display conditions
|
||||
|
||||
## [1.2.0] - 2025-12-29
|
||||
|
||||
### Added - Variable Product Support
|
||||
|
||||
595
CLAUDE.md
595
CLAUDE.md
@@ -1,7 +1,7 @@
|
||||
# WooCommerce Tier and Package Prices - AI Context Document
|
||||
|
||||
**Last Updated:** 2025-12-29
|
||||
**Current Version:** 1.2.0
|
||||
**Last Updated:** 2026-01-27
|
||||
**Current Version:** 1.3.1
|
||||
**Author:** Marco Graetsch
|
||||
**Project Status:** Production-ready WordPress plugin
|
||||
|
||||
@@ -13,11 +13,20 @@ This is a WooCommerce plugin that adds flexible pricing capabilities to products
|
||||
2. **Package Pricing (Fixed Bundles)**: Exact quantity packages at fixed prices (e.g., exactly 10 items for $95, exactly 25 for $200)
|
||||
|
||||
### Key Fact: 100% AI-Generated
|
||||
|
||||
This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase was created through AI assistance.
|
||||
|
||||
## Temporary Roadmap
|
||||
|
||||
**Note for AI Assistants:** Clean this section after the specific features are done or new releases are made. Effective changes are tracked in `CHANGELOG.md`. Do not add completed versions here - document them in the Session History section at the end of this file. Always keep the `Known Bugs` section and create a section with the next bugfix and minor version after a release.
|
||||
|
||||
### Version 1.3.2
|
||||
|
||||
- No planned changes yet
|
||||
|
||||
## Technical Stack
|
||||
|
||||
- **Language:** PHP 7.4+
|
||||
- **Language:** PHP 8.3+
|
||||
- **Framework:** WordPress Plugin API
|
||||
- **E-commerce:** WooCommerce 8.0+ (tested up to 10.x)
|
||||
- **Template Engine:** Twig 3.0 (via Composer)
|
||||
@@ -27,18 +36,23 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
|
||||
- **Internationalization:** WordPress i18n (.pot/.po/.mo files)
|
||||
|
||||
### Dependencies
|
||||
|
||||
```json
|
||||
{
|
||||
"twig/twig": "^3.0",
|
||||
"symfony/polyfill-ctype": "^1.x",
|
||||
"symfony/polyfill-mbstring": "^1.x"
|
||||
"magdev/wc-licensed-product-client": "dev-main",
|
||||
"symfony/http-client": "^7.0",
|
||||
"psr/log": "^3.0",
|
||||
"psr/cache": "^3.0",
|
||||
"psr/http-client": "^1.0"
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
|
||||
```txt
|
||||
wc-tier-and-package-prices/
|
||||
├── wc-tier-and-package-prices.php # Main plugin file (entry point)
|
||||
├── includes/ # PHP classes
|
||||
@@ -172,6 +186,7 @@ wc-tier-and-package-prices/
|
||||
### Price Calculation Logic
|
||||
|
||||
**Package Pricing** (exact match):
|
||||
|
||||
```php
|
||||
// In cart: if quantity == 10 and package exists for 10, use package price
|
||||
if ($quantity == $package['qty']) {
|
||||
@@ -181,6 +196,7 @@ if ($quantity == $package['qty']) {
|
||||
```
|
||||
|
||||
**Tier Pricing** (range-based):
|
||||
|
||||
```php
|
||||
// In cart: if quantity >= 10, use tier price for quantities 10+
|
||||
foreach ($tiers as $tier) {
|
||||
@@ -226,7 +242,9 @@ This metadata is used by display filters to show "(Package price)" or "(Volume d
|
||||
## Common Patterns & Conventions
|
||||
|
||||
### Class Instantiation Pattern
|
||||
|
||||
All classes auto-instantiate at the end of their file:
|
||||
|
||||
```php
|
||||
if (!class_exists('WC_TPP_Frontend')) {
|
||||
class WC_TPP_Frontend {
|
||||
@@ -247,7 +265,9 @@ new WC_TPP_Frontend(); // Auto-instantiate
|
||||
- Direct file access prevention via `ABSPATH` check
|
||||
|
||||
### Translation Ready
|
||||
|
||||
All user-facing strings use:
|
||||
|
||||
```php
|
||||
__('Text to translate', 'wc-tier-package-prices')
|
||||
_e('Text to translate', 'wc-tier-package-prices')
|
||||
@@ -270,6 +290,7 @@ Note: Swiss locales use CHF currency formatting in examples (e.g., "CHF 50.-")
|
||||
## Known Issues & Historical Context
|
||||
|
||||
### Settings Page Duplication Saga (v1.1.15-1.1.19)
|
||||
|
||||
Multiple versions attempted to fix settings page appearing twice:
|
||||
|
||||
- **Root cause:** Settings file auto-instantiation + Composer autoloader
|
||||
@@ -277,6 +298,7 @@ Multiple versions attempted to fix settings page appearing twice:
|
||||
- **Prevention:** Singleton pattern + duplicate detection in array
|
||||
|
||||
### Class Redeclaration Issues (v1.1.8-1.1.14)
|
||||
|
||||
Plugin was completely non-functional:
|
||||
|
||||
- **Cause:** Incorrect initialization pattern without `class_exists()` guards
|
||||
@@ -284,7 +306,8 @@ Plugin was completely non-functional:
|
||||
- **Lesson:** Always wrap class declarations in `class_exists()` checks
|
||||
|
||||
### WooCommerce Blocks Fatal Error (v1.1.19 → v1.1.20)
|
||||
```
|
||||
|
||||
```txt
|
||||
Fatal error: Cannot use object of type WC_Product_Simple as array
|
||||
Location: includes/class-wc-tpp-cart.php:233
|
||||
```
|
||||
@@ -293,9 +316,45 @@ Location: includes/class-wc-tpp-cart.php:233
|
||||
- **Fix:** Changed method signature to accept `WC_Product $product` instead of `$cart_item` array
|
||||
- **Status:** FIXED in v1.1.20
|
||||
|
||||
### CSS Specificity Issues (v1.2.3 → v1.2.4)
|
||||
|
||||
**Problem:** Admin table borders still visible despite `border: none` declarations in v1.2.3
|
||||
|
||||
```txt
|
||||
Issue: WooCommerce's core admin CSS has higher specificity border rules
|
||||
Location: assets/css/admin.css
|
||||
Symptom: Tables still showing borders in product edit screens
|
||||
```
|
||||
|
||||
- **Root Cause:** WooCommerce's default admin CSS uses highly specific selectors that override simple `border: none` declarations
|
||||
- **Failed Approach (v1.2.3):** Adding `border: none` to table elements without `!important`
|
||||
- **Successful Fix (v1.2.4):**
|
||||
- Added `!important` flags to ALL border removal rules
|
||||
- Added `border-collapse: collapse !important` to force borderless styling
|
||||
- Targeted all table structural elements: `table`, `th`, `td`, `thead`, `tbody`, `tr`
|
||||
- **Lesson:** When overriding WooCommerce core CSS, `!important` is often necessary due to high specificity in core styles
|
||||
|
||||
**Problem:** Help icon positioned at right edge instead of next to label text
|
||||
|
||||
```txt
|
||||
Issue: WooCommerce help-tip uses float: right positioning
|
||||
Location: assets/css/admin.css (checkbox/label layout)
|
||||
Symptom: Help icon appearing far from label text at container edge
|
||||
```
|
||||
|
||||
- **Root Cause:** WooCommerce's default `.woocommerce-help-tip` styling uses `float: right`
|
||||
- **Failed Approach (v1.2.3):** Simple margin adjustments without changing positioning model
|
||||
- **Successful Fix (v1.2.4):**
|
||||
- Removed float positioning: `float: none`
|
||||
- Changed to `display: inline-block` with `vertical-align: middle`
|
||||
- Wrapped label and help-tip in flexbox container: `display: inline-flex; align-items: center`
|
||||
- Controlled precise spacing with margins (checkbox: 12px, help-tip: 6px)
|
||||
- **Lesson:** Overriding float-based layouts often requires changing to flexbox for proper control
|
||||
|
||||
## Release Process
|
||||
|
||||
### Version Bumping
|
||||
|
||||
Update version in 3 places:
|
||||
|
||||
1. `wc-tier-and-package-prices.php` - Plugin header comment (line 7)
|
||||
@@ -307,8 +366,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.
|
||||
|
||||
```bash
|
||||
# From parent directory (/home/magdev/workspaces/node)
|
||||
cd /home/magdev/workspaces/node
|
||||
# From parent directory (/home/magdev/workspaces/php)
|
||||
cd /home/magdev/workspaces/php
|
||||
|
||||
# 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/ \
|
||||
@@ -331,9 +390,8 @@ zip -r wc-tier-and-package-prices/releases/wc-tier-and-package-prices-X.X.X.zip
|
||||
# Return to project directory
|
||||
cd wc-tier-and-package-prices
|
||||
|
||||
# Generate checksums
|
||||
# Generate SHA256 checksum
|
||||
cd releases
|
||||
md5sum wc-tier-and-package-prices-X.X.X.zip > wc-tier-and-package-prices-X.X.X.zip.md5
|
||||
sha256sum wc-tier-and-package-prices-X.X.X.zip > wc-tier-and-package-prices-X.X.X.zip.sha256
|
||||
cd ..
|
||||
```
|
||||
@@ -468,6 +526,7 @@ When making changes, test these critical paths:
|
||||
### Common Pitfalls and Solutions
|
||||
|
||||
#### Release Package Creation
|
||||
|
||||
**Problem:** Empty or corrupted zip files (0 bytes or wrong structure)
|
||||
**Cause:** Running zip command from wrong directory or incorrect path patterns
|
||||
**Solution:** Always run from parent directory (`/home/magdev/workspaces/node`) and use full relative paths in exclusions
|
||||
@@ -481,7 +540,9 @@ When making changes, test these critical paths:
|
||||
**Solution:** Follow verification steps and check package size immediately after creation
|
||||
|
||||
#### UI Changes in Admin
|
||||
|
||||
**WooCommerce CSS Classes:**
|
||||
|
||||
- `short` - Small input fields (~60px width)
|
||||
- `regular` - Medium input fields (~120px width)
|
||||
- `long` - Large input fields (~200px+ width)
|
||||
@@ -490,7 +551,91 @@ When modifying admin input fields in Twig templates, use WooCommerce's standard
|
||||
|
||||
**Location:** `templates/admin/*.twig` for admin UI changes
|
||||
|
||||
#### CSS Specificity and WooCommerce Overrides
|
||||
|
||||
**CRITICAL LESSON from v1.2.4:** WooCommerce's core admin CSS uses high-specificity selectors that require `!important` to override.
|
||||
|
||||
**Problem Symptoms:**
|
||||
|
||||
- CSS rules not applying despite correct selectors
|
||||
- Styles work in simple cases but fail with WooCommerce elements
|
||||
- Browser DevTools shows rule crossed out or overridden
|
||||
|
||||
**Diagnostic Steps:**
|
||||
|
||||
1. Inspect element in browser DevTools
|
||||
2. Check "Computed" tab to see which styles are actually applied
|
||||
3. Look for crossed-out rules in "Styles" tab (indicates override)
|
||||
4. Check selector specificity - WooCommerce often uses complex selectors
|
||||
|
||||
**Solution Patterns:**
|
||||
|
||||
**For Table Styling:**
|
||||
|
||||
```css
|
||||
/* ❌ This will likely be overridden */
|
||||
.wc-tpp-tiers-table {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* ✅ Use !important for core WooCommerce overrides */
|
||||
.wc-tpp-tiers-table,
|
||||
.wc-tpp-tiers-table th,
|
||||
.wc-tpp-tiers-table td,
|
||||
.wc-tpp-tiers-table thead,
|
||||
.wc-tpp-tiers-table tbody,
|
||||
.wc-tpp-tiers-table tr {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* ✅ Also use border-collapse to prevent cell borders */
|
||||
.wc-tpp-tiers-table {
|
||||
border-collapse: collapse !important;
|
||||
}
|
||||
```
|
||||
|
||||
**For Float-Based Layouts:**
|
||||
|
||||
```css
|
||||
/* ❌ Float positioning is hard to control precisely */
|
||||
.woocommerce-help-tip {
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* ✅ Use flexbox for precise control */
|
||||
label[for="_wc_tpp_restrict_to_packages"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.woocommerce-help-tip {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-left: 6px;
|
||||
margin-right: 0;
|
||||
}
|
||||
```
|
||||
|
||||
**General Rules:**
|
||||
|
||||
1. **Always test in actual WordPress admin** - browser preview may not show WooCommerce's CSS
|
||||
2. **Target all related elements** - tables require styling on `table`, `thead`, `tbody`, `tr`, `th`, `td`
|
||||
3. **Use `!important` sparingly but don't fear it** - sometimes it's the only way to override WooCommerce core
|
||||
4. **Prefer flexbox over floats** - gives better control over alignment and spacing
|
||||
5. **Check across browsers** - table rendering can vary between Chrome/Firefox/Safari
|
||||
|
||||
**When Styles Don't Apply:**
|
||||
|
||||
- First verify selector is correct (DevTools should show rule, even if crossed out)
|
||||
- If selector is correct but crossed out, increase specificity or add `!important`
|
||||
- If selector doesn't appear at all, check file is enqueued and cache is cleared
|
||||
- Use browser's "Inspect" right-click to see exact element structure
|
||||
|
||||
#### Git Workflow Issues
|
||||
|
||||
**Problem:** Cannot rebase due to uncommitted changes
|
||||
**Solution:** Stash local config files (`.claude/settings.local.json`) before git operations
|
||||
|
||||
@@ -519,7 +664,7 @@ The plugin uses Twig 3.0 for templating. Key files:
|
||||
|
||||
### Complete Release Workflow Summary
|
||||
|
||||
Based on v1.1.22 release experience, here's the complete workflow:
|
||||
Based on v1.1.22, v1.2.2, and v1.2.3 release experience, here's the complete workflow:
|
||||
|
||||
1. **Fix bugs/add features** on `dev` branch
|
||||
2. **Update version numbers** (3 files: main plugin file header, constant, composer.json)
|
||||
@@ -541,9 +686,56 @@ Based on v1.1.22 release experience, here's the complete workflow:
|
||||
- `composer.json` - Version bump
|
||||
- `CHANGELOG.md` - Release notes
|
||||
- `CLAUDE.md` - Version and roadmap updates
|
||||
- `releases/wc-tier-and-package-prices-X.X.X.zip*` - Package and checksums
|
||||
- Feature-specific files (templates, PHP classes, etc.)
|
||||
|
||||
**Note:** Release packages (`releases/`) are not tracked in git - they are generated locally for distribution.
|
||||
|
||||
### Release Package Creation - Critical Notes
|
||||
|
||||
**IMPORTANT:** The zip command must be run from the **parent directory** to create proper archive structure.
|
||||
|
||||
**Correct command (from `/home/magdev/workspaces/php`):**
|
||||
|
||||
```bash
|
||||
cd /home/magdev/workspaces/php
|
||||
zip -r wc-tier-and-package-prices/releases/wc-tier-and-package-prices-X.X.X.zip wc-tier-and-package-prices/ \
|
||||
-x '*/\.git/*' '*/.git/*' 'wc-tier-and-package-prices/.git/*' \
|
||||
'*.gitignore' '*.log' '*/.claude/*' '*/CLAUDE.md' \
|
||||
'*/releases/*' '*/wordpress/*' '*/node_modules/*' \
|
||||
'*/.DS_Store' '*/Thumbs.db' '*/.vscode/*' '*/.idea/*' \
|
||||
'*.sublime-*' '*/notes.*' '*/logs/*' '*/templates/cache/*' \
|
||||
'*/composer.lock'
|
||||
```
|
||||
|
||||
**Critical Exclusions:**
|
||||
|
||||
- `*/wordpress/*` and `*/core/*` - MUST be excluded! The project has a symlink to WordPress installation that zip will follow, creating 129MB+ packages instead of ~430KB
|
||||
- `.git/*` - All git metadata (multiple patterns needed for reliability)
|
||||
- `.claude/*` and `CLAUDE.md` - Development documentation
|
||||
- `releases/*` - Prevents including previous releases in new ones
|
||||
- `composer.lock` - Not needed in production (vendor/ is included)
|
||||
|
||||
**Expected Package Size:** ~430-431KB (383 files)
|
||||
|
||||
**Package Size Alert:** If >1MB, exclusions failed (likely wordpress symlink included)
|
||||
|
||||
**Verification Steps:**
|
||||
|
||||
```bash
|
||||
# 1. Check size (should be ~430KB)
|
||||
ls -lh releases/wc-tier-and-package-prices-X.X.X.zip
|
||||
|
||||
# 2. Verify file count (should be 383 files)
|
||||
unzip -l releases/wc-tier-and-package-prices-X.X.X.zip | tail -1
|
||||
|
||||
# 3. Check for excluded files
|
||||
unzip -l releases/wc-tier-and-package-prices-X.X.X.zip | grep -E "CLAUDE\.md|\.claude/|\.git/|wordpress/"
|
||||
# Should return nothing (exit code 1)
|
||||
|
||||
# 4. Verify version in package
|
||||
unzip -p releases/wc-tier-and-package-prices-X.X.X.zip wc-tier-and-package-prices/wc-tier-and-package-prices.php | head -30 | grep -E "Version:|WC_TPP_VERSION"
|
||||
```
|
||||
|
||||
### Future Features and Roadmap
|
||||
|
||||
The is a hierarchical list for upcoming features and can be considered as a
|
||||
@@ -558,7 +750,63 @@ Roadmap for the upcoming development.
|
||||
|
||||
#### Version 1.2.x
|
||||
|
||||
1. New Feature: 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.
|
||||
##### Bugfixes (Completed in v1.2.1)
|
||||
|
||||
1. ~~The admin templates are not show right. The row templates didn't match the new table structure. The table-body columns didn't fit the table-head columns.~~ ✅ **FIXED in v1.2.1** - Updated admin.css to remove flexbox styling that was breaking the new `<table>/<tr>/<td>` structure introduced in v1.2.0. The CSS was still using flexbox layout from the old `<div>/<p>` structure.
|
||||
|
||||
2. ~~The tier and package prices are not shown on simple product pages~~ ✅ **FIXED in v1.2.1** - Removed global enable/disable checks from frontend template. Pricing tables now display if configured on a product AND the "Display Pricing Table" setting is enabled, regardless of "Enable Tier Pricing" or "Enable Package Pricing" global settings. Cart calculations still respect global enable settings.
|
||||
|
||||
##### Bugfixes (Completed in v1.2.2)
|
||||
|
||||
1. ~~Remove the table borders in admin on variable product to better fit the surrounding element styles.~~ ✅ **FIXED in v1.2.2** - Added CSS rules to remove table borders specifically for variation pricing tables (`.wc-tpp-variation-pricing`), matching WooCommerce's borderless variation UI style.
|
||||
|
||||
2. ~~Add missing translations in admin templates ("Price", "Tier & Package Pricing", "Min Quantity") for all languages used in this project.~~ ✅ **FIXED in v1.2.2** - Added missing translation entries for "Min Quantity", "Price", and "Label (optional)" to all .po files (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH, it_CH, en_US) and recompiled .mo files.
|
||||
|
||||
3. ~~Check the template for wc_tpp_restrict_to_packages[] checkbox elements in admin on variable products and fix the rendering.~~ ✅ **FIXED in v1.2.2** - Fixed checkbox value parameter in variation pricing fields. Changed from ternary expression to direct value assignment, allowing WooCommerce's `woocommerce_wp_checkbox()` to properly handle the checked state.
|
||||
|
||||
##### Bugfixes (Completed in v1.2.3)
|
||||
|
||||
1. ~~Style the tier and packages tables in admin on simple products according to the styles on variable products.~~ ✅ **FIXED in v1.2.3** - Applied borderless table styling to all tier/package tables (both simple and variable products). Removed borders from table, th, and td elements to match WooCommerce's clean admin UI style.
|
||||
|
||||
2. ~~The checkbox styles from 1.2.2 bug 3 are still not looking correct. The helptext is written instead of hidden after the help icon and the margin between checkbox and label are to small.~~ ✅ **FIXED in v1.2.3** - Added `desc_tip => true` to variation checkbox to show tooltip instead of inline text. Added CSS rules to increase checkbox-label margin (8px) and hide inline description text when tooltip is used.
|
||||
|
||||
##### Bugfixes (Completed in v1.2.4)
|
||||
|
||||
1. ~~Bug 1 in v1.2.3 is not fixed. Now both table display have border again. they shouldn't have border.~~ ✅ **FIXED in v1.2.4** - Added `!important` flags and `border-collapse: collapse` to table CSS to override WooCommerce's default table styling. Added comprehensive border removal for all table elements (table, thead, tbody, tr, th, td) to ensure truly borderless tables across all browsers.
|
||||
|
||||
2. ~~Bug 2 in v1.2.3: Increase the margin between checkbox and label and put the help icon right next to the label, not at the right border~~ ✅ **FIXED in v1.2.4** - Increased checkbox right margin from 8px to 12px. Repositioned help tip icon to display inline right next to the label text using flexbox layout with `display: inline-flex`, removing float positioning that caused it to appear at the right edge.
|
||||
|
||||
##### Enhancements (Completed in v1.2.5)
|
||||
|
||||
1. ~~Hide the table-headers in admin area until a tier or respectivly a package price is defined.~~ ✅ **COMPLETED in v1.2.5** - Added CSS `:has()` pseudo-class selectors to automatically hide table headers when tbody is empty. Creates a cleaner interface showing only the helpful empty state message and "Add" button when no pricing rules are configured.
|
||||
|
||||
2. ~~Make it possible to define tier or package prices on variable products in the parent product as a default for that product and all variants of it unless a variant has its own tier or package prices.~~ ✅ **COMPLETED in v1.2.5** - Implemented parent product default pricing with automatic fallback. Variable products can define tier/package pricing once at parent level; variations inherit these defaults unless they have their own specific pricing. Added helper methods in cart class and updated all pricing/restriction checks to support parent fallback.
|
||||
|
||||
##### Bugfixes (Completed in v1.2.6 and v1.2.7)
|
||||
|
||||
1. ~~Table headers in admin are still visible when empty.~~ ✅ **FIXED in v1.2.7** - The CSS `:has()` pseudo-class approach from v1.2.5/v1.2.6 wasn't working reliably across all browsers. Implemented JavaScript-based solution that adds/removes `has-rows` class on tables. Headers now hide by default (CSS) and show only when table has rows (JavaScript toggles class). Function `updateTableHeaders()` is called on page load and after all add/remove row operations.
|
||||
|
||||
2. ~~Parent product pricing forms not visible in admin.~~ ✅ **FIXED in v1.2.6 and v1.2.7** - The backend fallback logic from v1.2.5 was implemented but the admin UI to configure it was missing. Added `add_variable_parent_pricing_fields()` method that displays pricing forms for variable product parents. Fixed hook issue in v1.2.7: changed from `woocommerce_product_options_pricing` (only fires for simple products) to `woocommerce_product_options_general_product_data` (fires for all product types). Variable product parents now have a "Default Tier & Package Pricing for All Variations" section where defaults can be configured.
|
||||
|
||||
##### Translation Updates (Completed in v1.2.7)
|
||||
|
||||
1. ✅ **COMPLETED** - Updated all translation files (.pot, .po, .mo) with new strings from v1.2.6 and v1.2.7 for variable product parent pricing features. All 7 language variants updated with translations for "Default Tier & Package Pricing for All Variations" and related strings.
|
||||
|
||||
##### Bugfixes (Completed in v1.2.8)
|
||||
|
||||
1. ~~Add a Suffix with the current configured default currency to the table-header and form placeholder. Use the common currency notation in placeholder~~ ✅ **FIXED in v1.2.8** - Updated all table headers in admin to display "Price (€)" format using `printf(__('Price (%s)'), get_woocommerce_currency_symbol())`. Modified all template render methods (tier_row, package_row, variation_tier_row, variation_package_row) to pass currency_symbol to Twig templates. Updated admin/tier-row.twig and admin/package-row.twig to concatenate currency symbol in price input placeholders (e.g., "e.g., 9.99 €"). Applied to simple products, variable parent products, and all variations.
|
||||
|
||||
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.
|
||||
|
||||
##### Bugfixes (Completed in v1.2.9)
|
||||
|
||||
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.~~ ✅ **FIXED in v1.2.9** - Fixed translation function placement in printf statements by changing from `printf(__('Price (%s)', ...), ...)` to `printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), ...)`. This ensures the text domain is passed correctly to the translation function while maintaining the currency placeholder functionality. Also removed the translation filter from concatenated placeholder strings in Twig templates (changed from `{{ ('e.g., 9.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }}` to `{{ 'e.g., 9.99 ' ~ currency_symbol }}`) because example values should not be translated and the filter was causing HTML entity encoding of special characters.
|
||||
|
||||
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.~~ ✅ **FIXED in v1.2.9** - Despite the fix in v1.2.8, edge cases remained due to conditional branching structure. Refactored `save_variation_pricing_fields()` with more defensive logic: initialize arrays at the start ($tiers = array()), populate only if valid POST data exists (with added is_array() check), then unconditionally perform either update_post_meta() (if !empty) or delete_post_meta() (if empty). This guarantees proper cleanup regardless of POST data structure and eliminates the if/else branching that could miss edge cases.
|
||||
|
||||
##### Planned Enhancements for v1.2.10+
|
||||
|
||||
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.
|
||||
|
||||
### When Debugging Cart Issues
|
||||
|
||||
@@ -574,6 +822,239 @@ Roadmap for the upcoming development.
|
||||
- Always check filter/action documentation for parameter types
|
||||
- Don't assume cart item arrays everywhere - sometimes it's product objects!
|
||||
|
||||
#### CRITICAL: Product Type-Specific Hooks (Learned in v1.2.6/v1.2.7)
|
||||
|
||||
WooCommerce has different hooks for different product types in the admin product edit page:
|
||||
|
||||
- `woocommerce_product_options_pricing` - **ONLY fires for simple products**, NOT variable products
|
||||
- `woocommerce_product_options_general_product_data` - Fires for ALL product types after the general tab
|
||||
- `woocommerce_variation_options_pricing` - Fires for individual variations within variable products
|
||||
|
||||
**Lesson:** When adding admin UI for variable product parents, use `woocommerce_product_options_general_product_data` and check `$product->is_type('variable')` to conditionally display. Using `woocommerce_product_options_pricing` will cause forms to never appear for variable products (as discovered in v1.2.6 → v1.2.7 fix).
|
||||
|
||||
#### CRITICAL: Currency Symbol Display (Learned in v1.2.8, Corrected in v1.2.9)
|
||||
|
||||
When displaying currency symbols in admin interface table headers and input placeholders:
|
||||
|
||||
**Table Headers:**
|
||||
|
||||
```php
|
||||
// ✅ Correct - Use printf with esc_html__ for translation (CORRECTED in v1.2.9)
|
||||
<th><?php printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
|
||||
|
||||
// ❌ Wrong - Hard-coded or missing currency
|
||||
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th>
|
||||
<th><?php _e('Price (€)', 'wc-tier-package-prices'); ?></th>
|
||||
```
|
||||
|
||||
**Twig Template Placeholders:**
|
||||
|
||||
```twig
|
||||
{# ✅ Correct - Pass currency_symbol from PHP and concatenate (CORRECTED in v1.2.9 - no translation filter) #}
|
||||
placeholder="{{ 'e.g., 9.99 ' ~ currency_symbol }}"
|
||||
|
||||
{# ❌ Wrong - Hard-coded or missing currency #}
|
||||
placeholder="{{ 'e.g., 9.99'|__('wc-tier-package-prices') }}"
|
||||
placeholder="{{ 'e.g., 9.99 €'|__('wc-tier-package-prices') }}"
|
||||
```
|
||||
|
||||
**Implementation Pattern:**
|
||||
|
||||
1. In PHP render methods, pass currency symbol to Twig: `'currency_symbol' => get_woocommerce_currency_symbol()`
|
||||
2. In Twig templates, concatenate using `~` operator: `'text ' ~ currency_symbol`
|
||||
3. Always use WooCommerce's `get_woocommerce_currency_symbol()` - never hard-code currency symbols
|
||||
|
||||
**Affected Methods:** All template render methods must pass `currency_symbol`:
|
||||
|
||||
- `render_tier_row()`
|
||||
- `render_package_row()`
|
||||
- `render_variation_tier_row()`
|
||||
- `render_variation_package_row()`
|
||||
|
||||
#### CRITICAL: Post Meta Deletion vs. Empty Arrays (Learned in v1.2.8)
|
||||
|
||||
When saving product meta data, WordPress distinguishes between "no data" (deleted meta) and "empty data" (empty array saved as meta):
|
||||
|
||||
**Problem Pattern:**
|
||||
|
||||
```php
|
||||
// ❌ WRONG - Saves empty array when all entries removed
|
||||
if (isset($_POST['_wc_tpp_tiers'])) {
|
||||
$tiers = array();
|
||||
foreach ($_POST['_wc_tpp_tiers'] as $tier) {
|
||||
if (!empty($tier['min_qty']) && !empty($tier['price'])) {
|
||||
$tiers[] = array(...);
|
||||
}
|
||||
}
|
||||
update_post_meta($post_id, '_wc_tpp_tiers', $tiers); // Saves [] if no valid entries
|
||||
} else {
|
||||
delete_post_meta($post_id, '_wc_tpp_tiers');
|
||||
}
|
||||
```
|
||||
|
||||
**Correct Pattern:**
|
||||
|
||||
```php
|
||||
// ✅ CORRECT - Deletes meta when no valid entries exist
|
||||
if (isset($_POST['_wc_tpp_tiers'])) {
|
||||
$tiers = array();
|
||||
foreach ($_POST['_wc_tpp_tiers'] as $tier) {
|
||||
if (!empty($tier['min_qty']) && !empty($tier['price'])) {
|
||||
$tiers[] = array(...);
|
||||
}
|
||||
}
|
||||
// Only save if we have valid entries, otherwise delete
|
||||
if (!empty($tiers)) {
|
||||
update_post_meta($post_id, '_wc_tpp_tiers', $tiers);
|
||||
} else {
|
||||
delete_post_meta($post_id, '_wc_tpp_tiers');
|
||||
}
|
||||
} else {
|
||||
delete_post_meta($post_id, '_wc_tpp_tiers');
|
||||
}
|
||||
```
|
||||
|
||||
**Why This Matters:**
|
||||
|
||||
- Empty arrays `[]` saved via `update_post_meta()` persist in database as serialized empty arrays
|
||||
- Frontend/cart code checking `if ($tiers)` will evaluate `[]` as falsy, but meta still exists in database
|
||||
- Database queries like `get_post_meta()` return `[]` instead of `false`, causing subtle bugs
|
||||
- Properly deleting meta keeps database clean and prevents "ghost" configurations
|
||||
|
||||
**Affected Methods in v1.2.8:**
|
||||
|
||||
- `save_tier_package_fields()` - Simple and variable parent products
|
||||
- `save_variation_pricing_fields()` - Individual variations
|
||||
|
||||
**Rule:** Always check `if (!empty($array))` before calling `update_post_meta()` for array data. If empty, call `delete_post_meta()` instead.
|
||||
|
||||
#### CRITICAL: WordPress Translation Functions with printf (Learned in v1.2.9)
|
||||
|
||||
When using `printf()` with WordPress translation functions, the text domain must be passed to the translation function, NOT to printf:
|
||||
|
||||
**Wrong Pattern:**
|
||||
|
||||
```php
|
||||
// ❌ WRONG - Text domain not passed to translation function
|
||||
printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol());
|
||||
```
|
||||
|
||||
**Problem:** The `__()` function receives the text domain as a second parameter, but in this pattern it's missing. This causes the string "Price (%s)" to not be found in translation files, so it won't be translated.
|
||||
|
||||
**Correct Pattern:**
|
||||
|
||||
```php
|
||||
// ✅ CORRECT - Text domain in translation function, with esc_html for output escaping
|
||||
printf(esc_html__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol());
|
||||
```
|
||||
|
||||
**Why This Works:**
|
||||
|
||||
- `esc_html__('Price (%s)', 'wc-tier-package-prices')` translates the string and returns it
|
||||
- `printf()` then substitutes the `%s` placeholder with the currency symbol
|
||||
- The translated string is used in the final output
|
||||
- `esc_html` ensures proper output escaping
|
||||
|
||||
**Applied in v1.2.9:** All 6 table headers in `includes/class-wc-tpp-product-meta.php`
|
||||
|
||||
#### CRITICAL: Twig Translation Filters and HTML Entity Encoding (Learned in v1.2.9)
|
||||
|
||||
When concatenating dynamic values in Twig templates, applying the translation filter can cause HTML entity encoding issues:
|
||||
|
||||
**Wrong Pattern:**
|
||||
|
||||
```twig
|
||||
{# ❌ WRONG - Translation filter encodes special characters in concatenated string #}
|
||||
placeholder="{{ ('e.g., 9.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }}"
|
||||
```
|
||||
|
||||
**Problem:** When `currency_symbol` contains special characters (€, £, ¥, etc.), the concatenated string is passed through the translation function which treats it as a translatable string and encodes special characters as HTML entities (`€`, `£`, etc.).
|
||||
|
||||
**Correct Pattern:**
|
||||
|
||||
```twig
|
||||
{# ✅ CORRECT - No translation filter, just concatenation #}
|
||||
placeholder="{{ 'e.g., 9.99 ' ~ currency_symbol }}"
|
||||
```
|
||||
|
||||
**Why This Works:**
|
||||
|
||||
- Placeholder examples don't need translation - they're illustrative values
|
||||
- Direct concatenation preserves special characters
|
||||
- Currency symbol displays correctly (€ instead of €)
|
||||
|
||||
**Rule:** Only apply translation filters to static text that needs translation, not to concatenated strings with dynamic values that contain special characters.
|
||||
|
||||
**Applied in v1.2.9:**
|
||||
|
||||
- `templates/admin/tier-row.twig` - Price input placeholder
|
||||
- `templates/admin/package-row.twig` - Price input placeholder
|
||||
|
||||
#### CRITICAL: Defensive Programming for POST Data Processing (Learned in v1.2.9)
|
||||
|
||||
The v1.2.8 fix for variation pricing deletion had the right logic but used a branching structure that could miss edge cases. The v1.2.9 refactor demonstrates a more defensive pattern:
|
||||
|
||||
**Less Defensive Pattern (v1.2.8):**
|
||||
|
||||
```php
|
||||
// ❌ BRITTLE - Multiple branches, easy to miss edge cases
|
||||
if (isset($_POST['wc_tpp_tiers'][$loop])) {
|
||||
$tiers = array();
|
||||
foreach ($_POST['wc_tpp_tiers'][$loop] as $tier) {
|
||||
// ... populate tiers ...
|
||||
}
|
||||
if (!empty($tiers)) {
|
||||
update_post_meta($variation_id, '_wc_tpp_tiers', $tiers);
|
||||
} else {
|
||||
delete_post_meta($variation_id, '_wc_tpp_tiers');
|
||||
}
|
||||
} else {
|
||||
delete_post_meta($variation_id, '_wc_tpp_tiers');
|
||||
}
|
||||
```
|
||||
|
||||
**Problem:** Two separate code paths to `delete_post_meta()`. If logic changes, easy to update one path but forget the other.
|
||||
|
||||
**More Defensive Pattern (v1.2.9):**
|
||||
|
||||
```php
|
||||
// ✅ DEFENSIVE - Single decision point, guaranteed cleanup
|
||||
$tiers = array();
|
||||
if (isset($_POST['wc_tpp_tiers'][$loop]) && is_array($_POST['wc_tpp_tiers'][$loop])) {
|
||||
foreach ($_POST['wc_tpp_tiers'][$loop] as $tier) {
|
||||
// ... populate tiers ...
|
||||
}
|
||||
}
|
||||
// Always execute one of these based on final state
|
||||
if (!empty($tiers)) {
|
||||
update_post_meta($variation_id, '_wc_tpp_tiers', $tiers);
|
||||
} else {
|
||||
delete_post_meta($variation_id, '_wc_tpp_tiers');
|
||||
}
|
||||
```
|
||||
|
||||
**Why This Is Better:**
|
||||
|
||||
- Initialize array at the start (guaranteed initial state)
|
||||
- Single conditional for populating (with extra `is_array()` safety check)
|
||||
- Single decision point for save/delete (one place to maintain)
|
||||
- Impossible to have a code path that doesn't call either update or delete
|
||||
- Much easier to reason about and modify
|
||||
|
||||
**Key Principles:**
|
||||
|
||||
1. **Initialize variables early** - Establish known initial state
|
||||
2. **Minimize branching** - Fewer code paths = fewer bugs
|
||||
3. **Single decision point** - One place determines final action
|
||||
4. **Add safety checks** - Validate assumptions (`is_array()`)
|
||||
5. **Guaranteed execution** - Always perform one of update/delete, never neither
|
||||
|
||||
**Applied in v1.2.9:**
|
||||
|
||||
- `save_variation_pricing_fields()` - Both tier and package pricing logic refactored
|
||||
|
||||
**Rule:** When processing user input to decide between update and delete, prefer the pattern: initialize → conditionally populate → unconditionally act based on final state.
|
||||
|
||||
### When Adding Features
|
||||
|
||||
- Follow the existing pattern: add setting → add UI → add logic → add template
|
||||
@@ -620,8 +1101,8 @@ Roadmap for the upcoming development.
|
||||
|
||||
### PHP
|
||||
|
||||
- Minimum: 7.4
|
||||
- Uses modern PHP features (type hints acceptable in new code)
|
||||
- Minimum: 8.3 (breaking change in v1.3.0)
|
||||
- Uses modern PHP features (type hints, named arguments, etc.)
|
||||
- Composer autoloader handles namespacing
|
||||
|
||||
### Browsers
|
||||
@@ -632,7 +1113,7 @@ Roadmap for the upcoming development.
|
||||
|
||||
## Support & Resources
|
||||
|
||||
- **Repository:** https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices
|
||||
- **Repository:** <https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices>
|
||||
- **Documentation:** See `README.md`, `QUICKSTART.md`, `USAGE_EXAMPLES.md`, `INSTALLATION.md`
|
||||
- **Changelog:** `CHANGELOG.md` (detailed version history)
|
||||
- **Issue Tracking:** Check fatal-errors-*.log files for production errors
|
||||
@@ -657,4 +1138,84 @@ The plugin architecture is solid and well-tested. Most bugs arise from:
|
||||
|
||||
---
|
||||
|
||||
## Session History
|
||||
|
||||
### v1.3.0 Release Session (2026-01-25)
|
||||
|
||||
**Accomplished:**
|
||||
|
||||
1. Fixed known bugs from CLAUDE.md:
|
||||
- Removed all releases from git tracking (`git rm --cached -r releases/`)
|
||||
- Deleted all MD5 checksum files, keeping only SHA256
|
||||
- Updated `.gitignore` to exclude `/releases/`
|
||||
|
||||
2. Implemented v1.3.0 features:
|
||||
- Bumped PHP requirement from 7.4 to 8.3 (breaking change)
|
||||
- Added PHP version check with admin notice for incompatible servers
|
||||
- Added `magdev/wc-licensed-product-client` library integration
|
||||
- Refactored settings page to use WooCommerce modern sub-tabs pattern (`get_own_sections()`)
|
||||
- Created "General" and "License" sub-tabs
|
||||
- Implemented license management with AJAX validation/activation
|
||||
- Added license status caching via WordPress transients
|
||||
- Added CSS styling for license status display
|
||||
|
||||
3. Updated all translation files:
|
||||
- Added 28 new translation strings for license management
|
||||
- Updated .pot template and all 7 .po files
|
||||
- Compiled all .mo files
|
||||
|
||||
4. Created release package:
|
||||
- Package size: 737KB (increased due to new dependencies)
|
||||
- 642 files included (more due to license client library)
|
||||
- SHA256 checksum generated
|
||||
|
||||
**Key Learnings:**
|
||||
|
||||
- WooCommerce settings sub-tabs use `get_own_sections()` and `get_settings_for_{section}_section()` pattern
|
||||
- License client library `magdev/wc-licensed-product-client` is from private Gitea repo - requires `repositories` config in composer.json
|
||||
- Package version was `^0.1` not `^1.0` - always check available versions before setting constraint
|
||||
- Release package size increased from ~430KB to ~737KB due to new Composer dependencies
|
||||
|
||||
### v1.3.1 Release Session (2026-01-27)
|
||||
|
||||
**Accomplished:**
|
||||
|
||||
1. Reviewed server-implementation.md from license client library
|
||||
2. Upgraded from `LicenseClient` to `SecureLicenseClient`:
|
||||
- Added "Server Secret" configuration field in License settings tab
|
||||
- Updated `get_license_client()` to use `SecureLicenseClient` with HMAC-SHA256 signature verification
|
||||
- Updated JavaScript to pass `server_secret` in AJAX requests
|
||||
|
||||
3. Added comprehensive exception handling:
|
||||
- `RateLimitExceededException` - Shows retry time to user
|
||||
- `SignatureException` - Reports signature verification failures
|
||||
- `InvalidArgumentException` - SSRF protection for private IP blocking
|
||||
|
||||
4. Updated Composer dependencies:
|
||||
- Changed `magdev/wc-licensed-product-client` from `^0.1` to `dev-main`
|
||||
- Library includes new security features (SSRF protection, response signatures)
|
||||
|
||||
5. Updated translations:
|
||||
- Added 7 new translation strings for v1.3.1
|
||||
- All 7 language files updated and compiled
|
||||
|
||||
6. Created release package:
|
||||
- Package size: 745KB
|
||||
- 642 files included
|
||||
- SHA256 checksum generated
|
||||
|
||||
7. Updated README.md:
|
||||
- Documented license management feature
|
||||
- Updated PHP requirement to 8.3
|
||||
- Added v1.3.0 and v1.3.1 changelog entries
|
||||
|
||||
**Key Learnings:**
|
||||
|
||||
- `SecureLicenseClient` requires `serverSecret` parameter for HMAC verification
|
||||
- The library's exception classes are in `Magdev\WcLicensedProductClient\Exception\` and `Magdev\WcLicensedProductClient\Security\` namespaces
|
||||
- `RateLimitExceededException` has a `retryAfter` property (int seconds)
|
||||
- Always check library documentation in `docs/server-implementation.md` for implementation requirements
|
||||
|
||||
---
|
||||
|
||||
Always refer to this document when starting work on this project. Good luck!
|
||||
|
||||
50
README.md
50
README.md
@@ -20,10 +20,11 @@ A powerful WooCommerce plugin that adds tier pricing and package pricing functio
|
||||
|
||||
### Admin Features
|
||||
- Easy-to-use product meta boxes for adding tiers and packages
|
||||
- Global settings page under WooCommerce menu
|
||||
- Global settings page under WooCommerce menu with sub-tabs
|
||||
- Configure display position (before/after add to cart, after price)
|
||||
- Enable/disable tier or package pricing independently
|
||||
- Sortable pricing rules
|
||||
- License management with secure HMAC signature verification
|
||||
|
||||
### Frontend Features
|
||||
- Beautiful pricing tables on product pages
|
||||
@@ -110,7 +111,7 @@ When editing a product, scroll to the **Product data** panel:
|
||||
|
||||
```
|
||||
wc-tier-and-package-prices/
|
||||
├── wc-tier-and-package-prices.php # Main plugin file (v1.1.20)
|
||||
├── wc-tier-and-package-prices.php # Main plugin file (v1.3.1)
|
||||
├── includes/
|
||||
│ ├── class-wc-tpp-admin.php # Admin settings integration
|
||||
│ ├── class-wc-tpp-settings.php # WooCommerce settings page
|
||||
@@ -149,7 +150,7 @@ wc-tier-and-package-prices/
|
||||
|
||||
- WordPress 6.0 or higher (tested up to 6.9.x)
|
||||
- WooCommerce 8.0 or higher (tested up to 10.x)
|
||||
- PHP 7.4 or higher
|
||||
- PHP 8.3 or higher (required since v1.3.0)
|
||||
|
||||
### Compatibility
|
||||
|
||||
@@ -183,9 +184,32 @@ This plugin is licensed under the GPL v2 or later.
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 1.3.1 - 2026-01-27
|
||||
|
||||
__Current Release__ - Secure License Client
|
||||
|
||||
- __Changed__: Switched to `SecureLicenseClient` with HMAC-SHA256 response signature verification
|
||||
- __New__: Server Secret configuration field for secure communication with license server
|
||||
- __New__: Rate limit exception handling with retry time display
|
||||
- __New__: Signature verification and URL validation error handling
|
||||
- __Security__: Response signatures verified using HMAC-SHA256 with license-specific derived keys
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for complete details.
|
||||
|
||||
### Version 1.3.0 - 2026-01-25
|
||||
|
||||
__Breaking Changes__ - PHP 8.3+ Required
|
||||
|
||||
- __Breaking__: Minimum PHP version increased from 7.4 to 8.3
|
||||
- __New__: License management via `magdev/wc-licensed-product-client` library
|
||||
- __New__: Settings page split into "General" and "License" sub-tabs
|
||||
- __New__: AJAX-based license validation and activation with visual feedback
|
||||
- __New__: License status caching with daily auto-refresh
|
||||
- __New__: PHP version check with admin notice for incompatible servers
|
||||
|
||||
### Version 1.2.0 - 2025-12-29
|
||||
|
||||
__Current Release__ - Variable Product Support
|
||||
Variable Product Support
|
||||
|
||||
- __New__: Full support for WooCommerce variable products with variation-level pricing
|
||||
- __New__: Each variation can have independent tier and package pricing configuration
|
||||
@@ -194,24 +218,6 @@ __Current Release__ - Variable Product Support
|
||||
- __Fixed__: Quantity restrictions now work correctly per-variation
|
||||
- 100% backward compatible - no breaking changes
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for complete details.
|
||||
|
||||
### Version 1.1.22 - 2025-12-23
|
||||
|
||||
- Increased width of label input fields in admin interface
|
||||
|
||||
#### Fixed
|
||||
- **CRITICAL:** WooCommerce Blocks fatal error in mini-cart and cart blocks
|
||||
- Fixed `woocommerce_store_api_product_quantity_editable` filter signature mismatch
|
||||
- Filter now correctly accepts `WC_Product` object instead of cart item array
|
||||
- Resolves "Cannot use object of type WC_Product_Simple as array" fatal error
|
||||
|
||||
#### Technical Details
|
||||
- Updated `block_quantity_editable()` method signature to accept product object
|
||||
- Changed parameter from `$cart_item` array to `WC_Product $product`
|
||||
- Uses `$product->get_id()` instead of array access for product ID
|
||||
- Full compatibility with WooCommerce Store API and block-based cart/checkout
|
||||
|
||||
### Recent Major Updates
|
||||
|
||||
#### Version 1.1.7 - Enhanced Tier Pricing
|
||||
|
||||
@@ -23,42 +23,63 @@
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Table styling - borderless design for all tier/package tables */
|
||||
.wc-tpp-tiers-table,
|
||||
.wc-tpp-packages-table {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
border: none !important;
|
||||
border-collapse: collapse !important;
|
||||
}
|
||||
|
||||
.wc-tpp-tiers-table th,
|
||||
.wc-tpp-packages-table th,
|
||||
.wc-tpp-tiers-table td,
|
||||
.wc-tpp-packages-table td {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.wc-tpp-tiers-table th {
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wc-tpp-packages-table th {
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wc-tpp-tiers-table thead,
|
||||
.wc-tpp-packages-table thead,
|
||||
.wc-tpp-tiers-table tbody,
|
||||
.wc-tpp-packages-table tbody,
|
||||
.wc-tpp-tiers-table tr,
|
||||
.wc-tpp-packages-table tr {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* Table row styling - rows are now <tr> elements in a table */
|
||||
.wc-tpp-tier-row,
|
||||
.wc-tpp-package-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: flex-end;
|
||||
padding: 15px;
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
/* No special styling needed - standard table row */
|
||||
}
|
||||
|
||||
.wc-tpp-tier-row .form-field,
|
||||
.wc-tpp-package-row .form-field {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.wc-tpp-tier-row label,
|
||||
.wc-tpp-package-row label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
.wc-tpp-tier-row td,
|
||||
.wc-tpp-package-row td {
|
||||
padding: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Ensure WooCommerce input classes work properly in table cells */
|
||||
.wc-tpp-tier-row input,
|
||||
.wc-tpp-package-row input {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.wc-tpp-remove-tier,
|
||||
.wc-tpp-remove-package {
|
||||
flex-shrink: 0;
|
||||
color: #b32d2e;
|
||||
border-color: #b32d2e;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.wc-tpp-remove-tier:hover,
|
||||
@@ -89,3 +110,96 @@
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Hide table headers when there are no pricing rules */
|
||||
/* Default: hide headers initially, JavaScript will show them when rows are added */
|
||||
.wc-tpp-tiers-table thead,
|
||||
.wc-tpp-packages-table thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show headers when table has pricing rows */
|
||||
.wc-tpp-tiers-table.has-rows thead,
|
||||
.wc-tpp-packages-table.has-rows thead {
|
||||
display: table-header-group !important;
|
||||
}
|
||||
|
||||
/* Checkbox styling improvements */
|
||||
#_wc_tpp_restrict_to_packages,
|
||||
input[id^="wc_tpp_restrict_to_packages_"] {
|
||||
margin-right: 12px !important;
|
||||
}
|
||||
|
||||
/* Position help tip icon right next to the label text */
|
||||
.wc-tpp-tier-pricing .woocommerce-help-tip,
|
||||
.wc-tpp-package-pricing .woocommerce-help-tip,
|
||||
.wc-tpp-variation-pricing .woocommerce-help-tip {
|
||||
margin-left: 6px;
|
||||
margin-right: 0;
|
||||
float: none;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Fix WooCommerce checkbox label layout for our checkboxes */
|
||||
label[for="_wc_tpp_restrict_to_packages"],
|
||||
label[for^="wc_tpp_restrict_to_packages_"] {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* Hide inline description text when tooltip is shown */
|
||||
#_wc_tpp_restrict_to_packages + .description,
|
||||
input[id^="wc_tpp_restrict_to_packages_"] + .description {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* License Status Styling */
|
||||
.wc-tpp-license-active {
|
||||
color: #46b450;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wc-tpp-license-inactive {
|
||||
color: #dc3232;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wc-tpp-license-expired {
|
||||
color: #ffb900;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#wc-tpp-license-spinner {
|
||||
float: none;
|
||||
margin-top: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#wc-tpp-validate-license,
|
||||
#wc-tpp-activate-license {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
#wc-tpp-license-status-container {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px 15px;
|
||||
background: #f9f9f9;
|
||||
border-left: 4px solid #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
#wc-tpp-license-status-container.valid {
|
||||
border-left-color: #46b450;
|
||||
background: #f0fff0;
|
||||
}
|
||||
|
||||
#wc-tpp-license-status-container.invalid {
|
||||
border-left-color: #dc3232;
|
||||
background: #fff0f0;
|
||||
}
|
||||
|
||||
#wc-tpp-license-status-container small {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,28 @@
|
||||
let tierIndex = $('.wc-tpp-tier-pricing .wc-tpp-tier-row').length;
|
||||
let packageIndex = $('.wc-tpp-package-pricing .wc-tpp-package-row').length;
|
||||
|
||||
// Function to update table header visibility
|
||||
function updateTableHeaders() {
|
||||
// Check all tier tables
|
||||
$('.wc-tpp-tiers-table').each(function() {
|
||||
const $table = $(this);
|
||||
const $tbody = $table.find('.wc-tpp-tiers-container');
|
||||
const hasRows = $tbody.find('tr').length > 0;
|
||||
$table.toggleClass('has-rows', hasRows);
|
||||
});
|
||||
|
||||
// Check all package tables
|
||||
$('.wc-tpp-packages-table').each(function() {
|
||||
const $table = $(this);
|
||||
const $tbody = $table.find('.wc-tpp-packages-container');
|
||||
const hasRows = $tbody.find('tr').length > 0;
|
||||
$table.toggleClass('has-rows', hasRows);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize table headers on page load
|
||||
updateTableHeaders();
|
||||
|
||||
// ========================================
|
||||
// Simple Product Handlers
|
||||
// ========================================
|
||||
@@ -21,6 +43,7 @@
|
||||
const newRow = template.replace(/\{\{INDEX\}\}/g, tierIndex);
|
||||
$('.wc-tpp-tier-pricing .wc-tpp-tiers-container').append(newRow);
|
||||
tierIndex++;
|
||||
updateTableHeaders();
|
||||
});
|
||||
|
||||
// Add package (simple products)
|
||||
@@ -30,6 +53,31 @@
|
||||
const newRow = template.replace(/\{\{INDEX\}\}/g, packageIndex);
|
||||
$('.wc-tpp-package-pricing .wc-tpp-packages-container').append(newRow);
|
||||
packageIndex++;
|
||||
updateTableHeaders();
|
||||
});
|
||||
|
||||
// ========================================
|
||||
// Variable Product Parent Handlers
|
||||
// ========================================
|
||||
|
||||
// Add tier (variable product parent default pricing)
|
||||
$('.wc-tpp-variable-parent-pricing .wc-tpp-add-tier').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
const template = $('#wc-tpp-tier-row-template').html();
|
||||
const newRow = template.replace(/\{\{INDEX\}\}/g, tierIndex);
|
||||
$('.wc-tpp-variable-parent-pricing .wc-tpp-tiers-container').append(newRow);
|
||||
tierIndex++;
|
||||
updateTableHeaders();
|
||||
});
|
||||
|
||||
// Add package (variable product parent default pricing)
|
||||
$('.wc-tpp-variable-parent-pricing .wc-tpp-add-package').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
const template = $('#wc-tpp-package-row-template').html();
|
||||
const newRow = template.replace(/\{\{INDEX\}\}/g, packageIndex);
|
||||
$('.wc-tpp-variable-parent-pricing .wc-tpp-packages-container').append(newRow);
|
||||
packageIndex++;
|
||||
updateTableHeaders();
|
||||
});
|
||||
|
||||
// ========================================
|
||||
@@ -50,6 +98,7 @@
|
||||
const newRow = template.replace(/\{\{INDEX\}\}/g, currentIndex);
|
||||
|
||||
$tbody.append(newRow);
|
||||
updateTableHeaders();
|
||||
});
|
||||
|
||||
// Add package (variations)
|
||||
@@ -66,6 +115,7 @@
|
||||
const newRow = template.replace(/\{\{INDEX\}\}/g, currentIndex);
|
||||
|
||||
$tbody.append(newRow);
|
||||
updateTableHeaders();
|
||||
});
|
||||
|
||||
// ========================================
|
||||
@@ -77,6 +127,7 @@
|
||||
e.preventDefault();
|
||||
if (confirm('Are you sure you want to remove this tier?')) {
|
||||
$(this).closest('.wc-tpp-tier-row').remove();
|
||||
updateTableHeaders();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -85,6 +136,7 @@
|
||||
e.preventDefault();
|
||||
if (confirm('Are you sure you want to remove this package?')) {
|
||||
$(this).closest('.wc-tpp-package-row').remove();
|
||||
updateTableHeaders();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "magdev/wc-tier-package-prices",
|
||||
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.1",
|
||||
"type": "wordpress-plugin",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"authors": [
|
||||
@@ -10,9 +10,16 @@
|
||||
"homepage": "https://src.bundespruefstelle.ch/magdev"
|
||||
}
|
||||
],
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://src.bundespruefstelle.ch/magdev/wc-licensed-product-client.git"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"twig/twig": "^3.0"
|
||||
"php": ">=8.3",
|
||||
"twig/twig": "^3.0",
|
||||
"magdev/wc-licensed-product-client": "dev-main"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
|
||||
@@ -50,10 +50,10 @@ if (!class_exists('WC_TPP_Cart')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for exact package match first (use effective ID for variations)
|
||||
// Check for exact package match first (pass product_id for parent fallback support)
|
||||
$package_price = null;
|
||||
if (get_option('wc_tpp_enable_package_pricing') === 'yes') {
|
||||
$package_price = WC_TPP_Frontend::get_package_price($effective_id, $quantity, $variation_id);
|
||||
$package_price = WC_TPP_Frontend::get_package_price($product_id, $quantity, $variation_id);
|
||||
}
|
||||
|
||||
if ($package_price !== null) {
|
||||
@@ -64,9 +64,9 @@ if (!class_exists('WC_TPP_Cart')) {
|
||||
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_pricing_type'] = 'package';
|
||||
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_total_price'] = $package_price;
|
||||
} else {
|
||||
// Apply tier pricing if no package match (use effective ID for variations)
|
||||
// Apply tier pricing if no package match (pass product_id for parent fallback support)
|
||||
if (get_option('wc_tpp_enable_tier_pricing') === 'yes') {
|
||||
$tier_price = WC_TPP_Frontend::get_tier_price($effective_id, $quantity, $variation_id);
|
||||
$tier_price = WC_TPP_Frontend::get_tier_price($product_id, $quantity, $variation_id);
|
||||
if ($tier_price !== null) {
|
||||
$product->set_price($tier_price);
|
||||
// Store pricing information in cart item for display
|
||||
@@ -103,20 +103,16 @@ if (!class_exists('WC_TPP_Cart')) {
|
||||
public function validate_package_quantity($passed, $product_id, $quantity) {
|
||||
// Check for variation ID in request (for variable products)
|
||||
$variation_id = isset($_REQUEST['variation_id']) ? absint($_REQUEST['variation_id']) : 0;
|
||||
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||
|
||||
// Check if restriction is enabled globally or for this product/variation
|
||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
||||
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||
|
||||
if (!$global_restrict && !$product_restrict) {
|
||||
// Check if restriction is enabled (with parent fallback for variations)
|
||||
if (!$this->is_restriction_enabled($product_id, $variation_id)) {
|
||||
return $passed;
|
||||
}
|
||||
|
||||
// Get packages for this product/variation
|
||||
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
|
||||
// Get packages for this product/variation (with parent fallback)
|
||||
$packages = $this->get_packages_with_fallback($product_id, $variation_id);
|
||||
|
||||
if (empty($packages) || !is_array($packages)) {
|
||||
if (!$packages) {
|
||||
return $passed;
|
||||
}
|
||||
|
||||
@@ -156,15 +152,8 @@ if (!class_exists('WC_TPP_Cart')) {
|
||||
$variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0;
|
||||
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||
|
||||
// Check if restriction is enabled globally or for this product/variation
|
||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
||||
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||
|
||||
// Get packages for this product/variation
|
||||
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
|
||||
|
||||
// If restriction is enabled and packages exist, show quantity as text only
|
||||
if (($global_restrict || $product_restrict) && !empty($packages)) {
|
||||
// Check if restriction is enabled (with parent fallback) and packages exist
|
||||
if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
|
||||
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s</span>',
|
||||
$effective_id,
|
||||
$cart_item['quantity']
|
||||
@@ -179,15 +168,8 @@ if (!class_exists('WC_TPP_Cart')) {
|
||||
$variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0;
|
||||
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||
|
||||
// Check if restriction is enabled globally or for this product/variation
|
||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
||||
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||
|
||||
// Get packages for this product/variation
|
||||
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
|
||||
|
||||
// If restriction is enabled and packages exist, show quantity as text only
|
||||
if (($global_restrict || $product_restrict) && !empty($packages)) {
|
||||
// Check if restriction is enabled (with parent fallback) and packages exist
|
||||
if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
|
||||
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s ×</span>',
|
||||
$effective_id,
|
||||
$cart_item['quantity']
|
||||
@@ -209,11 +191,8 @@ if (!class_exists('WC_TPP_Cart')) {
|
||||
$variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0;
|
||||
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||
|
||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
||||
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
|
||||
|
||||
if (($global_restrict || $product_restrict) && !empty($packages)) {
|
||||
// Check if restriction is enabled (with parent fallback) and packages exist
|
||||
if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
|
||||
$restricted_products[] = $effective_id;
|
||||
}
|
||||
}
|
||||
@@ -254,20 +233,61 @@ if (!class_exists('WC_TPP_Cart')) {
|
||||
return $editable;
|
||||
}
|
||||
|
||||
// For variations, use the variation ID directly (get_id() returns variation ID for WC_Product_Variation)
|
||||
$effective_id = $product_id;
|
||||
// For variations, get parent product ID and variation ID
|
||||
$variation_id = 0;
|
||||
$parent_id = $product_id;
|
||||
|
||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
||||
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
|
||||
if ($product->is_type('variation')) {
|
||||
$variation_id = $product_id;
|
||||
$parent_id = $product->get_parent_id();
|
||||
}
|
||||
|
||||
// If restriction is enabled and packages exist, make quantity non-editable
|
||||
if (($global_restrict || $product_restrict) && !empty($packages)) {
|
||||
// Check if restriction is enabled (with parent fallback) and packages exist
|
||||
if ($this->is_restriction_enabled($parent_id, $variation_id) && $this->get_packages_with_fallback($parent_id, $variation_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $editable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get packages with parent fallback for variations
|
||||
*
|
||||
* @param int $product_id Parent product ID
|
||||
* @param int $variation_id Variation ID (0 for simple products)
|
||||
* @return array|false Packages array or false if none found
|
||||
*/
|
||||
private function get_packages_with_fallback($product_id, $variation_id = 0) {
|
||||
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
|
||||
|
||||
// Fall back to parent pricing if variation doesn't have its own pricing
|
||||
if ((empty($packages) || !is_array($packages)) && $variation_id > 0) {
|
||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
||||
}
|
||||
|
||||
return (!empty($packages) && is_array($packages)) ? $packages : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if restriction is enabled for a product/variation with parent fallback
|
||||
*
|
||||
* @param int $product_id Parent product ID
|
||||
* @param int $variation_id Variation ID (0 for simple products)
|
||||
* @return bool Whether restriction is enabled
|
||||
*/
|
||||
private function is_restriction_enabled($product_id, $variation_id = 0) {
|
||||
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
||||
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||
|
||||
// Fall back to parent restriction setting if variation doesn't have its own
|
||||
if (!$product_restrict && $variation_id > 0) {
|
||||
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||
}
|
||||
|
||||
return $global_restrict || $product_restrict;
|
||||
}
|
||||
}
|
||||
|
||||
new WC_TPP_Cart();
|
||||
|
||||
@@ -133,6 +133,11 @@ if (!class_exists('WC_TPP_Frontend')) {
|
||||
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||
$tiers = get_post_meta($effective_id, '_wc_tpp_tiers', true);
|
||||
|
||||
// Fall back to parent pricing if variation doesn't have its own pricing
|
||||
if ((empty($tiers) || !is_array($tiers)) && $variation_id > 0) {
|
||||
$tiers = get_post_meta($product_id, '_wc_tpp_tiers', true);
|
||||
}
|
||||
|
||||
if (empty($tiers) || !is_array($tiers)) {
|
||||
return null;
|
||||
}
|
||||
@@ -159,6 +164,11 @@ if (!class_exists('WC_TPP_Frontend')) {
|
||||
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
|
||||
|
||||
// Fall back to parent pricing if variation doesn't have its own pricing
|
||||
if ((empty($packages) || !is_array($packages)) && $variation_id > 0) {
|
||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
||||
}
|
||||
|
||||
if (empty($packages) || !is_array($packages)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,128 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
add_action('woocommerce_product_options_pricing', array($this, 'add_package_pricing_fields'));
|
||||
add_action('woocommerce_process_product_meta', array($this, 'save_tier_package_fields'));
|
||||
|
||||
// Variable product parent hooks (for default pricing)
|
||||
// Use product_options_general_product_data which shows for all product types after the general tab
|
||||
add_action('woocommerce_product_options_general_product_data', array($this, 'add_variable_parent_pricing_fields'));
|
||||
|
||||
// Variable product variation hooks
|
||||
add_action('woocommerce_variation_options_pricing', array($this, 'add_variation_pricing_fields'), 10, 3);
|
||||
add_action('woocommerce_save_product_variation', array($this, 'save_variation_pricing_fields'), 10, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tier and package pricing fields for variable product parents
|
||||
* These serve as defaults for all variations unless overridden
|
||||
*/
|
||||
public function add_variable_parent_pricing_fields() {
|
||||
global $post;
|
||||
|
||||
// Only show for variable products, not simple products
|
||||
$product = wc_get_product($post->ID);
|
||||
if (!$product || !$product->is_type('variable')) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="options_group wc-tpp-variable-parent-pricing">
|
||||
<p class="form-field">
|
||||
<strong><?php _e('Default Tier & Package Pricing for All Variations', 'wc-tier-package-prices'); ?></strong>
|
||||
<span class="woocommerce-help-tip" data-tip="<?php esc_attr_e('Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing.', 'wc-tier-package-prices'); ?>"></span>
|
||||
</p>
|
||||
<p class="description" style="margin-left: 12px; margin-bottom: 15px;">
|
||||
<?php _e('Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing.', 'wc-tier-package-prices'); ?>
|
||||
</p>
|
||||
|
||||
<!-- Tier Pricing Section -->
|
||||
<div class="wc-tpp-parent-tier-pricing" style="margin-left: 12px;">
|
||||
<p><strong><?php _e('Tier Pricing', 'wc-tier-package-prices'); ?></strong></p>
|
||||
<table class="widefat wc-tpp-tiers-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></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></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="wc-tpp-tiers-container">
|
||||
<?php
|
||||
$tiers = get_post_meta($post->ID, '_wc_tpp_tiers', true);
|
||||
if (!is_array($tiers)) {
|
||||
$tiers = array();
|
||||
}
|
||||
|
||||
foreach ($tiers as $index => $tier) {
|
||||
$this->render_tier_row($index, $tier);
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="form-field">
|
||||
<button type="button" class="button wc-tpp-add-tier"><?php _e('Add Tier', 'wc-tier-package-prices'); ?></button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Package Pricing Section -->
|
||||
<div class="wc-tpp-parent-package-pricing" style="margin-left: 12px; margin-top: 20px;">
|
||||
<p><strong><?php _e('Package Pricing', 'wc-tier-package-prices'); ?></strong></p>
|
||||
<table class="widefat wc-tpp-packages-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></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></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="wc-tpp-packages-container">
|
||||
<?php
|
||||
$packages = get_post_meta($post->ID, '_wc_tpp_packages', true);
|
||||
if (!is_array($packages)) {
|
||||
$packages = array();
|
||||
}
|
||||
|
||||
foreach ($packages as $index => $package) {
|
||||
$this->render_package_row($index, $package);
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="form-field">
|
||||
<button type="button" class="button wc-tpp-add-package"><?php _e('Add Package', 'wc-tier-package-prices'); ?></button>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
woocommerce_wp_checkbox(array(
|
||||
'id' => '_wc_tpp_restrict_to_packages',
|
||||
'label' => __('Restrict to Package Quantities (Default)', 'wc-tier-package-prices'),
|
||||
'description' => __('Default restriction setting for all variations. Only allow quantities defined in packages above.', 'wc-tier-package-prices'),
|
||||
'desc_tip' => true,
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
|
||||
<!-- Templates for JavaScript (shared between simple and variable parent) -->
|
||||
<script type="text/html" id="wc-tpp-tier-row-template">
|
||||
<?php $this->render_tier_row('{{INDEX}}', array('min_qty' => '', 'price' => '', 'label' => '')); ?>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="wc-tpp-package-row-template">
|
||||
<?php $this->render_package_row('{{INDEX}}', array('qty' => '', 'price' => '', 'label' => '')); ?>
|
||||
</script>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function add_tier_pricing_fields() {
|
||||
global $post;
|
||||
|
||||
// Only show for simple products (variable products use add_variable_parent_pricing_fields)
|
||||
$product = wc_get_product($post->ID);
|
||||
if ($product && $product->is_type('variable')) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="options_group wc-tpp-tier-pricing">
|
||||
<p class="form-field">
|
||||
@@ -34,7 +149,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
||||
<th><?php _e('Price', 'wc-tier-package-prices'); ?></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></th>
|
||||
</tr>
|
||||
@@ -62,6 +177,13 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
|
||||
public function add_package_pricing_fields() {
|
||||
global $post;
|
||||
|
||||
// Only show for simple products (variable products use add_variable_parent_pricing_fields)
|
||||
$product = wc_get_product($post->ID);
|
||||
if ($product && $product->is_type('variable')) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="options_group wc-tpp-package-pricing">
|
||||
<p class="form-field">
|
||||
@@ -73,7 +195,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
||||
<th><?php _e('Price', 'wc-tier-package-prices'); ?></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></th>
|
||||
</tr>
|
||||
@@ -119,14 +241,16 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
private function render_tier_row($index, $tier) {
|
||||
WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array(
|
||||
'index' => $index,
|
||||
'tier' => $tier
|
||||
'tier' => $tier,
|
||||
'currency_symbol' => get_woocommerce_currency_symbol()
|
||||
));
|
||||
}
|
||||
|
||||
private function render_package_row($index, $package) {
|
||||
WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array(
|
||||
'index' => $index,
|
||||
'package' => $package
|
||||
'package' => $package,
|
||||
'currency_symbol' => get_woocommerce_currency_symbol()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -163,7 +287,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
||||
<th><?php _e('Price', 'wc-tier-package-prices'); ?></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></th>
|
||||
</tr>
|
||||
@@ -186,7 +310,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
||||
<th><?php _e('Price', 'wc-tier-package-prices'); ?></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></th>
|
||||
</tr>
|
||||
@@ -210,7 +334,8 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
'name' => 'wc_tpp_restrict_to_packages[' . $loop . ']',
|
||||
'label' => __('Restrict to Package Quantities', 'wc-tier-package-prices'),
|
||||
'description' => __('Only allow quantities defined in packages above', 'wc-tier-package-prices'),
|
||||
'value' => $restrict === 'yes' ? 'yes' : 'no',
|
||||
'desc_tip' => true,
|
||||
'value' => $restrict,
|
||||
'cbvalue' => 'yes',
|
||||
'wrapper_class' => 'form-row form-row-full'
|
||||
));
|
||||
@@ -240,7 +365,8 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array(
|
||||
'index' => $index,
|
||||
'tier' => $tier,
|
||||
'field_prefix' => 'wc_tpp_tiers[' . $loop . ']'
|
||||
'field_prefix' => 'wc_tpp_tiers[' . $loop . ']',
|
||||
'currency_symbol' => get_woocommerce_currency_symbol()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -255,7 +381,8 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array(
|
||||
'index' => $index,
|
||||
'package' => $package,
|
||||
'field_prefix' => 'wc_tpp_packages[' . $loop . ']'
|
||||
'field_prefix' => 'wc_tpp_packages[' . $loop . ']',
|
||||
'currency_symbol' => get_woocommerce_currency_symbol()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -291,10 +418,15 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
usort($tiers, function($a, $b) {
|
||||
return $a['min_qty'] - $b['min_qty'];
|
||||
});
|
||||
// Only save if we have valid tiers, otherwise delete
|
||||
if (!empty($tiers)) {
|
||||
update_post_meta($post_id, '_wc_tpp_tiers', $tiers);
|
||||
} else {
|
||||
delete_post_meta($post_id, '_wc_tpp_tiers');
|
||||
}
|
||||
} else {
|
||||
delete_post_meta($post_id, '_wc_tpp_tiers');
|
||||
}
|
||||
|
||||
// Save package pricing
|
||||
if (isset($_POST['_wc_tpp_packages'])) {
|
||||
@@ -312,10 +444,15 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
usort($packages, function($a, $b) {
|
||||
return $a['qty'] - $b['qty'];
|
||||
});
|
||||
// Only save if we have valid packages, otherwise delete
|
||||
if (!empty($packages)) {
|
||||
update_post_meta($post_id, '_wc_tpp_packages', $packages);
|
||||
} else {
|
||||
delete_post_meta($post_id, '_wc_tpp_packages');
|
||||
}
|
||||
} else {
|
||||
delete_post_meta($post_id, '_wc_tpp_packages');
|
||||
}
|
||||
|
||||
// Save package quantity restriction setting
|
||||
$restrict_to_packages = isset($_POST['_wc_tpp_restrict_to_packages']) ? 'yes' : 'no';
|
||||
@@ -335,8 +472,8 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
}
|
||||
|
||||
// Save tier pricing for this variation
|
||||
if (isset($_POST['wc_tpp_tiers'][$loop])) {
|
||||
$tiers = array();
|
||||
if (isset($_POST['wc_tpp_tiers'][$loop]) && is_array($_POST['wc_tpp_tiers'][$loop])) {
|
||||
foreach ($_POST['wc_tpp_tiers'][$loop] as $tier) {
|
||||
if (!empty($tier['min_qty']) && !empty($tier['price'])) {
|
||||
$tiers[] = array(
|
||||
@@ -350,14 +487,17 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
usort($tiers, function($a, $b) {
|
||||
return $a['min_qty'] - $b['min_qty'];
|
||||
});
|
||||
}
|
||||
// Always update or delete based on whether we have valid tiers
|
||||
if (!empty($tiers)) {
|
||||
update_post_meta($variation_id, '_wc_tpp_tiers', $tiers);
|
||||
} else {
|
||||
delete_post_meta($variation_id, '_wc_tpp_tiers');
|
||||
}
|
||||
|
||||
// Save package pricing for this variation
|
||||
if (isset($_POST['wc_tpp_packages'][$loop])) {
|
||||
$packages = array();
|
||||
if (isset($_POST['wc_tpp_packages'][$loop]) && is_array($_POST['wc_tpp_packages'][$loop])) {
|
||||
foreach ($_POST['wc_tpp_packages'][$loop] as $package) {
|
||||
if (!empty($package['qty']) && !empty($package['price'])) {
|
||||
$packages[] = array(
|
||||
@@ -371,6 +511,9 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
||||
usort($packages, function($a, $b) {
|
||||
return $a['qty'] - $b['qty'];
|
||||
});
|
||||
}
|
||||
// Always update or delete based on whether we have valid packages
|
||||
if (!empty($packages)) {
|
||||
update_post_meta($variation_id, '_wc_tpp_packages', $packages);
|
||||
} else {
|
||||
delete_post_meta($variation_id, '_wc_tpp_packages');
|
||||
|
||||
426
includes/class-wc-tpp-settings.php
Normal file → Executable file
426
includes/class-wc-tpp-settings.php
Normal file → Executable file
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* WooCommerce Settings Integration
|
||||
*
|
||||
* Adds Tier & Package Prices settings to WooCommerce Settings > Advanced tab
|
||||
* Adds Tier & Package Prices settings to WooCommerce Settings with sub-tabs
|
||||
*
|
||||
* @package WC_Tier_Package_Prices
|
||||
*/
|
||||
@@ -29,32 +29,31 @@ if (!class_exists('WC_TPP_Settings')) {
|
||||
$this->label = __('Tier & Package Prices', 'wc-tier-package-prices');
|
||||
|
||||
parent::__construct();
|
||||
|
||||
// Add AJAX handlers for license validation
|
||||
add_action('wp_ajax_wc_tpp_validate_license', array($this, 'ajax_validate_license'));
|
||||
add_action('wp_ajax_wc_tpp_activate_license', array($this, 'ajax_activate_license'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sections
|
||||
* Get own sections - Modern WooCommerce pattern
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sections() {
|
||||
$sections = array(
|
||||
protected function get_own_sections() {
|
||||
return array(
|
||||
'' => __('General', 'wc-tier-package-prices'),
|
||||
'license' => __('License', 'wc-tier-package-prices'),
|
||||
);
|
||||
|
||||
return apply_filters('woocommerce_get_sections_' . $this->id, $sections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings array
|
||||
* Get settings for the default (General) section
|
||||
*
|
||||
* @param string $current_section Current section name.
|
||||
* @return array
|
||||
*/
|
||||
public function get_settings($current_section = '') {
|
||||
$settings = array();
|
||||
|
||||
if ('' === $current_section) {
|
||||
$settings = array(
|
||||
protected function get_settings_for_default_section() {
|
||||
return array(
|
||||
array(
|
||||
'title' => __('Tier & Package Prices Settings', 'wc-tier-package-prices'),
|
||||
'type' => 'title',
|
||||
@@ -121,23 +120,410 @@ if (!class_exists('WC_TPP_Settings')) {
|
||||
);
|
||||
}
|
||||
|
||||
return apply_filters('woocommerce_get_settings_' . $this->id, $settings, $current_section);
|
||||
/**
|
||||
* Get settings for the License section
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_settings_for_license_section() {
|
||||
return array(
|
||||
array(
|
||||
'title' => __('License Management', 'wc-tier-package-prices'),
|
||||
'type' => 'title',
|
||||
'desc' => __('Enter your license key to receive updates and support.', 'wc-tier-package-prices'),
|
||||
'id' => 'wc_tpp_license_settings',
|
||||
),
|
||||
|
||||
array(
|
||||
'title' => __('License Server URL', 'wc-tier-package-prices'),
|
||||
'desc' => __('The URL of the license server.', 'wc-tier-package-prices'),
|
||||
'id' => 'wc_tpp_license_server_url',
|
||||
'type' => 'url',
|
||||
'default' => '',
|
||||
'css' => 'min-width:400px;',
|
||||
'desc_tip' => true,
|
||||
),
|
||||
|
||||
array(
|
||||
'title' => __('License Key', 'wc-tier-package-prices'),
|
||||
'desc' => __('Your license key for this plugin.', 'wc-tier-package-prices'),
|
||||
'id' => 'wc_tpp_license_key',
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
'css' => 'min-width:400px;',
|
||||
'desc_tip' => true,
|
||||
),
|
||||
|
||||
array(
|
||||
'title' => __('Server Secret', 'wc-tier-package-prices'),
|
||||
'desc' => __('The shared secret for secure communication with the license server.', 'wc-tier-package-prices'),
|
||||
'id' => 'wc_tpp_license_server_secret',
|
||||
'type' => 'password',
|
||||
'default' => '',
|
||||
'css' => 'min-width:400px;',
|
||||
'desc_tip' => true,
|
||||
),
|
||||
|
||||
array(
|
||||
'title' => __('License Status', 'wc-tier-package-prices'),
|
||||
'type' => 'wc_tpp_license_status',
|
||||
'id' => 'wc_tpp_license_status_display',
|
||||
),
|
||||
|
||||
array(
|
||||
'type' => 'sectionend',
|
||||
'id' => 'wc_tpp_license_settings',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached license status
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
private function get_cached_license_status() {
|
||||
return get_transient('wc_tpp_license_status');
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for license validation
|
||||
*/
|
||||
public function ajax_validate_license() {
|
||||
check_ajax_referer('wc_tpp_license_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_woocommerce')) {
|
||||
wp_send_json_error(array('message' => __('Permission denied.', 'wc-tier-package-prices')));
|
||||
}
|
||||
|
||||
$license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? ''));
|
||||
$server_url = esc_url_raw(wp_unslash($_POST['server_url'] ?? ''));
|
||||
$server_secret = sanitize_text_field(wp_unslash($_POST['server_secret'] ?? ''));
|
||||
|
||||
if (empty($license_key) || empty($server_url) || empty($server_secret)) {
|
||||
wp_send_json_error(array('message' => __('License key, server URL, and server secret are required.', 'wc-tier-package-prices')));
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->get_license_client($server_url, $server_secret);
|
||||
$domain = $this->get_current_domain();
|
||||
$result = $client->validate($license_key, $domain);
|
||||
|
||||
// Cache the status
|
||||
set_transient('wc_tpp_license_status', array(
|
||||
'valid' => true,
|
||||
'product_id' => $result->productId,
|
||||
'expires_at' => $result->expiresAt?->format('Y-m-d H:i:s'),
|
||||
'is_lifetime' => $result->isLifetime(),
|
||||
'checked_at' => current_time('mysql'),
|
||||
), DAY_IN_SECONDS);
|
||||
|
||||
wp_send_json_success(array(
|
||||
'message' => __('License is valid!', 'wc-tier-package-prices'),
|
||||
'status' => $this->get_cached_license_status(),
|
||||
));
|
||||
|
||||
} catch (\Magdev\WcLicensedProductClient\Exception\RateLimitExceededException $e) {
|
||||
wp_send_json_error(array(
|
||||
'message' => sprintf(
|
||||
/* translators: %d: Number of seconds to wait */
|
||||
__('Rate limit exceeded. Please try again in %d seconds.', 'wc-tier-package-prices'),
|
||||
$e->retryAfter ?? 60
|
||||
),
|
||||
'code' => 'rate_limit_exceeded',
|
||||
'retry_after' => $e->retryAfter,
|
||||
));
|
||||
} catch (\Magdev\WcLicensedProductClient\Security\SignatureException $e) {
|
||||
delete_transient('wc_tpp_license_status');
|
||||
wp_send_json_error(array(
|
||||
'message' => __('Response signature verification failed. Please check your server secret.', 'wc-tier-package-prices'),
|
||||
'code' => 'signature_error',
|
||||
));
|
||||
} catch (\Magdev\WcLicensedProductClient\Exception\LicenseException $e) {
|
||||
delete_transient('wc_tpp_license_status');
|
||||
wp_send_json_error(array(
|
||||
'message' => $e->getMessage(),
|
||||
'code' => $e->errorCode ?? 'unknown',
|
||||
));
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
wp_send_json_error(array(
|
||||
'message' => $e->getMessage(),
|
||||
'code' => 'invalid_url',
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
delete_transient('wc_tpp_license_status');
|
||||
wp_send_json_error(array(
|
||||
'message' => __('An unexpected error occurred. Please try again.', 'wc-tier-package-prices'),
|
||||
'code' => 'exception',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for license activation
|
||||
*/
|
||||
public function ajax_activate_license() {
|
||||
check_ajax_referer('wc_tpp_license_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_woocommerce')) {
|
||||
wp_send_json_error(array('message' => __('Permission denied.', 'wc-tier-package-prices')));
|
||||
}
|
||||
|
||||
$license_key = sanitize_text_field(wp_unslash($_POST['license_key'] ?? ''));
|
||||
$server_url = esc_url_raw(wp_unslash($_POST['server_url'] ?? ''));
|
||||
$server_secret = sanitize_text_field(wp_unslash($_POST['server_secret'] ?? ''));
|
||||
|
||||
if (empty($license_key) || empty($server_url) || empty($server_secret)) {
|
||||
wp_send_json_error(array('message' => __('License key, server URL, and server secret are required.', 'wc-tier-package-prices')));
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->get_license_client($server_url, $server_secret);
|
||||
$domain = $this->get_current_domain();
|
||||
$result = $client->activate($license_key, $domain);
|
||||
|
||||
if ($result->success) {
|
||||
// Validate to get full status after activation
|
||||
$validate_result = $client->validate($license_key, $domain);
|
||||
|
||||
set_transient('wc_tpp_license_status', array(
|
||||
'valid' => true,
|
||||
'product_id' => $validate_result->productId,
|
||||
'expires_at' => $validate_result->expiresAt?->format('Y-m-d H:i:s'),
|
||||
'is_lifetime' => $validate_result->isLifetime(),
|
||||
'checked_at' => current_time('mysql'),
|
||||
), DAY_IN_SECONDS);
|
||||
|
||||
wp_send_json_success(array(
|
||||
'message' => __('License activated successfully!', 'wc-tier-package-prices'),
|
||||
'status' => $this->get_cached_license_status(),
|
||||
));
|
||||
}
|
||||
|
||||
wp_send_json_error(array('message' => $result->message));
|
||||
|
||||
} catch (\Magdev\WcLicensedProductClient\Exception\RateLimitExceededException $e) {
|
||||
wp_send_json_error(array(
|
||||
'message' => sprintf(
|
||||
/* translators: %d: Number of seconds to wait */
|
||||
__('Rate limit exceeded. Please try again in %d seconds.', 'wc-tier-package-prices'),
|
||||
$e->retryAfter ?? 60
|
||||
),
|
||||
'code' => 'rate_limit_exceeded',
|
||||
'retry_after' => $e->retryAfter,
|
||||
));
|
||||
} catch (\Magdev\WcLicensedProductClient\Security\SignatureException $e) {
|
||||
wp_send_json_error(array(
|
||||
'message' => __('Response signature verification failed. Please check your server secret.', 'wc-tier-package-prices'),
|
||||
'code' => 'signature_error',
|
||||
));
|
||||
} catch (\Magdev\WcLicensedProductClient\Exception\LicenseException $e) {
|
||||
wp_send_json_error(array(
|
||||
'message' => $e->getMessage(),
|
||||
'code' => $e->errorCode ?? 'unknown',
|
||||
));
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
wp_send_json_error(array(
|
||||
'message' => $e->getMessage(),
|
||||
'code' => 'invalid_url',
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
wp_send_json_error(array(
|
||||
'message' => __('An unexpected error occurred. Please try again.', 'wc-tier-package-prices'),
|
||||
'code' => 'exception',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get license client instance
|
||||
*
|
||||
* Uses SecureLicenseClient for HMAC signature verification.
|
||||
*
|
||||
* @param string $server_url License server URL.
|
||||
* @param string $server_secret Shared secret for signature verification.
|
||||
* @return \Magdev\WcLicensedProductClient\LicenseClientInterface
|
||||
*/
|
||||
private function get_license_client(string $server_url, string $server_secret): \Magdev\WcLicensedProductClient\LicenseClientInterface {
|
||||
$httpClient = \Symfony\Component\HttpClient\HttpClient::create();
|
||||
return new \Magdev\WcLicensedProductClient\SecureLicenseClient(
|
||||
httpClient: $httpClient,
|
||||
baseUrl: $server_url,
|
||||
serverSecret: $server_secret,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current domain for license validation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_current_domain(): string {
|
||||
return wp_parse_url(home_url(), PHP_URL_HOST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the settings
|
||||
*/
|
||||
public function output() {
|
||||
$settings = $this->get_settings();
|
||||
WC_Admin_Settings::output_fields($settings);
|
||||
global $current_section;
|
||||
|
||||
// Register custom field type for license status display
|
||||
add_action('woocommerce_admin_field_wc_tpp_license_status', array($this, 'output_license_status_field'));
|
||||
|
||||
parent::output();
|
||||
|
||||
// Add JavaScript for license section
|
||||
if ('license' === $current_section) {
|
||||
$this->output_license_scripts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings
|
||||
* Output license status custom field
|
||||
*
|
||||
* @param array $value Field configuration.
|
||||
*/
|
||||
public function save() {
|
||||
$settings = $this->get_settings();
|
||||
WC_Admin_Settings::save_fields($settings);
|
||||
public function output_license_status_field($value) {
|
||||
$status = $this->get_cached_license_status();
|
||||
?>
|
||||
<tr valign="top">
|
||||
<th scope="row" class="titledesc">
|
||||
<label><?php esc_html_e('License Status', 'wc-tier-package-prices'); ?></label>
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<div id="wc-tpp-license-status-container" class="<?php echo !empty($status['valid']) ? 'valid' : 'invalid'; ?>">
|
||||
<?php $this->render_license_status_html($status); ?>
|
||||
</div>
|
||||
<p class="description" style="margin-top: 10px;">
|
||||
<button type="button" class="button" id="wc-tpp-validate-license">
|
||||
<?php esc_html_e('Validate License', 'wc-tier-package-prices'); ?>
|
||||
</button>
|
||||
<button type="button" class="button" id="wc-tpp-activate-license">
|
||||
<?php esc_html_e('Activate License', 'wc-tier-package-prices'); ?>
|
||||
</button>
|
||||
<span class="spinner" id="wc-tpp-license-spinner"></span>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render license status HTML
|
||||
*
|
||||
* @param array|false $status License status data.
|
||||
*/
|
||||
private function render_license_status_html($status) {
|
||||
if (empty($status)) {
|
||||
echo '<span class="wc-tpp-license-inactive">' . esc_html__('No license activated', 'wc-tier-package-prices') . '</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($status['valid'])) {
|
||||
echo '<span class="wc-tpp-license-active">' . esc_html__('License Active', 'wc-tier-package-prices') . '</span>';
|
||||
if (!empty($status['expires_at']) && empty($status['is_lifetime'])) {
|
||||
echo '<br><small>' . sprintf(
|
||||
/* translators: %s: Expiration date */
|
||||
esc_html__('Expires: %s', 'wc-tier-package-prices'),
|
||||
esc_html($status['expires_at'])
|
||||
) . '</small>';
|
||||
} elseif (!empty($status['is_lifetime'])) {
|
||||
echo '<br><small>' . esc_html__('Lifetime License', 'wc-tier-package-prices') . '</small>';
|
||||
}
|
||||
if (!empty($status['checked_at'])) {
|
||||
echo '<br><small>' . sprintf(
|
||||
/* translators: %s: Last check timestamp */
|
||||
esc_html__('Last checked: %s', 'wc-tier-package-prices'),
|
||||
esc_html($status['checked_at'])
|
||||
) . '</small>';
|
||||
}
|
||||
} else {
|
||||
echo '<span class="wc-tpp-license-inactive">' . esc_html__('License Invalid', 'wc-tier-package-prices') . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output JavaScript for license management
|
||||
*/
|
||||
private function output_license_scripts() {
|
||||
$nonce = wp_create_nonce('wc_tpp_license_nonce');
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
jQuery(function($) {
|
||||
var $validateBtn = $('#wc-tpp-validate-license');
|
||||
var $activateBtn = $('#wc-tpp-activate-license');
|
||||
var $spinner = $('#wc-tpp-license-spinner');
|
||||
|
||||
function getLicenseData() {
|
||||
return {
|
||||
license_key: $('#wc_tpp_license_key').val(),
|
||||
server_url: $('#wc_tpp_license_server_url').val(),
|
||||
server_secret: $('#wc_tpp_license_server_secret').val(),
|
||||
nonce: '<?php echo esc_js($nonce); ?>'
|
||||
};
|
||||
}
|
||||
|
||||
function showSpinner() {
|
||||
$spinner.addClass('is-active');
|
||||
$validateBtn.prop('disabled', true);
|
||||
$activateBtn.prop('disabled', true);
|
||||
}
|
||||
|
||||
function hideSpinner() {
|
||||
$spinner.removeClass('is-active');
|
||||
$validateBtn.prop('disabled', false);
|
||||
$activateBtn.prop('disabled', false);
|
||||
}
|
||||
|
||||
$validateBtn.on('click', function() {
|
||||
var data = getLicenseData();
|
||||
if (!data.license_key || !data.server_url || !data.server_secret) {
|
||||
alert('<?php echo esc_js(__('Please enter license server URL, license key, and server secret.', 'wc-tier-package-prices')); ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
showSpinner();
|
||||
$.post(ajaxurl, $.extend({action: 'wc_tpp_validate_license'}, data))
|
||||
.done(function(response) {
|
||||
if (response.success) {
|
||||
alert(response.data.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data.message || '<?php echo esc_js(__('Validation failed.', 'wc-tier-package-prices')); ?>');
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
alert('<?php echo esc_js(__('Request failed. Please try again.', 'wc-tier-package-prices')); ?>');
|
||||
})
|
||||
.always(hideSpinner);
|
||||
});
|
||||
|
||||
$activateBtn.on('click', function() {
|
||||
var data = getLicenseData();
|
||||
if (!data.license_key || !data.server_url || !data.server_secret) {
|
||||
alert('<?php echo esc_js(__('Please enter license server URL, license key, and server secret.', 'wc-tier-package-prices')); ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
showSpinner();
|
||||
$.post(ajaxurl, $.extend({action: 'wc_tpp_activate_license'}, data))
|
||||
.done(function(response) {
|
||||
if (response.success) {
|
||||
alert(response.data.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data.message || '<?php echo esc_js(__('Activation failed.', 'wc-tier-package-prices')); ?>');
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
alert('<?php echo esc_js(__('Request failed. Please try again.', 'wc-tier-package-prices')); ?>');
|
||||
})
|
||||
.always(hideSpinner);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,10 @@
|
||||
# This file is distributed under the GPL v2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.21\n"
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
|
||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||
"POT-Creation-Date: 2025-12-23 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 00:00+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-30 00:00+0000\n"
|
||||
"Last-Translator: Marco Graetsch\n"
|
||||
"Language-Team: German (Switzerland)\n"
|
||||
"Language: de_CH\n"
|
||||
@@ -152,6 +152,26 @@ msgstr "Paket hinzufügen"
|
||||
msgid "Only allow quantities defined in packages above"
|
||||
msgstr "Nur oben definierte Paketmengen zulassen"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:42
|
||||
msgid "Default Tier & Package Pricing for All Variations"
|
||||
msgstr "Standard Staffel- & Paketpreise für alle Varianten"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:43
|
||||
msgid "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
msgstr "Legen Sie Standardpreise für alle Varianten fest. Einzelne Varianten können diese Standardwerte mit ihren eigenen spezifischen Preisen überschreiben."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:44
|
||||
msgid "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
msgstr "Konfigurieren Sie hier Standard-Staffel- und Paketpreise. Alle Varianten übernehmen diese Einstellungen, es sei denn, sie definieren ihre eigenen spezifischen Preise."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:87
|
||||
msgid "Restrict to Package Quantities (Default)"
|
||||
msgstr "Auf Paketmengen beschränken (Standard)"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:88
|
||||
msgid "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
msgstr "Standard-Beschränkungseinstellung für alle Varianten. Nur oben definierte Paketmengen zulassen."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:90
|
||||
msgid "Minimum Quantity"
|
||||
msgstr "Mindestmenge"
|
||||
@@ -240,6 +260,141 @@ msgstr "Die Menge %1$d ist für %2$s nicht verfügbar. Bitte wählen Sie aus den
|
||||
msgid "View Options"
|
||||
msgstr "Optionen ansehen"
|
||||
|
||||
#: includes/class-wc-tpp-frontend.php:178
|
||||
msgid "View options for %s"
|
||||
msgstr "Optionen für %s ansehen"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:36
|
||||
#: includes/class-wc-tpp-product-meta.php:140
|
||||
msgid "Min Quantity"
|
||||
msgstr "Mindestmenge"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:37
|
||||
#: includes/class-wc-tpp-product-meta.php:141
|
||||
#: includes/class-wc-tpp-product-meta.php:164
|
||||
msgid "Price"
|
||||
msgstr "Preis"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:38
|
||||
#: includes/class-wc-tpp-product-meta.php:142
|
||||
#: includes/class-wc-tpp-product-meta.php:165
|
||||
msgid "Label (optional)"
|
||||
msgstr "Beschriftung (optional)"
|
||||
|
||||
#: wc-tier-and-package-prices.php
|
||||
msgid "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
msgstr "WooCommerce Staffel- und Paketpreise erfordert PHP 8.3 oder höher. Ihr Server verwendet PHP %s."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License"
|
||||
msgstr "Lizenz"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Management"
|
||||
msgstr "Lizenzverwaltung"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Enter your license key to receive updates and support."
|
||||
msgstr "Geben Sie Ihren Lizenzschlüssel ein, um Updates und Support zu erhalten."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Server URL"
|
||||
msgstr "Lizenzserver-URL"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The URL of the license server."
|
||||
msgstr "Die URL des Lizenzservers."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Key"
|
||||
msgstr "Lizenzschlüssel"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Your license key for this plugin."
|
||||
msgstr "Ihr Lizenzschlüssel für dieses Plugin."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Status"
|
||||
msgstr "Lizenzstatus"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validate License"
|
||||
msgstr "Lizenz überprüfen"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activate License"
|
||||
msgstr "Lizenz aktivieren"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "No license activated"
|
||||
msgstr "Keine Lizenz aktiviert"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Active"
|
||||
msgstr "Lizenz aktiv"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Invalid"
|
||||
msgstr "Lizenz ungültig"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Lifetime License"
|
||||
msgstr "Lebenslange Lizenz"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Expires: %s"
|
||||
msgstr "Läuft ab: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Last checked: %s"
|
||||
msgstr "Zuletzt geprüft: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License is valid!"
|
||||
msgstr "Lizenz ist gültig!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License activated successfully!"
|
||||
msgstr "Lizenz erfolgreich aktiviert!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Permission denied."
|
||||
msgstr "Zugriff verweigert."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Server Secret"
|
||||
msgstr "Server-Geheimnis"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The shared secret for secure communication with the license server."
|
||||
msgstr "Das gemeinsame Geheimnis für die sichere Kommunikation mit dem Lizenzserver."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License key, server URL, and server secret are required."
|
||||
msgstr "Lizenzschlüssel, Server-URL und Server-Geheimnis sind erforderlich."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
#. translators: %d: Number of seconds to wait
|
||||
msgid "Rate limit exceeded. Please try again in %d seconds."
|
||||
msgstr "Anfragelimit überschritten. Bitte versuchen Sie es in %d Sekunden erneut."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Response signature verification failed. Please check your server secret."
|
||||
msgstr "Überprüfung der Antwortsignatur fehlgeschlagen. Bitte überprüfen Sie Ihr Server-Geheimnis."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "An unexpected error occurred. Please try again."
|
||||
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Please enter license server URL, license key, and server secret."
|
||||
msgstr "Bitte geben Sie die Lizenzserver-URL, den Lizenzschlüssel und das Server-Geheimnis ein."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validation failed."
|
||||
msgstr "Überprüfung fehlgeschlagen."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activation failed."
|
||||
msgstr "Aktivierung fehlgeschlagen."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Request failed. Please try again."
|
||||
msgstr "Anfrage fehlgeschlagen. Bitte versuchen Sie es erneut."
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,10 @@
|
||||
# This file is distributed under the GPL v2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.6\n"
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
|
||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||
"POT-Creation-Date: 2025-12-21 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-21 00:00+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-30 00:00+0000\n"
|
||||
"Last-Translator: Marco Graetsch\n"
|
||||
"Language-Team: German (Switzerland)\n"
|
||||
"Language: de_CH_informal\n"
|
||||
@@ -152,6 +152,26 @@ msgstr "Paket hinzufügen"
|
||||
msgid "Only allow quantities defined in packages above"
|
||||
msgstr "Nur oben definierte Paketmengen zulassen"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:42
|
||||
msgid "Default Tier & Package Pricing for All Variations"
|
||||
msgstr "Standard Staffel- & Paketpreise für alle Varianten"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:43
|
||||
msgid "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
msgstr "Lege Standardpreise für alle Varianten fest. Einzelne Varianten können diese Standardwerte mit ihren eigenen spezifischen Preisen überschreiben."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:44
|
||||
msgid "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
msgstr "Konfiguriere hier Standard-Staffel- und Paketpreise. Alle Varianten übernehmen diese Einstellungen, es sei denn, sie definieren ihre eigenen spezifischen Preise."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:87
|
||||
msgid "Restrict to Package Quantities (Default)"
|
||||
msgstr "Auf Paketmengen beschränken (Standard)"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:88
|
||||
msgid "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
msgstr "Standard-Beschränkungseinstellung für alle Varianten. Nur oben definierte Paketmengen zulassen."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:90
|
||||
msgid "Minimum Quantity"
|
||||
msgstr "Mindestmenge"
|
||||
@@ -240,6 +260,141 @@ msgstr "Die Menge %1$d ist für %2$s nicht verfügbar. Bitte wähle aus den verf
|
||||
msgid "View Options"
|
||||
msgstr "Optionen ansehen"
|
||||
|
||||
#: includes/class-wc-tpp-frontend.php:178
|
||||
msgid "View options for %s"
|
||||
msgstr "Optionen für %s ansehen"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:36
|
||||
#: includes/class-wc-tpp-product-meta.php:140
|
||||
msgid "Min Quantity"
|
||||
msgstr "Mindestmenge"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:37
|
||||
#: includes/class-wc-tpp-product-meta.php:141
|
||||
#: includes/class-wc-tpp-product-meta.php:164
|
||||
msgid "Price"
|
||||
msgstr "Preis"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:38
|
||||
#: includes/class-wc-tpp-product-meta.php:142
|
||||
#: includes/class-wc-tpp-product-meta.php:165
|
||||
msgid "Label (optional)"
|
||||
msgstr "Beschriftung (optional)"
|
||||
|
||||
#: wc-tier-and-package-prices.php
|
||||
msgid "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
msgstr "WooCommerce Staffel- und Paketpreise erfordert PHP 8.3 oder höher. Dein Server verwendet PHP %s."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License"
|
||||
msgstr "Lizenz"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Management"
|
||||
msgstr "Lizenzverwaltung"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Enter your license key to receive updates and support."
|
||||
msgstr "Gib deinen Lizenzschlüssel ein, um Updates und Support zu erhalten."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Server URL"
|
||||
msgstr "Lizenzserver-URL"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The URL of the license server."
|
||||
msgstr "Die URL des Lizenzservers."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Key"
|
||||
msgstr "Lizenzschlüssel"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Your license key for this plugin."
|
||||
msgstr "Dein Lizenzschlüssel für dieses Plugin."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Status"
|
||||
msgstr "Lizenzstatus"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validate License"
|
||||
msgstr "Lizenz überprüfen"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activate License"
|
||||
msgstr "Lizenz aktivieren"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "No license activated"
|
||||
msgstr "Keine Lizenz aktiviert"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Active"
|
||||
msgstr "Lizenz aktiv"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Invalid"
|
||||
msgstr "Lizenz ungültig"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Lifetime License"
|
||||
msgstr "Lebenslange Lizenz"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Expires: %s"
|
||||
msgstr "Läuft ab: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Last checked: %s"
|
||||
msgstr "Zuletzt geprüft: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License is valid!"
|
||||
msgstr "Lizenz ist gültig!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License activated successfully!"
|
||||
msgstr "Lizenz erfolgreich aktiviert!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Permission denied."
|
||||
msgstr "Zugriff verweigert."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Server Secret"
|
||||
msgstr "Server-Geheimnis"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The shared secret for secure communication with the license server."
|
||||
msgstr "Das gemeinsame Geheimnis für die sichere Kommunikation mit dem Lizenzserver."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License key, server URL, and server secret are required."
|
||||
msgstr "Lizenzschlüssel, Server-URL und Server-Geheimnis sind erforderlich."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
#. translators: %d: Number of seconds to wait
|
||||
msgid "Rate limit exceeded. Please try again in %d seconds."
|
||||
msgstr "Anfragelimit überschritten. Bitte versuche es in %d Sekunden erneut."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Response signature verification failed. Please check your server secret."
|
||||
msgstr "Überprüfung der Antwortsignatur fehlgeschlagen. Bitte überprüfe dein Server-Geheimnis."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "An unexpected error occurred. Please try again."
|
||||
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Please enter license server URL, license key, and server secret."
|
||||
msgstr "Bitte gib die Lizenzserver-URL, den Lizenzschlüssel und das Server-Geheimnis ein."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validation failed."
|
||||
msgstr "Überprüfung fehlgeschlagen."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activation failed."
|
||||
msgstr "Aktivierung fehlgeschlagen."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Request failed. Please try again."
|
||||
msgstr "Anfrage fehlgeschlagen. Bitte versuche es erneut."
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,10 @@
|
||||
# This file is distributed under the GPL v2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.6\n"
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
|
||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||
"POT-Creation-Date: 2025-12-21 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-21 00:00+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-30 00:00+0000\n"
|
||||
"Last-Translator: Marco Graetsch\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de_DE\n"
|
||||
@@ -152,6 +152,26 @@ msgstr "Paket hinzufügen"
|
||||
msgid "Only allow quantities defined in packages above"
|
||||
msgstr "Nur oben definierte Paketmengen zulassen"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:42
|
||||
msgid "Default Tier & Package Pricing for All Variations"
|
||||
msgstr "Standard Staffel- & Paketpreise für alle Varianten"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:43
|
||||
msgid "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
msgstr "Legen Sie Standardpreise für alle Varianten fest. Einzelne Varianten können diese Standardwerte mit ihren eigenen spezifischen Preisen überschreiben."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:44
|
||||
msgid "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
msgstr "Konfigurieren Sie hier Standard-Staffel- und Paketpreise. Alle Varianten übernehmen diese Einstellungen, es sei denn, sie definieren ihre eigenen spezifischen Preise."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:87
|
||||
msgid "Restrict to Package Quantities (Default)"
|
||||
msgstr "Auf Paketmengen beschränken (Standard)"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:88
|
||||
msgid "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
msgstr "Standard-Beschränkungseinstellung für alle Varianten. Nur oben definierte Paketmengen zulassen."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:90
|
||||
msgid "Minimum Quantity"
|
||||
msgstr "Mindestmenge"
|
||||
@@ -240,6 +260,141 @@ msgstr "Die Menge %1$d ist für %2$s nicht verfügbar. Bitte wählen Sie aus den
|
||||
msgid "View Options"
|
||||
msgstr "Optionen ansehen"
|
||||
|
||||
#: includes/class-wc-tpp-frontend.php:178
|
||||
msgid "View options for %s"
|
||||
msgstr "Optionen für %s ansehen"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:36
|
||||
#: includes/class-wc-tpp-product-meta.php:140
|
||||
msgid "Min Quantity"
|
||||
msgstr "Mindestmenge"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:37
|
||||
#: includes/class-wc-tpp-product-meta.php:141
|
||||
#: includes/class-wc-tpp-product-meta.php:164
|
||||
msgid "Price"
|
||||
msgstr "Preis"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:38
|
||||
#: includes/class-wc-tpp-product-meta.php:142
|
||||
#: includes/class-wc-tpp-product-meta.php:165
|
||||
msgid "Label (optional)"
|
||||
msgstr "Beschriftung (optional)"
|
||||
|
||||
#: wc-tier-and-package-prices.php
|
||||
msgid "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
msgstr "WooCommerce Staffel- und Paketpreise erfordert PHP 8.3 oder höher. Ihr Server verwendet PHP %s."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License"
|
||||
msgstr "Lizenz"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Management"
|
||||
msgstr "Lizenzverwaltung"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Enter your license key to receive updates and support."
|
||||
msgstr "Geben Sie Ihren Lizenzschlüssel ein, um Updates und Support zu erhalten."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Server URL"
|
||||
msgstr "Lizenzserver-URL"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The URL of the license server."
|
||||
msgstr "Die URL des Lizenzservers."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Key"
|
||||
msgstr "Lizenzschlüssel"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Your license key for this plugin."
|
||||
msgstr "Ihr Lizenzschlüssel für dieses Plugin."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Status"
|
||||
msgstr "Lizenzstatus"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validate License"
|
||||
msgstr "Lizenz überprüfen"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activate License"
|
||||
msgstr "Lizenz aktivieren"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "No license activated"
|
||||
msgstr "Keine Lizenz aktiviert"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Active"
|
||||
msgstr "Lizenz aktiv"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Invalid"
|
||||
msgstr "Lizenz ungültig"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Lifetime License"
|
||||
msgstr "Lebenslange Lizenz"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Expires: %s"
|
||||
msgstr "Läuft ab: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Last checked: %s"
|
||||
msgstr "Zuletzt geprüft: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License is valid!"
|
||||
msgstr "Lizenz ist gültig!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License activated successfully!"
|
||||
msgstr "Lizenz erfolgreich aktiviert!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Permission denied."
|
||||
msgstr "Zugriff verweigert."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Server Secret"
|
||||
msgstr "Server-Geheimnis"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The shared secret for secure communication with the license server."
|
||||
msgstr "Das gemeinsame Geheimnis für die sichere Kommunikation mit dem Lizenzserver."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License key, server URL, and server secret are required."
|
||||
msgstr "Lizenzschlüssel, Server-URL und Server-Geheimnis sind erforderlich."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
#. translators: %d: Number of seconds to wait
|
||||
msgid "Rate limit exceeded. Please try again in %d seconds."
|
||||
msgstr "Anfragelimit überschritten. Bitte versuchen Sie es in %d Sekunden erneut."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Response signature verification failed. Please check your server secret."
|
||||
msgstr "Überprüfung der Antwortsignatur fehlgeschlagen. Bitte überprüfen Sie Ihr Server-Geheimnis."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "An unexpected error occurred. Please try again."
|
||||
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Please enter license server URL, license key, and server secret."
|
||||
msgstr "Bitte geben Sie die Lizenzserver-URL, den Lizenzschlüssel und das Server-Geheimnis ein."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validation failed."
|
||||
msgstr "Überprüfung fehlgeschlagen."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activation failed."
|
||||
msgstr "Aktivierung fehlgeschlagen."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Request failed. Please try again."
|
||||
msgstr "Anfrage fehlgeschlagen. Bitte versuchen Sie es erneut."
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,10 @@
|
||||
# This file is distributed under the GPL v2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.21\n"
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
|
||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||
"POT-Creation-Date: 2025-12-23 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 00:00+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-30 00:00+0000\n"
|
||||
"Last-Translator: Marco Graetsch\n"
|
||||
"Language-Team: German (Germany)\n"
|
||||
"Language: de_DE_informal\n"
|
||||
@@ -152,6 +152,26 @@ msgstr "Paket hinzufügen"
|
||||
msgid "Only allow quantities defined in packages above"
|
||||
msgstr "Nur oben definierte Paketmengen zulassen"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:42
|
||||
msgid "Default Tier & Package Pricing for All Variations"
|
||||
msgstr "Standard Staffel- & Paketpreise für alle Varianten"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:43
|
||||
msgid "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
msgstr "Lege Standardpreise für alle Varianten fest. Einzelne Varianten können diese Standardwerte mit ihren eigenen spezifischen Preisen überschreiben."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:44
|
||||
msgid "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
msgstr "Konfiguriere hier Standard-Staffel- und Paketpreise. Alle Varianten übernehmen diese Einstellungen, es sei denn, sie definieren ihre eigenen spezifischen Preise."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:87
|
||||
msgid "Restrict to Package Quantities (Default)"
|
||||
msgstr "Auf Paketmengen beschränken (Standard)"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:88
|
||||
msgid "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
msgstr "Standard-Beschränkungseinstellung für alle Varianten. Nur oben definierte Paketmengen zulassen."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:90
|
||||
msgid "Minimum Quantity"
|
||||
msgstr "Mindestmenge"
|
||||
@@ -240,6 +260,141 @@ msgstr "Die Menge %1$d ist für %2$s nicht verfügbar. Bitte wähle aus den verf
|
||||
msgid "View Options"
|
||||
msgstr "Optionen ansehen"
|
||||
|
||||
#: includes/class-wc-tpp-frontend.php:178
|
||||
msgid "View options for %s"
|
||||
msgstr "Optionen für %s ansehen"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:36
|
||||
#: includes/class-wc-tpp-product-meta.php:140
|
||||
msgid "Min Quantity"
|
||||
msgstr "Mindestmenge"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:37
|
||||
#: includes/class-wc-tpp-product-meta.php:141
|
||||
#: includes/class-wc-tpp-product-meta.php:164
|
||||
msgid "Price"
|
||||
msgstr "Preis"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:38
|
||||
#: includes/class-wc-tpp-product-meta.php:142
|
||||
#: includes/class-wc-tpp-product-meta.php:165
|
||||
msgid "Label (optional)"
|
||||
msgstr "Beschriftung (optional)"
|
||||
|
||||
#: wc-tier-and-package-prices.php
|
||||
msgid "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
msgstr "WooCommerce Staffel- und Paketpreise erfordert PHP 8.3 oder höher. Dein Server verwendet PHP %s."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License"
|
||||
msgstr "Lizenz"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Management"
|
||||
msgstr "Lizenzverwaltung"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Enter your license key to receive updates and support."
|
||||
msgstr "Gib deinen Lizenzschlüssel ein, um Updates und Support zu erhalten."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Server URL"
|
||||
msgstr "Lizenzserver-URL"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The URL of the license server."
|
||||
msgstr "Die URL des Lizenzservers."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Key"
|
||||
msgstr "Lizenzschlüssel"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Your license key for this plugin."
|
||||
msgstr "Dein Lizenzschlüssel für dieses Plugin."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Status"
|
||||
msgstr "Lizenzstatus"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validate License"
|
||||
msgstr "Lizenz überprüfen"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activate License"
|
||||
msgstr "Lizenz aktivieren"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "No license activated"
|
||||
msgstr "Keine Lizenz aktiviert"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Active"
|
||||
msgstr "Lizenz aktiv"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Invalid"
|
||||
msgstr "Lizenz ungültig"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Lifetime License"
|
||||
msgstr "Lebenslange Lizenz"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Expires: %s"
|
||||
msgstr "Läuft ab: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Last checked: %s"
|
||||
msgstr "Zuletzt geprüft: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License is valid!"
|
||||
msgstr "Lizenz ist gültig!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License activated successfully!"
|
||||
msgstr "Lizenz erfolgreich aktiviert!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Permission denied."
|
||||
msgstr "Zugriff verweigert."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Server Secret"
|
||||
msgstr "Server-Geheimnis"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The shared secret for secure communication with the license server."
|
||||
msgstr "Das gemeinsame Geheimnis für die sichere Kommunikation mit dem Lizenzserver."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License key, server URL, and server secret are required."
|
||||
msgstr "Lizenzschlüssel, Server-URL und Server-Geheimnis sind erforderlich."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
#. translators: %d: Number of seconds to wait
|
||||
msgid "Rate limit exceeded. Please try again in %d seconds."
|
||||
msgstr "Anfragelimit überschritten. Bitte versuche es in %d Sekunden erneut."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Response signature verification failed. Please check your server secret."
|
||||
msgstr "Überprüfung der Antwortsignatur fehlgeschlagen. Bitte überprüfe dein Server-Geheimnis."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "An unexpected error occurred. Please try again."
|
||||
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Please enter license server URL, license key, and server secret."
|
||||
msgstr "Bitte gib die Lizenzserver-URL, den Lizenzschlüssel und das Server-Geheimnis ein."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validation failed."
|
||||
msgstr "Überprüfung fehlgeschlagen."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activation failed."
|
||||
msgstr "Aktivierung fehlgeschlagen."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Request failed. Please try again."
|
||||
msgstr "Anfrage fehlgeschlagen. Bitte versuche es erneut."
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,10 @@
|
||||
# This file is distributed under the GPL v2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.6\n"
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
|
||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||
"POT-Creation-Date: 2025-12-21 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-21 00:00+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-30 00:00+0000\n"
|
||||
"Last-Translator: Marco Graetsch\n"
|
||||
"Language-Team: English\n"
|
||||
"Language: en_US\n"
|
||||
@@ -152,6 +152,26 @@ msgstr "Add Package"
|
||||
msgid "Only allow quantities defined in packages above"
|
||||
msgstr "Only allow quantities defined in packages above"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:42
|
||||
msgid "Default Tier & Package Pricing for All Variations"
|
||||
msgstr "Default Tier & Package Pricing for All Variations"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:43
|
||||
msgid "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
msgstr "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:44
|
||||
msgid "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
msgstr "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:87
|
||||
msgid "Restrict to Package Quantities (Default)"
|
||||
msgstr "Restrict to Package Quantities (Default)"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:88
|
||||
msgid "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
msgstr "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:90
|
||||
msgid "Minimum Quantity"
|
||||
msgstr "Minimum Quantity"
|
||||
@@ -240,6 +260,141 @@ msgstr "The quantity %1$d is not available for %2$s. Please choose from the avai
|
||||
msgid "View Options"
|
||||
msgstr "View Options"
|
||||
|
||||
#: includes/class-wc-tpp-frontend.php:178
|
||||
msgid "View options for %s"
|
||||
msgstr "View options for %s"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:36
|
||||
#: includes/class-wc-tpp-product-meta.php:140
|
||||
msgid "Min Quantity"
|
||||
msgstr "Min Quantity"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:37
|
||||
#: includes/class-wc-tpp-product-meta.php:141
|
||||
#: includes/class-wc-tpp-product-meta.php:164
|
||||
msgid "Price"
|
||||
msgstr "Price"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:38
|
||||
#: includes/class-wc-tpp-product-meta.php:142
|
||||
#: includes/class-wc-tpp-product-meta.php:165
|
||||
msgid "Label (optional)"
|
||||
msgstr "Label (optional)"
|
||||
|
||||
#: wc-tier-and-package-prices.php
|
||||
msgid "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
msgstr "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License"
|
||||
msgstr "License"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Management"
|
||||
msgstr "License Management"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Enter your license key to receive updates and support."
|
||||
msgstr "Enter your license key to receive updates and support."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Server URL"
|
||||
msgstr "License Server URL"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The URL of the license server."
|
||||
msgstr "The URL of the license server."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Key"
|
||||
msgstr "License Key"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Your license key for this plugin."
|
||||
msgstr "Your license key for this plugin."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Status"
|
||||
msgstr "License Status"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validate License"
|
||||
msgstr "Validate License"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activate License"
|
||||
msgstr "Activate License"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "No license activated"
|
||||
msgstr "No license activated"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Active"
|
||||
msgstr "License Active"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Invalid"
|
||||
msgstr "License Invalid"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Lifetime License"
|
||||
msgstr "Lifetime License"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Expires: %s"
|
||||
msgstr "Expires: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Last checked: %s"
|
||||
msgstr "Last checked: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License is valid!"
|
||||
msgstr "License is valid!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License activated successfully!"
|
||||
msgstr "License activated successfully!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Permission denied."
|
||||
msgstr "Permission denied."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Server Secret"
|
||||
msgstr "Server Secret"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The shared secret for secure communication with the license server."
|
||||
msgstr "The shared secret for secure communication with the license server."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License key, server URL, and server secret are required."
|
||||
msgstr "License key, server URL, and server secret are required."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
#. translators: %d: Number of seconds to wait
|
||||
msgid "Rate limit exceeded. Please try again in %d seconds."
|
||||
msgstr "Rate limit exceeded. Please try again in %d seconds."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Response signature verification failed. Please check your server secret."
|
||||
msgstr "Response signature verification failed. Please check your server secret."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "An unexpected error occurred. Please try again."
|
||||
msgstr "An unexpected error occurred. Please try again."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Please enter license server URL, license key, and server secret."
|
||||
msgstr "Please enter license server URL, license key, and server secret."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validation failed."
|
||||
msgstr "Validation failed."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activation failed."
|
||||
msgstr "Activation failed."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Request failed. Please try again."
|
||||
msgstr "Request failed. Please try again."
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,10 @@
|
||||
# This file is distributed under the GPL v2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.21\n"
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
|
||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||
"POT-Creation-Date: 2025-12-23 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 00:00+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-30 00:00+0000\n"
|
||||
"Last-Translator: Marco Graetsch\n"
|
||||
"Language-Team: French (Switzerland)\n"
|
||||
"Language: fr_CH\n"
|
||||
@@ -152,6 +152,26 @@ msgstr "Ajouter un forfait"
|
||||
msgid "Only allow quantities defined in packages above"
|
||||
msgstr "Autoriser uniquement les quantités définies dans les forfaits ci-dessus"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:42
|
||||
msgid "Default Tier & Package Pricing for All Variations"
|
||||
msgstr "Tarification par paliers et forfaits par défaut pour toutes les variations"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:43
|
||||
msgid "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
msgstr "Définir la tarification par défaut pour toutes les variations. Les variations individuelles peuvent remplacer ces valeurs par défaut par leur propre tarification spécifique."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:44
|
||||
msgid "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
msgstr "Configurez ici la tarification par paliers et forfaits par défaut. Toutes les variations hériteront de ces paramètres sauf si elles définissent leur propre tarification spécifique."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:87
|
||||
msgid "Restrict to Package Quantities (Default)"
|
||||
msgstr "Limiter aux quantités de forfaits (par défaut)"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:88
|
||||
msgid "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
msgstr "Paramètre de restriction par défaut pour toutes les variations. Autoriser uniquement les quantités définies dans les forfaits ci-dessus."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:90
|
||||
msgid "Minimum Quantity"
|
||||
msgstr "Quantité minimale"
|
||||
@@ -240,6 +260,141 @@ msgstr "La quantité %1$d n'est pas disponible pour %2$s. Veuillez choisir parmi
|
||||
msgid "View Options"
|
||||
msgstr "Voir les options"
|
||||
|
||||
#: includes/class-wc-tpp-frontend.php:178
|
||||
msgid "View options for %s"
|
||||
msgstr "Voir les options pour %s"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:36
|
||||
#: includes/class-wc-tpp-product-meta.php:140
|
||||
msgid "Min Quantity"
|
||||
msgstr "Quantité minimale"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:37
|
||||
#: includes/class-wc-tpp-product-meta.php:141
|
||||
#: includes/class-wc-tpp-product-meta.php:164
|
||||
msgid "Price"
|
||||
msgstr "Prix"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:38
|
||||
#: includes/class-wc-tpp-product-meta.php:142
|
||||
#: includes/class-wc-tpp-product-meta.php:165
|
||||
msgid "Label (optional)"
|
||||
msgstr "Étiquette (optionnel)"
|
||||
|
||||
#: wc-tier-and-package-prices.php
|
||||
msgid "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
msgstr "WooCommerce Prix par paliers et forfaits nécessite PHP 8.3 ou supérieur. Votre serveur utilise PHP %s."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License"
|
||||
msgstr "Licence"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Management"
|
||||
msgstr "Gestion des licences"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Enter your license key to receive updates and support."
|
||||
msgstr "Entrez votre clé de licence pour recevoir des mises à jour et du support."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Server URL"
|
||||
msgstr "URL du serveur de licence"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The URL of the license server."
|
||||
msgstr "L'URL du serveur de licence."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Key"
|
||||
msgstr "Clé de licence"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Your license key for this plugin."
|
||||
msgstr "Votre clé de licence pour cette extension."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Status"
|
||||
msgstr "Statut de la licence"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validate License"
|
||||
msgstr "Valider la licence"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activate License"
|
||||
msgstr "Activer la licence"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "No license activated"
|
||||
msgstr "Aucune licence activée"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Active"
|
||||
msgstr "Licence active"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Invalid"
|
||||
msgstr "Licence invalide"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Lifetime License"
|
||||
msgstr "Licence à vie"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Expires: %s"
|
||||
msgstr "Expire le: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Last checked: %s"
|
||||
msgstr "Dernière vérification: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License is valid!"
|
||||
msgstr "La licence est valide!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License activated successfully!"
|
||||
msgstr "Licence activée avec succès!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Permission denied."
|
||||
msgstr "Accès refusé."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Server Secret"
|
||||
msgstr "Secret serveur"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The shared secret for secure communication with the license server."
|
||||
msgstr "Le secret partagé pour la communication sécurisée avec le serveur de licence."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License key, server URL, and server secret are required."
|
||||
msgstr "La clé de licence, l'URL du serveur et le secret serveur sont requis."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
#. translators: %d: Number of seconds to wait
|
||||
msgid "Rate limit exceeded. Please try again in %d seconds."
|
||||
msgstr "Limite de requêtes dépassée. Veuillez réessayer dans %d secondes."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Response signature verification failed. Please check your server secret."
|
||||
msgstr "La vérification de la signature a échoué. Veuillez vérifier votre secret serveur."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "An unexpected error occurred. Please try again."
|
||||
msgstr "Une erreur inattendue s'est produite. Veuillez réessayer."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Please enter license server URL, license key, and server secret."
|
||||
msgstr "Veuillez entrer l'URL du serveur de licence, la clé de licence et le secret serveur."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validation failed."
|
||||
msgstr "La validation a échoué."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activation failed."
|
||||
msgstr "L'activation a échoué."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Request failed. Please try again."
|
||||
msgstr "La requête a échoué. Veuillez réessayer."
|
||||
|
||||
Binary file not shown.
@@ -3,10 +3,10 @@
|
||||
# This file is distributed under the GPL v2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.21\n"
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\n"
|
||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||
"POT-Creation-Date: 2025-12-23 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-23 00:00+0000\n"
|
||||
"POT-Creation-Date: 2025-12-30 00:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-30 00:00+0000\n"
|
||||
"Last-Translator: Marco Graetsch\n"
|
||||
"Language-Team: Italian (Switzerland)\n"
|
||||
"Language: it_CH\n"
|
||||
@@ -152,6 +152,26 @@ msgstr "Aggiungi pacchetto"
|
||||
msgid "Only allow quantities defined in packages above"
|
||||
msgstr "Consenti solo le quantità definite nei pacchetti sopra"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:42
|
||||
msgid "Default Tier & Package Pricing for All Variations"
|
||||
msgstr "Prezzi a scaglioni e pacchetti predefiniti per tutte le variazioni"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:43
|
||||
msgid "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
msgstr "Imposta i prezzi predefiniti per tutte le variazioni. Le singole variazioni possono sovrascrivere questi valori predefiniti con i propri prezzi specifici."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:44
|
||||
msgid "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
msgstr "Configura qui i prezzi a scaglioni e pacchetti predefiniti. Tutte le variazioni erediteranno queste impostazioni a meno che non definiscano i propri prezzi specifici."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:87
|
||||
msgid "Restrict to Package Quantities (Default)"
|
||||
msgstr "Limita alle quantità dei pacchetti (predefinito)"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:88
|
||||
msgid "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
msgstr "Impostazione di restrizione predefinita per tutte le variazioni. Consenti solo le quantità definite nei pacchetti sopra."
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:90
|
||||
msgid "Minimum Quantity"
|
||||
msgstr "Quantità minima"
|
||||
@@ -240,6 +260,141 @@ msgstr "La quantità %1$d non è disponibile per %2$s. Si prega di scegliere tra
|
||||
msgid "View Options"
|
||||
msgstr "Visualizza opzioni"
|
||||
|
||||
#: includes/class-wc-tpp-frontend.php:178
|
||||
msgid "View options for %s"
|
||||
msgstr "Visualizza opzioni per %s"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:36
|
||||
#: includes/class-wc-tpp-product-meta.php:140
|
||||
msgid "Min Quantity"
|
||||
msgstr "Quantità minima"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:37
|
||||
#: includes/class-wc-tpp-product-meta.php:141
|
||||
#: includes/class-wc-tpp-product-meta.php:164
|
||||
msgid "Price"
|
||||
msgstr "Prezzo"
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:38
|
||||
#: includes/class-wc-tpp-product-meta.php:142
|
||||
#: includes/class-wc-tpp-product-meta.php:165
|
||||
msgid "Label (optional)"
|
||||
msgstr "Etichetta (facoltativo)"
|
||||
|
||||
#: wc-tier-and-package-prices.php
|
||||
msgid "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
msgstr "WooCommerce Prezzi a scaglioni e pacchetti richiede PHP 8.3 o superiore. Il tuo server utilizza PHP %s."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License"
|
||||
msgstr "Licenza"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Management"
|
||||
msgstr "Gestione licenza"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Enter your license key to receive updates and support."
|
||||
msgstr "Inserisci la tua chiave di licenza per ricevere aggiornamenti e supporto."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Server URL"
|
||||
msgstr "URL server licenza"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The URL of the license server."
|
||||
msgstr "L'URL del server di licenza."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Key"
|
||||
msgstr "Chiave di licenza"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Your license key for this plugin."
|
||||
msgstr "La tua chiave di licenza per questo plugin."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Status"
|
||||
msgstr "Stato licenza"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validate License"
|
||||
msgstr "Verifica licenza"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activate License"
|
||||
msgstr "Attiva licenza"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "No license activated"
|
||||
msgstr "Nessuna licenza attivata"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Active"
|
||||
msgstr "Licenza attiva"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Invalid"
|
||||
msgstr "Licenza non valida"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Lifetime License"
|
||||
msgstr "Licenza a vita"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Expires: %s"
|
||||
msgstr "Scade il: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Last checked: %s"
|
||||
msgstr "Ultima verifica: %s"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License is valid!"
|
||||
msgstr "La licenza è valida!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License activated successfully!"
|
||||
msgstr "Licenza attivata con successo!"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Permission denied."
|
||||
msgstr "Accesso negato."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Server Secret"
|
||||
msgstr "Segreto del server"
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The shared secret for secure communication with the license server."
|
||||
msgstr "Il segreto condiviso per la comunicazione sicura con il server di licenza."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License key, server URL, and server secret are required."
|
||||
msgstr "La chiave di licenza, l'URL del server e il segreto del server sono obbligatori."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
#. translators: %d: Number of seconds to wait
|
||||
msgid "Rate limit exceeded. Please try again in %d seconds."
|
||||
msgstr "Limite di richieste superato. Per favore riprova tra %d secondi."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Response signature verification failed. Please check your server secret."
|
||||
msgstr "Verifica della firma della risposta fallita. Per favore controlla il tuo segreto del server."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "An unexpected error occurred. Please try again."
|
||||
msgstr "Si è verificato un errore imprevisto. Per favore riprova."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Please enter license server URL, license key, and server secret."
|
||||
msgstr "Inserisci l'URL del server di licenza, la chiave di licenza e il segreto del server."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validation failed."
|
||||
msgstr "Verifica fallita."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activation failed."
|
||||
msgstr "Attivazione fallita."
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Request failed. Please try again."
|
||||
msgstr "Richiesta fallita. Per favore riprova."
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# This file is distributed under the GPL v2 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.6\n"
|
||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.3.1\n"
|
||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||
"POT-Creation-Date: 2025-12-21 00:00+0000\n"
|
||||
"POT-Creation-Date: 2026-01-27 00:00+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -132,6 +132,26 @@ msgstr ""
|
||||
msgid "Only allow quantities defined in packages above"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:42
|
||||
msgid "Default Tier & Package Pricing for All Variations"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:43
|
||||
msgid "Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:44
|
||||
msgid "Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:87
|
||||
msgid "Restrict to Package Quantities (Default)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-product-meta.php:88
|
||||
msgid "Default restriction setting for all variations. Only allow quantities defined in packages above."
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/tier-row.twig:9
|
||||
msgid "Minimum Quantity"
|
||||
msgstr ""
|
||||
@@ -230,3 +250,124 @@ msgstr ""
|
||||
#: includes/class-wc-tpp-frontend.php:178
|
||||
msgid "View options for %s"
|
||||
msgstr ""
|
||||
|
||||
#: wc-tier-and-package-prices.php
|
||||
msgid "WooCommerce Tier and Package Prices requires PHP 8.3 or higher. Your server is running PHP %s."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Management"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Enter your license key to receive updates and support."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Server URL"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The URL of the license server."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Key"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Your license key for this plugin."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Status"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validate License"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activate License"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "No license activated"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Active"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License Invalid"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Lifetime License"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Expires: %s"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Last checked: %s"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License is valid!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License activated successfully!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Permission denied."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Server Secret"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "The shared secret for secure communication with the license server."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "License key, server URL, and server secret are required."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
#. translators: %d: Number of seconds to wait
|
||||
msgid "Rate limit exceeded. Please try again in %d seconds."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Response signature verification failed. Please check your server secret."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "An unexpected error occurred. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Please enter license server URL, license key, and server secret."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Validation failed."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Activation failed."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-tpp-settings.php
|
||||
msgid "Request failed. Please try again."
|
||||
msgstr ""
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
# WooCommerce Tier and Package Prices - Releases
|
||||
|
||||
This directory contains production-ready releases of the WooCommerce Tier and Package Prices plugin.
|
||||
|
||||
## Latest Release
|
||||
|
||||
**Version 1.1.20** - December 23, 2025
|
||||
|
||||
### What's New in 1.1.20
|
||||
- 🔧 **CRITICAL FIX:** Resolved WooCommerce Blocks fatal error in mini-cart and cart blocks
|
||||
- ✅ Full WooCommerce Blocks compatibility (cart, mini-cart, checkout)
|
||||
- ✅ Fixed `woocommerce_store_api_product_quantity_editable` filter signature
|
||||
- ✅ Tier labels for enhanced UX (v1.1.7)
|
||||
- ✅ Package quantity restrictions (v1.1.0)
|
||||
- ✅ Clickable tier rows with auto-fill
|
||||
- ✅ Enhanced "View Options" buttons
|
||||
|
||||
### Quick Install
|
||||
|
||||
```bash
|
||||
# Download the package
|
||||
wget https://your-domain.com/releases/wc-tier-and-package-prices-1.1.20.zip
|
||||
|
||||
# Verify checksum (optional but recommended)
|
||||
sha256sum wc-tier-and-package-prices-1.1.20.zip
|
||||
|
||||
# Install via WordPress admin or WP-CLI
|
||||
wp plugin install wc-tier-and-package-prices-1.1.20.zip --activate
|
||||
```
|
||||
|
||||
## Files in This Directory
|
||||
|
||||
### Release Packages
|
||||
|
||||
| File | Description | Size |
|
||||
|------|-------------|------|
|
||||
| `wc-tier-and-package-prices-1.1.20.zip` | Production plugin package | ~400 KB |
|
||||
| `wc-tier-and-package-prices-1.1.20.zip.sha256` | SHA-256 checksum | - |
|
||||
| `wc-tier-and-package-prices-1.1.20.zip.md5` | MD5 checksum | - |
|
||||
| `RELEASE-INFO-1.1.20.md` | Detailed release information | - |
|
||||
|
||||
## Verification
|
||||
|
||||
### Verify Package Integrity
|
||||
|
||||
**Using SHA-256:**
|
||||
```bash
|
||||
sha256sum -c wc-tier-and-package-prices-1.1.20.zip.sha256
|
||||
```
|
||||
|
||||
**Using MD5:**
|
||||
```bash
|
||||
md5sum -c wc-tier-and-package-prices-1.1.20.zip.md5
|
||||
```
|
||||
|
||||
### Expected Checksums
|
||||
|
||||
Checksums will be generated when the release package is created.
|
||||
|
||||
## Installation Methods
|
||||
|
||||
### Method 1: WordPress Admin (Recommended for most users)
|
||||
|
||||
1. Download `wc-tier-and-package-prices-1.1.20.zip`
|
||||
2. Go to **WordPress Admin > Plugins > Add New**
|
||||
3. Click **Upload Plugin**
|
||||
4. Choose the downloaded ZIP file
|
||||
5. Click **Install Now**
|
||||
6. Click **Activate Plugin**
|
||||
|
||||
### Method 2: WP-CLI (For developers)
|
||||
|
||||
```bash
|
||||
wp plugin install /path/to/wc-tier-and-package-prices-1.1.20.zip --activate
|
||||
```
|
||||
|
||||
### Method 3: Manual Installation (Advanced)
|
||||
|
||||
```bash
|
||||
# Extract to wp-content/plugins/
|
||||
unzip wc-tier-and-package-prices-1.1.20.zip -d /path/to/wordpress/wp-content/plugins/
|
||||
|
||||
# Set correct permissions
|
||||
chmod -R 755 /path/to/wordpress/wp-content/plugins/wc-tier-and-package-prices
|
||||
|
||||
# Activate via WordPress admin or WP-CLI
|
||||
wp plugin activate wc-tier-and-package-prices
|
||||
```
|
||||
|
||||
## What's Included
|
||||
|
||||
### Core Features
|
||||
- ✅ Tier pricing with optional labels (quantity-based discounts)
|
||||
- ✅ Package pricing with quantity restrictions (fixed-price bundles)
|
||||
- ✅ Clickable tier rows with auto-quantity fill
|
||||
- ✅ WooCommerce Blocks full support (cart, mini-cart, checkout)
|
||||
- ✅ Twig template engine for secure templating
|
||||
- ✅ WooCommerce HPOS (High-Performance Order Storage) compatible
|
||||
- ✅ Quantity restriction enforcement
|
||||
- ✅ "View Options" catalog buttons for restricted products
|
||||
- ✅ Multilingual support (3 languages)
|
||||
|
||||
### Translations
|
||||
- 🇺🇸 English (US)
|
||||
- 🇩🇪 German (Germany)
|
||||
- 🇨🇭 German (Switzerland, Informal)
|
||||
|
||||
### Production Ready
|
||||
- ✅ Optimized autoloader
|
||||
- ✅ No development dependencies
|
||||
- ✅ Compiled Twig templates support
|
||||
- ✅ Tested with WooCommerce 8.0 - 10.x
|
||||
- ✅ Tested with WordPress 6.0 - 6.9.x
|
||||
- ✅ PHP 7.4+ compatible
|
||||
- ✅ Block-based themes compatible
|
||||
|
||||
## Package Contents
|
||||
|
||||
```
|
||||
wc-tier-and-package-prices/
|
||||
├── assets/ # CSS and JavaScript
|
||||
│ ├── css/
|
||||
│ │ ├── admin.css
|
||||
│ │ └── frontend.css
|
||||
│ └── js/
|
||||
│ ├── admin.js
|
||||
│ └── frontend.js
|
||||
├── includes/ # PHP classes
|
||||
│ ├── class-wc-tpp-admin.php
|
||||
│ ├── class-wc-tpp-settings.php
|
||||
│ ├── class-wc-tpp-cart.php
|
||||
│ ├── class-wc-tpp-frontend.php
|
||||
│ ├── class-wc-tpp-product-meta.php
|
||||
│ └── class-wc-tpp-template-loader.php
|
||||
├── languages/ # Translation files
|
||||
│ ├── wc-tier-package-prices-de_CH_informal.po
|
||||
│ ├── wc-tier-package-prices-de_CH_informal.mo
|
||||
│ ├── wc-tier-package-prices-de_DE.po
|
||||
│ ├── wc-tier-package-prices-de_DE.mo
|
||||
│ ├── wc-tier-package-prices-en_US.po
|
||||
│ ├── wc-tier-package-prices-en_US.mo
|
||||
│ └── wc-tier-package-prices.pot
|
||||
├── templates/ # Twig templates
|
||||
│ ├── admin/
|
||||
│ │ ├── tier-row.twig
|
||||
│ │ └── package-row.twig
|
||||
│ └── frontend/
|
||||
│ ├── pricing-table.twig
|
||||
│ ├── tier-pricing-table.twig
|
||||
│ └── package-pricing-display.twig
|
||||
├── vendor/ # Composer dependencies
|
||||
│ └── twig/twig/
|
||||
├── CHANGELOG.md
|
||||
├── INSTALLATION.md
|
||||
├── QUICKSTART.md
|
||||
├── USAGE_EXAMPLES.md
|
||||
├── README.md
|
||||
├── composer.json
|
||||
└── wc-tier-and-package-prices.php
|
||||
```
|
||||
|
||||
## System Requirements
|
||||
|
||||
| Requirement | Minimum Version | Tested Up To |
|
||||
|-------------|----------------|--------------|
|
||||
| WordPress | 6.0+ | 6.9.x |
|
||||
| PHP | 7.4+ | 8.x |
|
||||
| WooCommerce | 8.0+ | 10.x |
|
||||
| MySQL | 5.6+ | 8.x |
|
||||
|
||||
## Support
|
||||
|
||||
- **Documentation:** See README.md, INSTALLATION.md, QUICKSTART.md, USAGE_EXAMPLES.md
|
||||
- **Repository:** https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices
|
||||
- **Author:** Marco Graetsch
|
||||
|
||||
## Version History
|
||||
|
||||
### 1.1.20 (2025-12-23) - Current Release
|
||||
- **CRITICAL FIX:** WooCommerce Blocks fatal error resolved
|
||||
- Fixed filter signature for `woocommerce_store_api_product_quantity_editable`
|
||||
- Full compatibility with WooCommerce Store API and block-based cart/checkout
|
||||
- Enhanced stability for block-based themes
|
||||
|
||||
### 1.1.7 (2025-12-22)
|
||||
- Added optional tier labels for enhanced UX
|
||||
- Clickable tier rows with auto-quantity fill
|
||||
- Add to Cart button auto-disable for invalid quantities
|
||||
|
||||
### 1.1.4 (2025-12-22)
|
||||
- WooCommerce Blocks support (cart, mini-cart, checkout)
|
||||
- Enhanced "View Options" button styling
|
||||
|
||||
### 1.1.0 (2025-12-21)
|
||||
- Package quantity restrictions (global and per-product)
|
||||
- Quantity field hiding for restricted products
|
||||
- Server-side validation for package quantities
|
||||
|
||||
### 1.0.1 (2025-12-21)
|
||||
- Added Twig template engine
|
||||
- Added Swiss German translation
|
||||
- Improved template organization
|
||||
- Enhanced security with auto-escaping
|
||||
|
||||
### 1.0.0 (2025-12-21)
|
||||
- Initial release
|
||||
- Tier pricing functionality
|
||||
- Package pricing functionality
|
||||
- German and English translations
|
||||
|
||||
For complete version history, see [CHANGELOG.md](../CHANGELOG.md)
|
||||
|
||||
## License
|
||||
|
||||
GPL v2 or later - https://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
---
|
||||
|
||||
**Note:** All packages are production-ready with optimized autoloaders and no development dependencies included.
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
e6cfc9b88df9e7763be0cd56517ce8ab wc-tier-and-package-prices-1.0.1.zip
|
||||
@@ -1 +0,0 @@
|
||||
92c1385d92527e77646e37f23c1bd1555a4290a5ec9314c0ee6ed896ded55e88 wc-tier-and-package-prices-1.0.1.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
830f443ce4b65e2ca9cfede3257bc4f5 wc-tier-and-package-prices-1.0.2.zip
|
||||
@@ -1 +0,0 @@
|
||||
c1a5339da10b3625156b8fff4ec848e4a1318d6edc497bd5026cfe0a3ef39daa wc-tier-and-package-prices-1.0.2.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
ef68125c54b0c10f04ba82d48a98b4aa wc-tier-and-package-prices-1.1.0.zip
|
||||
@@ -1 +0,0 @@
|
||||
da6b462f3dc297b282ed0da258b78fd9f2f82f3e76289c4c8fadd1ac9e02c55b wc-tier-and-package-prices-1.1.0.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
51c4f8a7c3ccede2d2005f2fe3ebe44e wc-tier-and-package-prices-1.1.1.zip
|
||||
@@ -1 +0,0 @@
|
||||
b951f8b7ddd2bad6b3415d4583709fdf88f66aea4eae70110c903757ff53e045 wc-tier-and-package-prices-1.1.1.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
81be5283219cfa722f6d382a788e7dc1 releases/wc-tier-and-package-prices-1.1.10.zip
|
||||
@@ -1 +0,0 @@
|
||||
2d3b01e61c8a03a8f20bc99b2019ca50fa08ecd68188feb2d2105dfe35d36f0d releases/wc-tier-and-package-prices-1.1.10.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
4a0c0b07b29d4b7046f9d3ff3f091321 releases/wc-tier-and-package-prices-1.1.11.zip
|
||||
@@ -1 +0,0 @@
|
||||
3da9423d136a2ff254b61577ba1f84d4c0f0d1e57bae361ac29c90327feeeceb releases/wc-tier-and-package-prices-1.1.11.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
c1c0b5880636686227246be2c37dc42a releases/wc-tier-and-package-prices-1.1.12.zip
|
||||
@@ -1 +0,0 @@
|
||||
05b32356d46803dbb7fa17c13a2d8da96f77126746e2895e2f5c6dd0e7b490ff releases/wc-tier-and-package-prices-1.1.12.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
8572eed399554905fbf331d18f0677a0 wc-tier-and-package-prices-1.1.13.zip
|
||||
@@ -1 +0,0 @@
|
||||
83e29b2e40dd43e77bd83cd03d4ccc54ef53555b55544eba4d38161101f79f20 wc-tier-and-package-prices-1.1.13.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
e0cc51d1493ed35ab254220d9f46997b wc-tier-and-package-prices-1.1.14.zip
|
||||
@@ -1 +0,0 @@
|
||||
8a2ce7438ee49baffdcaaf323b6426d73dd1cf704bea94a80fcce27a42c097ad wc-tier-and-package-prices-1.1.14.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
15fa0e0933c85b23f66940bf43810835 wc-tier-and-package-prices-1.1.15.zip
|
||||
@@ -1 +0,0 @@
|
||||
a419579111ad20b127411e1078ca99187156d606381549e6bf147ffc3bd58de1 wc-tier-and-package-prices-1.1.15.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
d30a90715dbcd46a1dfd19f025897530 wc-tier-and-package-prices-1.1.16.zip
|
||||
@@ -1 +0,0 @@
|
||||
e2ad36e049a902b8e287154867ef72c0e169766508781e223176a2a753b60915 wc-tier-and-package-prices-1.1.16.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
9adbb9aad13b8d141cfabfdf53643480 wc-tier-and-package-prices-1.1.17.zip
|
||||
@@ -1 +0,0 @@
|
||||
985a195bf98d4dbc0a7afa90173efcda472f4c769adf3c833fa6a99ba9d44095 wc-tier-and-package-prices-1.1.17.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
ec4bb1d78a3c27488244b44971916ffd wc-tier-and-package-prices-1.1.18.zip
|
||||
@@ -1 +0,0 @@
|
||||
7d942002edd866c2b6f3192ba010fe64058b7433c5ac776a48e9c3c41f4e2fda wc-tier-and-package-prices-1.1.18.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
c61c3a059429d8dacdce71d4acce401e wc-tier-and-package-prices-1.1.19.zip
|
||||
@@ -1 +0,0 @@
|
||||
0e8bc4ccd233d388238e800cd0e0a129f9e8da14008e7164db7934a48ca8223a wc-tier-and-package-prices-1.1.19.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
eee69fcf391b3f3df9380306ffb31b1b wc-tier-and-package-prices-1.1.2.zip
|
||||
@@ -1 +0,0 @@
|
||||
40ffd29ebc6af635f689472040acd220ae1c8df2f0d852fab4b43ce0fb5fe739 wc-tier-and-package-prices-1.1.2.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
bfdeee75bfe3795c9ab9abfe47f12a41 wc-tier-and-package-prices-1.1.20.zip
|
||||
@@ -1 +0,0 @@
|
||||
953859241d15d76ec4783c72bac851ddd69e5a1f7b119ee4f9ebd30c7fabed17 wc-tier-and-package-prices-1.1.20.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
16813b3ed0d1001d5f60194d61d36fc2 wc-tier-and-package-prices-1.1.21.zip
|
||||
@@ -1 +0,0 @@
|
||||
e0063852a9ac23b1fd994471a2829f9dcbe26316f00ddee2d00f77c7c6a47c8f wc-tier-and-package-prices-1.1.21.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
7d5a5c7980a91dff5167c90a6f3290b0 wc-tier-and-package-prices-1.1.22.zip
|
||||
@@ -1 +0,0 @@
|
||||
f94dee838a3f288b4acb3b3d9a4e88ef987f9b1bc918403186014d8d43fee6d9 wc-tier-and-package-prices-1.1.22.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
dfec91be7e375b09613ba81cfebbe013 wc-tier-and-package-prices-1.1.3.zip
|
||||
@@ -1 +0,0 @@
|
||||
7938542680b71a7b73269c96a4dff78f2222ac8409092011c5e40e97a5e465aa wc-tier-and-package-prices-1.1.3.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
3e5bc2cae17ecb81b729c3fdc979df23 wc-tier-and-package-prices-1.1.4.zip
|
||||
@@ -1 +0,0 @@
|
||||
19553b2fed1c6ca20a8168eab8c570cb0302be801322cd41d86cec40b70ff162 wc-tier-and-package-prices-1.1.4.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
e9f8a69e4be107d857d3beb671d5a9fe wc-tier-and-package-prices-1.1.5.zip
|
||||
@@ -1 +0,0 @@
|
||||
a13d71f3f65c7cf41613f88d7bcfcb112acfefb800fa6b95932f44a47cf764f3 wc-tier-and-package-prices-1.1.5.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
dbea10acffdc849f9aa387d128cb6d6e wc-tier-and-package-prices-1.1.6.zip
|
||||
@@ -1 +0,0 @@
|
||||
730e366764449ac963bc85848ac8a91f654e4b35500ed3132a280ab4f215c80c wc-tier-and-package-prices-1.1.6.zip
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
c593b6fc730133ecaf772f77d5659080 wc-tier-and-package-prices-1.1.7.zip
|
||||
@@ -1 +0,0 @@
|
||||
00e7c473a8b0fc23eb1cd52f4b38db030618ed142a9a9fdfd9b1aa4c5849ec70 wc-tier-and-package-prices-1.1.7.zip
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user