You've already forked wc-tier-and-package-prices
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1542060ca6 | |||
| e17bdefe06 | |||
| fa26247d1b | |||
| a4b84f7e41 | |||
| 2e9c948a07 | |||
| dd4333bd11 | |||
| b909221ae2 | |||
| d80c9d90f9 | |||
| 2bf0cd82fe | |||
| 9451cc1965 | |||
| 02b0308058 | |||
| 38e9506d4e | |||
| 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 | |||
| 87784f467a | |||
| 45a89fc693 | |||
| f530b37285 | |||
| 1e6d86ca10 | |||
| f958c7b640 | |||
| b1d31f4894 | |||
| 0c75234dcb | |||
| 556cba20fa |
0
.claude/commands/.gitignore
vendored
Normal file
0
.claude/commands/.gitignore
vendored
Normal file
@@ -18,7 +18,31 @@
|
|||||||
"Bash(done)",
|
"Bash(done)",
|
||||||
"Bash(git commit:*)",
|
"Bash(git commit:*)",
|
||||||
"Bash(node -c:*)",
|
"Bash(node -c:*)",
|
||||||
"Bash(php -l:*)"
|
"Bash(php -l:*)",
|
||||||
|
"Bash(git push:*)",
|
||||||
|
"Bash(git checkout:*)",
|
||||||
|
"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:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
217
.gitea/workflows/release.yml
Normal file
217
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
name: Create Release Package
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.3'
|
||||||
|
extensions: mbstring, xml, zip, intl, gettext
|
||||||
|
tools: composer:v2
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF_NAME#v}
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Building version: $VERSION"
|
||||||
|
|
||||||
|
- name: Validate composer.json
|
||||||
|
run: composer validate --strict
|
||||||
|
|
||||||
|
- name: Install Composer dependencies (production)
|
||||||
|
run: |
|
||||||
|
composer config platform.php 8.3.0
|
||||||
|
composer install --no-dev --optimize-autoloader --no-interaction
|
||||||
|
|
||||||
|
- name: Install gettext
|
||||||
|
run: apt-get update && apt-get install -y gettext
|
||||||
|
|
||||||
|
- name: Compile translations
|
||||||
|
run: |
|
||||||
|
for po in languages/*.po; do
|
||||||
|
if [ -f "$po" ]; then
|
||||||
|
mo="${po%.po}.mo"
|
||||||
|
echo "Compiling $po to $mo"
|
||||||
|
msgfmt -o "$mo" "$po"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Verify plugin version matches tag
|
||||||
|
run: |
|
||||||
|
PLUGIN_VERSION=$(grep -oP "Version:\s*\K[0-9]+\.[0-9]+\.[0-9]+" wc-tier-and-package-prices.php | head -1)
|
||||||
|
TAG_VERSION=${{ steps.version.outputs.version }}
|
||||||
|
if [ "$PLUGIN_VERSION" != "$TAG_VERSION" ]; then
|
||||||
|
echo "Error: Plugin version ($PLUGIN_VERSION) does not match tag version ($TAG_VERSION)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Version verified: $PLUGIN_VERSION"
|
||||||
|
|
||||||
|
- name: Create release directory
|
||||||
|
run: mkdir -p releases
|
||||||
|
|
||||||
|
- name: Build release package
|
||||||
|
run: |
|
||||||
|
VERSION=${{ steps.version.outputs.version }}
|
||||||
|
PLUGIN_NAME="wc-tier-and-package-prices"
|
||||||
|
RELEASE_FILE="releases/${PLUGIN_NAME}-${VERSION}.zip"
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
zip -r "${PLUGIN_NAME}/${RELEASE_FILE}" "${PLUGIN_NAME}" \
|
||||||
|
-x "${PLUGIN_NAME}/.git/*" \
|
||||||
|
-x "${PLUGIN_NAME}/.gitea/*" \
|
||||||
|
-x "${PLUGIN_NAME}/.github/*" \
|
||||||
|
-x "${PLUGIN_NAME}/.vscode/*" \
|
||||||
|
-x "${PLUGIN_NAME}/.idea/*" \
|
||||||
|
-x "${PLUGIN_NAME}/.claude/*" \
|
||||||
|
-x "${PLUGIN_NAME}/CLAUDE.md" \
|
||||||
|
-x "${PLUGIN_NAME}/wordpress" \
|
||||||
|
-x "${PLUGIN_NAME}/wordpress/*" \
|
||||||
|
-x "${PLUGIN_NAME}/core" \
|
||||||
|
-x "${PLUGIN_NAME}/core/*" \
|
||||||
|
-x "${PLUGIN_NAME}/wp-core" \
|
||||||
|
-x "${PLUGIN_NAME}/wp-core/*" \
|
||||||
|
-x "${PLUGIN_NAME}/releases/*" \
|
||||||
|
-x "${PLUGIN_NAME}/composer.lock" \
|
||||||
|
-x "${PLUGIN_NAME}/*.log" \
|
||||||
|
-x "${PLUGIN_NAME}/logs/*" \
|
||||||
|
-x "${PLUGIN_NAME}/.gitignore" \
|
||||||
|
-x "${PLUGIN_NAME}/.gitmodules" \
|
||||||
|
-x "${PLUGIN_NAME}/.editorconfig" \
|
||||||
|
-x "${PLUGIN_NAME}/phpcs.xml*" \
|
||||||
|
-x "${PLUGIN_NAME}/phpunit.xml*" \
|
||||||
|
-x "${PLUGIN_NAME}/tests/*" \
|
||||||
|
-x "${PLUGIN_NAME}/templates/cache/*" \
|
||||||
|
-x "${PLUGIN_NAME}/notes.*" \
|
||||||
|
-x "${PLUGIN_NAME}/*.po~" \
|
||||||
|
-x "${PLUGIN_NAME}/*.bak" \
|
||||||
|
-x "${PLUGIN_NAME}/lib/*/.git/*" \
|
||||||
|
-x "${PLUGIN_NAME}/lib/*/CLAUDE.md" \
|
||||||
|
-x "${PLUGIN_NAME}/vendor/magdev/*/.git/*" \
|
||||||
|
-x "${PLUGIN_NAME}/vendor/magdev/*/CLAUDE.md" \
|
||||||
|
-x "*.DS_Store" \
|
||||||
|
-x "*Thumbs.db"
|
||||||
|
|
||||||
|
cd "${PLUGIN_NAME}"
|
||||||
|
echo "Created: ${RELEASE_FILE}"
|
||||||
|
ls -lh "${RELEASE_FILE}"
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
run: |
|
||||||
|
VERSION=${{ steps.version.outputs.version }}
|
||||||
|
PLUGIN_NAME="wc-tier-and-package-prices"
|
||||||
|
RELEASE_FILE="releases/${PLUGIN_NAME}-${VERSION}.zip"
|
||||||
|
|
||||||
|
cd releases
|
||||||
|
sha256sum "${PLUGIN_NAME}-${VERSION}.zip" > "${PLUGIN_NAME}-${VERSION}.zip.sha256"
|
||||||
|
echo "SHA256:"
|
||||||
|
cat "${PLUGIN_NAME}-${VERSION}.zip.sha256"
|
||||||
|
|
||||||
|
- name: Verify package structure
|
||||||
|
run: |
|
||||||
|
set +o pipefail
|
||||||
|
VERSION=${{ steps.version.outputs.version }}
|
||||||
|
PLUGIN_NAME="wc-tier-and-package-prices"
|
||||||
|
|
||||||
|
echo "Package contents (first 50 entries):"
|
||||||
|
unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | head -50 || true
|
||||||
|
|
||||||
|
# Verify main plugin file exists
|
||||||
|
if unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | grep -q "${PLUGIN_NAME}/${PLUGIN_NAME}.php"; then
|
||||||
|
echo "✓ Main plugin file at correct location"
|
||||||
|
else
|
||||||
|
echo "✗ Error: Main plugin file not found at ${PLUGIN_NAME}/${PLUGIN_NAME}.php"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify vendor directory included
|
||||||
|
if unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | grep -q "${PLUGIN_NAME}/vendor/"; then
|
||||||
|
echo "✓ Vendor directory included"
|
||||||
|
else
|
||||||
|
echo "✗ Error: Vendor directory not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify excluded files are not present
|
||||||
|
if unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | grep -qE "CLAUDE\.md|\.claude/|\.git/|wordpress/"; then
|
||||||
|
echo "✗ Error: Excluded files found in package"
|
||||||
|
unzip -l "releases/${PLUGIN_NAME}-${VERSION}.zip" | grep -E "CLAUDE\.md|\.claude/|\.git/|wordpress/"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✓ No excluded files in package"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Extract changelog for release notes
|
||||||
|
id: changelog
|
||||||
|
run: |
|
||||||
|
VERSION=${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
# Extract release notes from CHANGELOG.md
|
||||||
|
NOTES=$(sed -n "/^## \[${VERSION}\]/,/^## \[/p" CHANGELOG.md | sed '$ d' | tail -n +2)
|
||||||
|
if [ -z "$NOTES" ]; then
|
||||||
|
NOTES="Release version ${VERSION}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$NOTES" > release_notes.txt
|
||||||
|
echo "Release notes extracted"
|
||||||
|
|
||||||
|
- name: Create Gitea Release
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.SRC_GITEA_TOKEN }}
|
||||||
|
run: |
|
||||||
|
VERSION=${{ steps.version.outputs.version }}
|
||||||
|
TAG_NAME=${{ github.ref_name }}
|
||||||
|
PLUGIN_NAME="wc-tier-and-package-prices"
|
||||||
|
|
||||||
|
# Check if this is a prerelease (contains hyphen like v1.0.0-beta)
|
||||||
|
PRERELEASE="false"
|
||||||
|
if [[ "$TAG_NAME" == *-* ]]; then
|
||||||
|
PRERELEASE="true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BODY=$(cat release_notes.txt)
|
||||||
|
|
||||||
|
# Create release via Gitea API
|
||||||
|
RELEASE_RESPONSE=$(curl -s -X POST \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"tag_name\": \"${TAG_NAME}\", \"name\": \"Release ${VERSION}\", \"body\": $(echo "$BODY" | jq -Rs .), \"draft\": false, \"prerelease\": ${PRERELEASE}}" \
|
||||||
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases")
|
||||||
|
|
||||||
|
RELEASE_ID=$(echo "$RELEASE_RESPONSE" | jq -r '.id')
|
||||||
|
|
||||||
|
if [ "$RELEASE_ID" == "null" ] || [ -z "$RELEASE_ID" ]; then
|
||||||
|
echo "Failed to create release:"
|
||||||
|
echo "$RELEASE_RESPONSE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Created release ID: $RELEASE_ID"
|
||||||
|
|
||||||
|
# Upload release assets
|
||||||
|
for file in "releases/${PLUGIN_NAME}-${VERSION}.zip" "releases/${PLUGIN_NAME}-${VERSION}.zip.sha256"; do
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
FILENAME=$(basename "$file")
|
||||||
|
echo "Uploading $FILENAME..."
|
||||||
|
curl -s -X POST \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary "@$file" \
|
||||||
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${FILENAME}"
|
||||||
|
echo "Uploaded $FILENAME"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Release created successfully: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${TAG_NAME}"
|
||||||
14
.gitignore
vendored
Normal file → Executable file
14
.gitignore
vendored
Normal file → Executable file
@@ -29,3 +29,17 @@ notes.*
|
|||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
._*
|
._*
|
||||||
|
|
||||||
|
# local code
|
||||||
|
wordpress
|
||||||
|
core
|
||||||
|
wp-core
|
||||||
|
|
||||||
|
# Releases (not tracked in git)
|
||||||
|
/releases/
|
||||||
|
|
||||||
|
# Claude Code local settings
|
||||||
|
.claude/settings.json
|
||||||
|
|
||||||
|
# Marketing texts (not for public distribution)
|
||||||
|
MARKETING.md
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "lib/wc-licensed-product-client"]
|
||||||
|
path = lib/wc-licensed-product-client
|
||||||
|
url = ../wc-licensed-product-client.git
|
||||||
587
CHANGELOG.md
587
CHANGELOG.md
@@ -5,6 +5,593 @@ All notable changes to WooCommerce Tier and Package Prices will be documented in
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.4.1] - 2026-02-03
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Localhost License Bypass**: License validation is automatically bypassed for localhost environments
|
||||||
|
- Supports localhost, 127.0.0.1, ::1 (IPv6), `*.localhost`, `*.local`, `*.test`, `*.example`, `*.invalid`
|
||||||
|
- Private IP ranges (Docker, VMs) are also recognized as localhost
|
||||||
|
- Displays "(Localhost environment - license validation bypassed)" in License Status
|
||||||
|
|
||||||
|
- **Self-Licensing Bypass**: License validation is bypassed when the site URL matches the license server URL
|
||||||
|
- Useful for testing the plugin on the license server itself
|
||||||
|
- Displays "(Self-licensing server - license validation bypassed)" in License Status
|
||||||
|
|
||||||
|
- **Auto-Update System**: Plugin can now receive updates directly from the license server
|
||||||
|
- New "Auto-Updates" settings tab under WooCommerce > Tier & Package Prices
|
||||||
|
- **Enable Update Notifications**: Toggle to check for available updates (default: enabled)
|
||||||
|
- **Automatically Install Updates**: Optional auto-install when WordPress performs background updates
|
||||||
|
- **Check Frequency**: Configurable hours between update checks (1-168 hours, default: 12)
|
||||||
|
- **Update Status**: Shows current version and available update with "Check for Updates" button
|
||||||
|
- Updates appear in WordPress Dashboard > Updates with full plugin info
|
||||||
|
|
||||||
|
- **New Classes**:
|
||||||
|
- `WC_TPP_License_Checker`: Singleton class handling license validation with localhost/self-licensing bypass
|
||||||
|
- `WC_TPP_Update_Checker`: Singleton class integrating with WordPress plugin update system
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
|
||||||
|
**License Checker Features**:
|
||||||
|
|
||||||
|
- `is_localhost()`: Detects localhost environments using multiple patterns
|
||||||
|
- `is_self_licensing()`: Compares license server URL with site URL
|
||||||
|
- `is_license_valid()`: Main entry point with bypass logic
|
||||||
|
- `get_bypass_reason()`: Returns 'localhost', 'self_licensing', or null
|
||||||
|
- Cached validation (1 hour success, 5 minutes error)
|
||||||
|
|
||||||
|
**Update Checker Features**:
|
||||||
|
|
||||||
|
- Hooks into `pre_set_site_transient_update_plugins` for update detection
|
||||||
|
- Hooks into `plugins_api` for update modal information
|
||||||
|
- Hooks into `http_request_args` for license authentication
|
||||||
|
- Hooks into `auto_update_plugin` for background updates
|
||||||
|
- Can be disabled via `WC_TPP_DISABLE_AUTO_UPDATE` constant
|
||||||
|
|
||||||
|
**New Settings Options**:
|
||||||
|
|
||||||
|
- `wc_tpp_update_notification_enabled` (yes/no, default: yes)
|
||||||
|
- `wc_tpp_auto_install_enabled` (yes/no, default: no)
|
||||||
|
- `wc_tpp_update_check_frequency` (number, default: 12)
|
||||||
|
|
||||||
|
**Update Server Endpoint**:
|
||||||
|
|
||||||
|
- POST to `/wp-json/wc-licensed-product/v1/update-check`
|
||||||
|
- Request body: `{license_key, domain, plugin_slug, current_version}`
|
||||||
|
- Response: `{success, update_available, version, download_url, ...}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [1.4.0] - 2026-01-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Gitea CI/CD Release Pipeline**: Automated release workflow triggered on version tags
|
||||||
|
- Validates plugin version matches tag version
|
||||||
|
- Installs production Composer dependencies
|
||||||
|
- Compiles translation files (.po to .mo)
|
||||||
|
- Creates release package with proper exclusions
|
||||||
|
- Generates SHA256 checksum
|
||||||
|
- Publishes release to Gitea with changelog notes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
**Major Feature**: Complete support for WooCommerce variable products with variation-level pricing
|
||||||
|
|
||||||
|
- Variable products can now have tier and package pricing configured independently for each variation
|
||||||
|
- Admin UI: Each variation displays tier/package pricing fields in the variation edit panel
|
||||||
|
- Frontend: Pricing tables load dynamically via AJAX when customer selects a variation
|
||||||
|
- Cart: Variation-specific pricing correctly applied during checkout
|
||||||
|
- Quantity restrictions work per-variation (not just per-product)
|
||||||
|
- Catalog buttons: "View Options" appears for variable products with restricted variations
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Admin Templates**: Converted tier/package row templates from `<div>` to `<tr>` table structure for better layout
|
||||||
|
- **Admin UI**: Simple product pricing fields now use table layout for consistency with variations
|
||||||
|
- **Frontend Display**: Variable products show placeholder container; pricing appears on variation selection
|
||||||
|
- **Cart Logic**: All cart methods now use "effective ID" pattern (variation ID when present, product ID otherwise)
|
||||||
|
- **Template System**: Added `field_prefix` parameter support to admin templates for variation field naming
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
|
||||||
|
#### Backend Changes
|
||||||
|
|
||||||
|
- **class-wc-tpp-cart.php**: Added variation ID resolution throughout; updated all meta lookups to use effective ID
|
||||||
|
- **class-wc-tpp-frontend.php**:
|
||||||
|
- Updated `get_tier_price()` and `get_package_price()` to accept `variation_id` parameter
|
||||||
|
- Added AJAX endpoint `ajax_get_variation_pricing()` for fetching variation pricing data
|
||||||
|
- Updated `display_pricing_table()` to detect variable products and show placeholder
|
||||||
|
- Fixed `modify_catalog_add_to_cart_button()` to check variations for restrictions
|
||||||
|
- **class-wc-tpp-product-meta.php**:
|
||||||
|
- Added hooks: `woocommerce_variation_options_pricing`, `woocommerce_save_product_variation`
|
||||||
|
- New method: `add_variation_pricing_fields()` - renders pricing UI in variation panels
|
||||||
|
- New method: `save_variation_pricing_fields()` - saves variation-specific pricing data
|
||||||
|
- New methods: `render_variation_tier_row()`, `render_variation_package_row()` - variation rendering helpers
|
||||||
|
|
||||||
|
#### Frontend Changes
|
||||||
|
|
||||||
|
- **frontend.js**:
|
||||||
|
- Added variation selector integration listening to `found_variation` and `reset_data` events
|
||||||
|
- Implemented AJAX fetching of variation pricing when variation selected
|
||||||
|
- Dynamic quantity restriction handling per-variation
|
||||||
|
- Re-initialization of event handlers for dynamically loaded pricing tables
|
||||||
|
- **admin.js**:
|
||||||
|
- Separated simple product and variation handlers
|
||||||
|
- Variation-specific add/remove tier/package row management
|
||||||
|
- Context-aware template selection using variation loop index
|
||||||
|
|
||||||
|
#### Template Changes
|
||||||
|
|
||||||
|
- **tier-row.twig**: Added `field_prefix` variable for variation field naming; changed to `<tr>` structure
|
||||||
|
- **package-row.twig**: Added `field_prefix` variable for variation field naming; changed to `<tr>` structure
|
||||||
|
|
||||||
|
#### Data Storage
|
||||||
|
|
||||||
|
- Meta keys remain the same: `_wc_tpp_tiers`, `_wc_tpp_packages`, `_wc_tpp_restrict_to_packages`
|
||||||
|
- Simple products: Stored on product post meta
|
||||||
|
- Variations: Stored on variation post meta (independent per-variation)
|
||||||
|
|
||||||
|
### Backward Compatibility
|
||||||
|
|
||||||
|
- **100% backward compatible** - No breaking changes
|
||||||
|
- Simple products continue working exactly as before
|
||||||
|
- Existing tier/package data unaffected
|
||||||
|
- No database migrations required
|
||||||
|
- Templates remain compatible (field_prefix optional)
|
||||||
|
|
||||||
|
### Migration Notes
|
||||||
|
|
||||||
|
- Existing installations can upgrade seamlessly
|
||||||
|
- Variable products simply gain new functionality
|
||||||
|
- No action required for existing simple product configurations
|
||||||
|
|
||||||
|
### Performance Considerations
|
||||||
|
|
||||||
|
- AJAX requests only made when variation selected (not on page load)
|
||||||
|
- Pricing data fetched per-variation (not all variations at once)
|
||||||
|
- Nonce verification on all AJAX requests for security
|
||||||
|
- Template rendering server-side for SEO/performance
|
||||||
|
|
||||||
|
### Testing Performed
|
||||||
|
|
||||||
|
- Simple products: All existing functionality verified
|
||||||
|
- Variable products: Tier pricing, package pricing, restrictions tested per-variation
|
||||||
|
- Mixed carts: Simple + variable products working correctly
|
||||||
|
- WooCommerce Blocks: Cart block, mini-cart block, checkout block compatibility verified
|
||||||
|
- Admin UI: Add/remove rows working for both simple products and variations
|
||||||
|
- Quantity restrictions: Enforced correctly per-variation in cart and checkout
|
||||||
|
|
||||||
|
## [1.1.22] - 2025-12-23
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Increased width of label input fields for tier pricing and package pricing in admin interface
|
||||||
|
- Changed label field CSS class from `short` to `regular` (approximately 2x wider)
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
|
||||||
|
- Updated `templates/admin/tier-row.twig` - Changed label input class from `short` to `regular`
|
||||||
|
- Updated `templates/admin/package-row.twig` - Changed label input class from `short` to `regular`
|
||||||
|
- Provides more space for descriptive labels like "Wholesale", "Bulk Discount", "Starter Pack", etc.
|
||||||
|
- Uses WooCommerce standard input field sizing classes
|
||||||
|
|
||||||
|
### Known Issues
|
||||||
|
|
||||||
|
- **Double-install bug**: When manually updating the plugin by uploading a new version, WordPress may install it as a separate plugin instead of updating the existing one
|
||||||
|
- **Workaround**: Before installing a new version, deactivate and delete the old version first, then install the new version
|
||||||
|
- **Root cause**: Plugin lacks automatic update mechanism; requires manual installation
|
||||||
|
- **Future fix**: Consider implementing update server or WordPress.org repository integration
|
||||||
|
|
||||||
|
## [1.1.21] - 2025-12-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- New translation for `de_CH` (Swiss German - formal)
|
||||||
|
- New translation for `de_DE_informal` (Informal German)
|
||||||
|
- New translation for `fr_CH` (Swiss French)
|
||||||
|
- New translation for `it_CH` (Swiss Italian)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated all translation files (.po) to version 1.1.21
|
||||||
|
- Compiled all translation files to .mo format for runtime use
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
|
||||||
|
- Created 4 new locale-specific translation files following WordPress i18n standards
|
||||||
|
- Swiss locales use CHF currency formatting in examples (CHF 50.-, CHF 100.-)
|
||||||
|
- German informal translations use "du/dein" instead of "Sie/Ihr"
|
||||||
|
- All translations maintain consistent terminology across plugin UI
|
||||||
|
- Compiled .mo files included for immediate WordPress language support
|
||||||
|
|
||||||
|
## [1.1.20] - 2025-12-22
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **CRITICAL**: Fatal error in WooCommerce Blocks cart/mini-cart: "Cannot use object of type WC_Product_Simple as array"
|
||||||
|
- Filter `woocommerce_store_api_product_quantity_editable` signature mismatch
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated `is_quantity_editable_in_blocks()` method to accept `WC_Product` object instead of cart item array
|
||||||
|
- Changed method signature from `is_quantity_editable_in_blocks($cart_item)` to `is_quantity_editable_in_blocks(WC_Product $product)`
|
||||||
|
- Use `$product->get_id()` instead of `$cart_item['product_id']` for product identification
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
|
||||||
|
- WooCommerce Store API passes product object to this filter, not cart item array
|
||||||
|
- Previous code attempted array access on product object causing fatal error
|
||||||
|
- Error occurred at `includes/class-wc-tpp-cart.php:233`
|
||||||
|
- Affects WooCommerce Blocks-based cart, mini-cart, and checkout
|
||||||
|
- Classic cart/checkout unaffected (uses different hooks)
|
||||||
|
|
||||||
## [1.1.19] - 2025-12-22
|
## [1.1.19] - 2025-12-22
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -90,8 +90,8 @@ After activation, you should see:
|
|||||||
|
|
||||||
Before installation, verify:
|
Before installation, verify:
|
||||||
|
|
||||||
- ✓ WordPress 5.8 or higher
|
- ✓ WordPress 6.0 or higher (tested up to 6.9.x)
|
||||||
- ✓ WooCommerce 5.0 or higher installed and activated
|
- ✓ WooCommerce 8.0 or higher (tested up to 10.x) installed and activated
|
||||||
- ✓ PHP 7.4 or higher
|
- ✓ PHP 7.4 or higher
|
||||||
- ✓ Write permissions in `/wp-content/plugins/` directory
|
- ✓ Write permissions in `/wp-content/plugins/` directory
|
||||||
|
|
||||||
@@ -153,16 +153,34 @@ wp-content/
|
|||||||
├── USAGE_EXAMPLES.md
|
├── USAGE_EXAMPLES.md
|
||||||
├── includes/
|
├── includes/
|
||||||
│ ├── class-wc-tpp-admin.php
|
│ ├── class-wc-tpp-admin.php
|
||||||
|
│ ├── class-wc-tpp-settings.php
|
||||||
│ ├── class-wc-tpp-cart.php
|
│ ├── class-wc-tpp-cart.php
|
||||||
│ ├── class-wc-tpp-frontend.php
|
│ ├── class-wc-tpp-frontend.php
|
||||||
│ └── class-wc-tpp-product-meta.php
|
│ ├── class-wc-tpp-product-meta.php
|
||||||
└── assets/
|
│ └── class-wc-tpp-template-loader.php
|
||||||
├── css/
|
├── templates/
|
||||||
│ ├── admin.css
|
│ ├── admin/
|
||||||
│ └── frontend.css
|
│ │ ├── tier-row.twig
|
||||||
└── js/
|
│ │ └── package-row.twig
|
||||||
├── admin.js
|
│ └── frontend/
|
||||||
└── frontend.js
|
│ ├── pricing-table.twig
|
||||||
|
│ ├── tier-pricing-table.twig
|
||||||
|
│ └── package-pricing-display.twig
|
||||||
|
├── assets/
|
||||||
|
│ ├── css/
|
||||||
|
│ │ ├── admin.css
|
||||||
|
│ │ └── frontend.css
|
||||||
|
│ └── js/
|
||||||
|
│ ├── admin.js
|
||||||
|
│ └── frontend.js
|
||||||
|
├── languages/
|
||||||
|
│ ├── wc-tier-package-prices.pot
|
||||||
|
│ ├── wc-tier-package-prices-de_DE.po
|
||||||
|
│ ├── wc-tier-package-prices-de_DE.mo
|
||||||
|
│ ├── wc-tier-package-prices-de_CH_informal.po
|
||||||
|
│ └── wc-tier-package-prices-de_CH_informal.mo
|
||||||
|
└── vendor/
|
||||||
|
└── (Twig and dependencies)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Quick Start Guide
|
# Quick Start Guide
|
||||||
|
|
||||||
Get started with WooCommerce Tier and Package Prices in 5 minutes!
|
Get started with WooCommerce Tier and Package Prices (v1.1.20) in 5 minutes!
|
||||||
|
|
||||||
## Step 1: Install (2 minutes)
|
## Step 1: Install (2 minutes)
|
||||||
|
|
||||||
@@ -79,8 +79,11 @@ Use packages for common bundles (6-pack, dozen, case)
|
|||||||
1. **Round Numbers**: Use 10, 25, 50, 100 for tiers
|
1. **Round Numbers**: Use 10, 25, 50, 100 for tiers
|
||||||
2. **Meaningful Savings**: Offer at least 10% off per tier
|
2. **Meaningful Savings**: Offer at least 10% off per tier
|
||||||
3. **Label Packages**: "Family Pack" sells better than "4-pack"
|
3. **Label Packages**: "Family Pack" sells better than "4-pack"
|
||||||
4. **Test Checkout**: Always complete a test order
|
4. **Label Tiers**: Use descriptive labels like "Wholesale Price" or "Bulk Discount"
|
||||||
5. **Mobile Check**: View on phone to verify responsiveness
|
5. **Test Checkout**: Always complete a test order
|
||||||
|
6. **Mobile Check**: View on phone to verify responsiveness
|
||||||
|
7. **Quantity Restrictions**: Enable package restrictions to prevent arbitrary quantities
|
||||||
|
8. **Blocks Compatible**: Works with both classic and block-based carts/checkout
|
||||||
|
|
||||||
## Need More Help?
|
## Need More Help?
|
||||||
|
|
||||||
|
|||||||
141
README.md
141
README.md
@@ -20,10 +20,11 @@ A powerful WooCommerce plugin that adds tier pricing and package pricing functio
|
|||||||
|
|
||||||
### Admin Features
|
### Admin Features
|
||||||
- Easy-to-use product meta boxes for adding tiers and packages
|
- 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)
|
- Configure display position (before/after add to cart, after price)
|
||||||
- Enable/disable tier or package pricing independently
|
- Enable/disable tier or package pricing independently
|
||||||
- Sortable pricing rules
|
- Sortable pricing rules
|
||||||
|
- License management with secure HMAC signature verification
|
||||||
|
|
||||||
### Frontend Features
|
### Frontend Features
|
||||||
- Beautiful pricing tables on product pages
|
- Beautiful pricing tables on product pages
|
||||||
@@ -39,6 +40,20 @@ A powerful WooCommerce plugin that adds tier pricing and package pricing functio
|
|||||||
2. Activate the plugin through the 'Plugins' menu in WordPress
|
2. Activate the plugin through the 'Plugins' menu in WordPress
|
||||||
3. Make sure WooCommerce is installed and activated
|
3. Make sure WooCommerce is installed and activated
|
||||||
|
|
||||||
|
### Automated Releases
|
||||||
|
|
||||||
|
This project uses a Gitea CI/CD pipeline for automated releases. When a version tag (e.g., `v1.4.0`) is pushed:
|
||||||
|
|
||||||
|
1. Code is checked out with git submodules (dependencies bundled)
|
||||||
|
2. The pipeline validates the plugin version matches the tag
|
||||||
|
3. Composer dependencies are installed (production only)
|
||||||
|
4. Translation files are compiled (.po → .mo)
|
||||||
|
5. A release package is created with proper exclusions
|
||||||
|
6. SHA256 checksum is generated
|
||||||
|
7. Release is published to Gitea with changelog notes extracted from CHANGELOG.md
|
||||||
|
|
||||||
|
The `magdev/wc-licensed-product-client` library is bundled as a git submodule to avoid private repository authentication during CI/CD.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Global Settings
|
### Global Settings
|
||||||
@@ -110,12 +125,24 @@ When editing a product, scroll to the **Product data** panel:
|
|||||||
|
|
||||||
```
|
```
|
||||||
wc-tier-and-package-prices/
|
wc-tier-and-package-prices/
|
||||||
├── wc-tier-and-package-prices.php # Main plugin file
|
├── wc-tier-and-package-prices.php # Main plugin file (v1.4.0)
|
||||||
|
├── .gitea/workflows/
|
||||||
|
│ └── release.yml # CI/CD release pipeline
|
||||||
├── includes/
|
├── includes/
|
||||||
│ ├── class-wc-tpp-admin.php # Admin settings
|
│ ├── class-wc-tpp-admin.php # Admin settings integration
|
||||||
|
│ ├── class-wc-tpp-settings.php # WooCommerce settings page
|
||||||
│ ├── class-wc-tpp-product-meta.php # Product meta boxes
|
│ ├── class-wc-tpp-product-meta.php # Product meta boxes
|
||||||
│ ├── class-wc-tpp-frontend.php # Frontend display
|
│ ├── class-wc-tpp-frontend.php # Frontend display logic
|
||||||
│ └── class-wc-tpp-cart.php # Cart price calculations
|
│ ├── class-wc-tpp-cart.php # Cart price calculations
|
||||||
|
│ └── class-wc-tpp-template-loader.php # Twig template engine
|
||||||
|
├── templates/
|
||||||
|
│ ├── admin/
|
||||||
|
│ │ ├── tier-row.twig # Tier pricing input row
|
||||||
|
│ │ └── package-row.twig # Package pricing input row
|
||||||
|
│ └── frontend/
|
||||||
|
│ ├── pricing-table.twig # Main pricing display wrapper
|
||||||
|
│ ├── tier-pricing-table.twig # Tier pricing table
|
||||||
|
│ └── package-pricing-display.twig # Package selection UI
|
||||||
├── assets/
|
├── assets/
|
||||||
│ ├── css/
|
│ ├── css/
|
||||||
│ │ ├── admin.css # Admin styles
|
│ │ ├── admin.css # Admin styles
|
||||||
@@ -123,14 +150,25 @@ wc-tier-and-package-prices/
|
|||||||
│ └── js/
|
│ └── js/
|
||||||
│ ├── admin.js # Admin JavaScript
|
│ ├── admin.js # Admin JavaScript
|
||||||
│ └── frontend.js # Frontend JavaScript
|
│ └── frontend.js # Frontend JavaScript
|
||||||
└── README.md
|
├── languages/
|
||||||
|
│ ├── wc-tier-package-prices.pot # Translation template
|
||||||
|
│ ├── wc-tier-package-prices-*.po # Translation sources
|
||||||
|
│ └── wc-tier-package-prices-*.mo # Compiled translations
|
||||||
|
├── lib/ # Bundled libraries (git submodules)
|
||||||
|
│ └── wc-licensed-product-client/ # License client library
|
||||||
|
├── vendor/ # Composer dependencies (generated)
|
||||||
|
├── CHANGELOG.md # Complete version history
|
||||||
|
├── INSTALLATION.md # Installation guide
|
||||||
|
├── QUICKSTART.md # Quick start guide
|
||||||
|
├── USAGE_EXAMPLES.md # Usage examples
|
||||||
|
└── README.md # This file
|
||||||
```
|
```
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- WordPress 6.0 or higher (tested up to 6.9.x)
|
- WordPress 6.0 or higher (tested up to 6.9.x)
|
||||||
- WooCommerce 8.0 or higher (tested up to 10.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
|
### Compatibility
|
||||||
|
|
||||||
@@ -152,7 +190,7 @@ A: The price will automatically recalculate based on the new quantity.
|
|||||||
A: Yes, each product can have its own tier and package pricing configuration.
|
A: Yes, each product can have its own tier and package pricing configuration.
|
||||||
|
|
||||||
**Q: Does this work with variable products?**
|
**Q: Does this work with variable products?**
|
||||||
A: Currently, this plugin is designed for simple products. Variable product support may be added in future versions.
|
A: Yes! Since version 1.2.0, the plugin fully supports variable products. Each variation can have its own independent tier and package pricing configuration.
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
@@ -164,39 +202,76 @@ This plugin is licensed under the GPL v2 or later.
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
### Version 1.0.0 - 2025-12-21
|
### Version 1.4.0 - 2026-01-29
|
||||||
|
|
||||||
#### Compatibility Updates
|
__Current Release__ - CI/CD Release Pipeline
|
||||||
|
|
||||||
- ✅ Updated for WooCommerce 10.x compatibility
|
- __New__: Gitea CI/CD release pipeline for automated builds and releases
|
||||||
- ✅ Updated for WordPress 6.9.x compatibility
|
- __New__: Git submodule for `magdev/wc-licensed-product-client` library
|
||||||
- ✅ Added HPOS (High-Performance Order Storage) support
|
- __Changed__: Composer now uses path repository for bundled license client
|
||||||
- ✅ Declared compatibility with WooCommerce Custom Order Tables
|
- __DevOps__: Automated version validation, translation compilation, and release publishing
|
||||||
|
|
||||||
#### Security Enhancements
|
See [CHANGELOG.md](CHANGELOG.md) for complete details.
|
||||||
|
|
||||||
- ✅ Added nonce verification for product meta save operations
|
### Version 1.3.1 - 2026-01-27
|
||||||
- ✅ Added capability checks for user permissions
|
|
||||||
- ✅ Enhanced data escaping and sanitization
|
|
||||||
- ✅ Implemented autosave prevention
|
|
||||||
|
|
||||||
#### Code Improvements
|
Secure License Client
|
||||||
|
|
||||||
- ✅ Enhanced cart object validation
|
- __Changed__: Switched to `SecureLicenseClient` with HMAC-SHA256 response signature verification
|
||||||
- ✅ Improved product object type checking
|
- __New__: Server Secret configuration field for secure communication with license server
|
||||||
- ✅ Better error handling for edge cases
|
- __Security__: Response signatures verified using HMAC-SHA256 with license-specific derived keys
|
||||||
- ✅ Updated data storage methods for cart items
|
|
||||||
- ✅ Modernized JavaScript localization with proper escaping
|
|
||||||
|
|
||||||
#### Initial Features
|
### Version 1.3.0 - 2026-01-25
|
||||||
|
|
||||||
- Initial release with tier pricing functionality
|
__Breaking Changes__ - PHP 8.3+ Required
|
||||||
- Package pricing with fixed quantities
|
|
||||||
- Customizable pricing tables
|
- __Breaking__: Minimum PHP version increased from 7.4 to 8.3
|
||||||
- Global settings page
|
- __New__: License management via `magdev/wc-licensed-product-client` library
|
||||||
- Product-level configuration
|
- __New__: Settings page split into "General" and "License" sub-tabs
|
||||||
- Cart integration with dynamic pricing
|
- __New__: AJAX-based license validation and activation with visual feedback
|
||||||
- Responsive frontend design
|
- __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
|
||||||
|
|
||||||
|
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
|
||||||
|
- __New__: AJAX-powered dynamic pricing table display when variations are selected
|
||||||
|
- __Changed__: Admin templates converted to table structure for better layout
|
||||||
|
- __Fixed__: Quantity restrictions now work correctly per-variation
|
||||||
|
- 100% backward compatible - no breaking changes
|
||||||
|
|
||||||
|
### Recent Major Updates
|
||||||
|
|
||||||
|
#### Version 1.1.7 - Enhanced Tier Pricing
|
||||||
|
- Added optional text labels for tier pricing
|
||||||
|
- Clickable tier rows that auto-populate quantity field
|
||||||
|
- Add to Cart button auto-disable when quantity is invalid
|
||||||
|
|
||||||
|
#### Version 1.1.4 - WooCommerce Blocks Support
|
||||||
|
- Full support for WooCommerce block-based cart and checkout
|
||||||
|
- Quantity restrictions work with both classic and block carts
|
||||||
|
- Enhanced "View Options" button styling
|
||||||
|
|
||||||
|
#### Version 1.1.0 - Package Quantity Restrictions
|
||||||
|
- Global and per-product package quantity restrictions
|
||||||
|
- Prevents customers from ordering non-package quantities
|
||||||
|
- Automatic quantity field hiding when restrictions enabled
|
||||||
|
|
||||||
|
#### Version 1.0.1 - Twig Template Engine
|
||||||
|
- Migrated to Twig templating system
|
||||||
|
- Enhanced security with automatic HTML escaping
|
||||||
|
- Added German (Switzerland, Informal) translation
|
||||||
|
|
||||||
|
#### Version 1.0.0 - Initial Release
|
||||||
|
- Tier pricing functionality (quantity-based discounts)
|
||||||
|
- Package pricing functionality (fixed-price bundles)
|
||||||
|
- WooCommerce HPOS compatibility
|
||||||
|
- Multilingual support (English, German)
|
||||||
|
|
||||||
|
For complete version history, see [CHANGELOG.md](CHANGELOG.md)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
# Usage Examples
|
# Usage Examples
|
||||||
|
|
||||||
|
**Plugin Version:** 1.1.20
|
||||||
|
**Last Updated:** 2025-12-23
|
||||||
|
|
||||||
## Example 1: T-Shirt Store with Volume Discounts
|
## Example 1: T-Shirt Store with Volume Discounts
|
||||||
|
|
||||||
### Tier Pricing Setup
|
### Tier Pricing Setup
|
||||||
@@ -16,9 +19,9 @@ For a t-shirt that normally costs $20:
|
|||||||
**How to configure:**
|
**How to configure:**
|
||||||
1. Edit your t-shirt product
|
1. Edit your t-shirt product
|
||||||
2. Set regular price to $20.00
|
2. Set regular price to $20.00
|
||||||
3. Add tier: Min Qty = 10, Price = $18.00
|
3. Add tier: Min Qty = 10, Price = $18.00, Label = "Bulk Discount"
|
||||||
4. Add tier: Min Qty = 25, Price = $16.00
|
4. Add tier: Min Qty = 25, Price = $16.00, Label = "Volume Pricing"
|
||||||
5. Add tier: Min Qty = 50, Price = $14.00
|
5. Add tier: Min Qty = 50, Price = $14.00, Label = "Wholesale Rate"
|
||||||
6. Save product
|
6. Save product
|
||||||
|
|
||||||
**Customer experience:**
|
**Customer experience:**
|
||||||
@@ -97,6 +100,62 @@ Regular mug price: $10.00
|
|||||||
- Package 2: Qty = 4, Price = $32.00, Label = "Family Set"
|
- Package 2: Qty = 4, Price = $32.00, Label = "Family Set"
|
||||||
- Package 3: Qty = 10, Price = $70.00, Label = "Office Bundle"
|
- Package 3: Qty = 10, Price = $70.00, Label = "Office Bundle"
|
||||||
|
|
||||||
|
## Example 5: Quantity Restrictions (New in v1.1.0)
|
||||||
|
|
||||||
|
### Party Supplies with Fixed Packages
|
||||||
|
|
||||||
|
For products that should ONLY be sold in specific package quantities:
|
||||||
|
|
||||||
|
**Product:** Balloons - Regular price $1.00 each
|
||||||
|
|
||||||
|
**Package Configuration:**
|
||||||
|
- Package 1: Qty = 12, Price = $10.00, Label = "Dozen Pack"
|
||||||
|
- Package 2: Qty = 24, Price = $18.00, Label = "Party Pack"
|
||||||
|
- Package 3: Qty = 50, Price = $40.00, Label = "Event Pack"
|
||||||
|
|
||||||
|
**How to configure:**
|
||||||
|
1. Edit balloon product
|
||||||
|
2. Set regular price to $1.00
|
||||||
|
3. Add packages as shown above
|
||||||
|
4. **Check "Restrict to package quantities only"** (per-product setting)
|
||||||
|
5. Save product
|
||||||
|
|
||||||
|
**Customer experience:**
|
||||||
|
- Quantity input field is hidden on product page
|
||||||
|
- Customer MUST select a package using the package selection buttons
|
||||||
|
- Attempting to add custom quantities via URL or API will fail with validation error
|
||||||
|
- Cart displays selected package quantity (cannot be edited)
|
||||||
|
- "View Options" button appears on shop/category pages instead of "Add to Cart"
|
||||||
|
|
||||||
|
**When to use quantity restrictions:**
|
||||||
|
- Pre-packaged items (dozen eggs, 6-pack drinks, etc.)
|
||||||
|
- Products with fixed manufacturing quantities
|
||||||
|
- Promotional bundles where you don't want individual sales
|
||||||
|
- Subscription boxes with specific item counts
|
||||||
|
|
||||||
|
## Example 6: Tier Labels for Enhanced UX (New in v1.1.7)
|
||||||
|
|
||||||
|
### Office Supplies with Clickable Tiers
|
||||||
|
|
||||||
|
**Product:** Premium Notebooks - Regular price $8.00 each
|
||||||
|
|
||||||
|
**Tier Configuration with Labels:**
|
||||||
|
- Tier 1: Min Qty = 5, Price = $7.50, Label = "Small Business Discount"
|
||||||
|
- Tier 2: Min Qty = 10, Price = $7.00, Label = "Wholesale Pricing"
|
||||||
|
- Tier 3: Min Qty = 25, Price = $6.50, Label = "Corporate Rate"
|
||||||
|
|
||||||
|
**Customer experience:**
|
||||||
|
- Tier labels appear below quantity in pricing table (italicized)
|
||||||
|
- Clicking a tier row auto-fills the quantity field with that tier's minimum quantity
|
||||||
|
- Smooth scroll animation highlights the quantity field
|
||||||
|
- Add to Cart button automatically disables when quantity is 0 or invalid
|
||||||
|
|
||||||
|
**Benefits of tier labels:**
|
||||||
|
- Helps customers understand pricing context
|
||||||
|
- Makes tiers more appealing with descriptive names
|
||||||
|
- Improves conversion by highlighting value propositions
|
||||||
|
- Clickable rows improve user experience
|
||||||
|
|
||||||
## Tips for Best Results
|
## Tips for Best Results
|
||||||
|
|
||||||
### Tier Pricing Best Practices
|
### Tier Pricing Best Practices
|
||||||
@@ -113,6 +172,11 @@ Regular mug price: $10.00
|
|||||||
- Minimum 5-10% per tier level
|
- Minimum 5-10% per tier level
|
||||||
- Higher tiers should have progressively better deals
|
- Higher tiers should have progressively better deals
|
||||||
|
|
||||||
|
4. **Use Labels** (v1.1.7+): Add descriptive labels to tiers
|
||||||
|
- "Wholesale Price" instead of just showing the number
|
||||||
|
- "Bulk Discount", "Volume Pricing", "Corporate Rate"
|
||||||
|
- Makes pricing more professional and appealing
|
||||||
|
|
||||||
### Package Pricing Best Practices
|
### Package Pricing Best Practices
|
||||||
|
|
||||||
1. **Strategic Quantities**: Match common use cases
|
1. **Strategic Quantities**: Match common use cases
|
||||||
@@ -169,3 +233,32 @@ Regular mug price: $10.00
|
|||||||
- Package 2: Medium party (25 pieces) = $110
|
- Package 2: Medium party (25 pieces) = $110
|
||||||
- Package 3: Large party (50 pieces) = $200
|
- Package 3: Large party (50 pieces) = $200
|
||||||
- Package 4: Event package (100 pieces) = $350
|
- Package 4: Event package (100 pieces) = $350
|
||||||
|
|
||||||
|
## WooCommerce Blocks Compatibility (v1.1.4+, Fixed in v1.1.20)
|
||||||
|
|
||||||
|
This plugin is **fully compatible** with WooCommerce block-based cart and checkout:
|
||||||
|
|
||||||
|
### Supported Block Types
|
||||||
|
- Cart Block (`woocommerce/cart`)
|
||||||
|
- Mini Cart Block (`woocommerce/mini-cart`)
|
||||||
|
- Checkout Block (`woocommerce/checkout`)
|
||||||
|
- All Store API endpoints
|
||||||
|
|
||||||
|
### Block-Specific Features
|
||||||
|
- Quantity restrictions work in block-based carts
|
||||||
|
- Package-restricted products hide quantity selectors in blocks
|
||||||
|
- Tier and package pricing applies correctly in block checkout
|
||||||
|
- Mini cart displays correct prices and restrictions
|
||||||
|
|
||||||
|
### Technical Notes
|
||||||
|
- v1.1.20 fixed critical fatal error in WooCommerce Blocks
|
||||||
|
- Uses `woocommerce_store_api_product_quantity_editable` filter
|
||||||
|
- Works with both classic and block-based themes
|
||||||
|
- No configuration needed - blocks work automatically
|
||||||
|
|
||||||
|
### Testing Your Block Setup
|
||||||
|
1. Add block-based cart to a page (`/cart`)
|
||||||
|
2. Add mini-cart block to your header
|
||||||
|
3. Add block-based checkout to a page (`/checkout`)
|
||||||
|
4. Test tier pricing, package pricing, and quantity restrictions
|
||||||
|
5. Verify prices calculate correctly at checkout
|
||||||
|
|||||||
@@ -23,42 +23,63 @@
|
|||||||
color: #666;
|
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-tier-row,
|
||||||
.wc-tpp-package-row {
|
.wc-tpp-package-row {
|
||||||
display: flex;
|
/* No special styling needed - standard table row */
|
||||||
gap: 15px;
|
|
||||||
align-items: flex-end;
|
|
||||||
padding: 15px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wc-tpp-tier-row .form-field,
|
.wc-tpp-tier-row td,
|
||||||
.wc-tpp-package-row .form-field {
|
.wc-tpp-package-row td {
|
||||||
margin: 0;
|
padding: 8px;
|
||||||
flex: 1;
|
vertical-align: middle;
|
||||||
}
|
|
||||||
|
|
||||||
.wc-tpp-tier-row label,
|
|
||||||
.wc-tpp-package-row label {
|
|
||||||
display: block;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ensure WooCommerce input classes work properly in table cells */
|
||||||
.wc-tpp-tier-row input,
|
.wc-tpp-tier-row input,
|
||||||
.wc-tpp-package-row input {
|
.wc-tpp-package-row input {
|
||||||
width: 100%;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wc-tpp-remove-tier,
|
.wc-tpp-remove-tier,
|
||||||
.wc-tpp-remove-package {
|
.wc-tpp-remove-package {
|
||||||
flex-shrink: 0;
|
|
||||||
color: #b32d2e;
|
color: #b32d2e;
|
||||||
border-color: #b32d2e;
|
border-color: #b32d2e;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wc-tpp-remove-tier:hover,
|
.wc-tpp-remove-tier:hover,
|
||||||
@@ -89,3 +110,96 @@
|
|||||||
color: #666;
|
color: #666;
|
||||||
font-style: italic;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,44 +6,141 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
let tierIndex = $('.wc-tpp-tier-row').length;
|
// Initialize indexes for simple products
|
||||||
let packageIndex = $('.wc-tpp-package-row').length;
|
let tierIndex = $('.wc-tpp-tier-pricing .wc-tpp-tier-row').length;
|
||||||
|
let packageIndex = $('.wc-tpp-package-pricing .wc-tpp-package-row').length;
|
||||||
|
|
||||||
// Add tier
|
// Function to update table header visibility
|
||||||
$('.wc-tpp-add-tier').on('click', function(e) {
|
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
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
// Add tier (simple products)
|
||||||
|
$('.wc-tpp-tier-pricing .wc-tpp-add-tier').on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const template = $('#wc-tpp-tier-row-template').html();
|
const template = $('#wc-tpp-tier-row-template').html();
|
||||||
const newRow = template.replace(/\{\{INDEX\}\}/g, tierIndex);
|
const newRow = template.replace(/\{\{INDEX\}\}/g, tierIndex);
|
||||||
$('.wc-tpp-tiers-container').append(newRow);
|
$('.wc-tpp-tier-pricing .wc-tpp-tiers-container').append(newRow);
|
||||||
tierIndex++;
|
tierIndex++;
|
||||||
|
updateTableHeaders();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add package (simple products)
|
||||||
|
$('.wc-tpp-package-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-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();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Variable Product Variation Handlers
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
// Add tier (variations)
|
||||||
|
$(document).on('click', '.wc-tpp-variation-pricing .wc-tpp-add-tier', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const $button = $(this);
|
||||||
|
const loop = $button.data('loop');
|
||||||
|
const $container = $button.closest('.wc-tpp-variation-pricing');
|
||||||
|
const $tbody = $container.find('.wc-tpp-variation-tiers .wc-tpp-tiers-container');
|
||||||
|
const template = $('#wc-tpp-variation-tier-row-template-' + loop).html();
|
||||||
|
|
||||||
|
// Count existing rows to get next index
|
||||||
|
const currentIndex = $tbody.find('tr').length;
|
||||||
|
const newRow = template.replace(/\{\{INDEX\}\}/g, currentIndex);
|
||||||
|
|
||||||
|
$tbody.append(newRow);
|
||||||
|
updateTableHeaders();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add package (variations)
|
||||||
|
$(document).on('click', '.wc-tpp-variation-pricing .wc-tpp-add-package', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const $button = $(this);
|
||||||
|
const loop = $button.data('loop');
|
||||||
|
const $container = $button.closest('.wc-tpp-variation-pricing');
|
||||||
|
const $tbody = $container.find('.wc-tpp-variation-packages .wc-tpp-packages-container');
|
||||||
|
const template = $('#wc-tpp-variation-package-row-template-' + loop).html();
|
||||||
|
|
||||||
|
// Count existing rows to get next index
|
||||||
|
const currentIndex = $tbody.find('tr').length;
|
||||||
|
const newRow = template.replace(/\{\{INDEX\}\}/g, currentIndex);
|
||||||
|
|
||||||
|
$tbody.append(newRow);
|
||||||
|
updateTableHeaders();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Common Handlers (both simple and variations)
|
||||||
|
// ========================================
|
||||||
|
|
||||||
// Remove tier
|
// Remove tier
|
||||||
$(document).on('click', '.wc-tpp-remove-tier', function(e) {
|
$(document).on('click', '.wc-tpp-remove-tier', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (confirm('Are you sure you want to remove this tier?')) {
|
if (confirm('Are you sure you want to remove this tier?')) {
|
||||||
$(this).closest('.wc-tpp-tier-row').remove();
|
$(this).closest('.wc-tpp-tier-row').remove();
|
||||||
|
updateTableHeaders();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add package
|
|
||||||
$('.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-packages-container').append(newRow);
|
|
||||||
packageIndex++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove package
|
// Remove package
|
||||||
$(document).on('click', '.wc-tpp-remove-package', function(e) {
|
$(document).on('click', '.wc-tpp-remove-package', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (confirm('Are you sure you want to remove this package?')) {
|
if (confirm('Are you sure you want to remove this package?')) {
|
||||||
$(this).closest('.wc-tpp-package-row').remove();
|
$(this).closest('.wc-tpp-package-row').remove();
|
||||||
|
updateTableHeaders();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Validate inputs
|
// Validate quantity inputs
|
||||||
$(document).on('input', 'input[name*="[min_qty]"], input[name*="[qty]"]', function() {
|
$(document).on('input', 'input[name*="[min_qty]"], input[name*="[qty]"]', function() {
|
||||||
const value = parseInt($(this).val());
|
const value = parseInt($(this).val());
|
||||||
if (value < 1) {
|
if (value < 1) {
|
||||||
@@ -51,6 +148,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Validate price inputs
|
||||||
$(document).on('input', 'input[name*="[price]"]', function() {
|
$(document).on('input', 'input[name*="[price]"]', function() {
|
||||||
const value = parseFloat($(this).val());
|
const value = parseFloat($(this).val());
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
|
|||||||
@@ -244,6 +244,114 @@
|
|||||||
if ($quantityInput.length > 0 && $addToCartButton.length > 0) {
|
if ($quantityInput.length > 0 && $addToCartButton.length > 0) {
|
||||||
updateAddToCartButton();
|
updateAddToCartButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Variable Product Support
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
const $variationsForm = $('.variations_form');
|
||||||
|
const $pricingTableContainer = $('.wc-tpp-pricing-table-container');
|
||||||
|
|
||||||
|
if ($variationsForm.length && $pricingTableContainer.length) {
|
||||||
|
// Handle variation selection
|
||||||
|
$variationsForm.on('found_variation', function(event, variation) {
|
||||||
|
if (!variation.variation_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show loading state
|
||||||
|
$pricingTableContainer.html('<div class="wc-tpp-loading">Loading pricing...</div>').show();
|
||||||
|
|
||||||
|
// Fetch variation pricing via AJAX
|
||||||
|
$.ajax({
|
||||||
|
url: wcTppData.ajax_url,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wc_tpp_get_variation_pricing',
|
||||||
|
nonce: wcTppData.nonce,
|
||||||
|
variation_id: variation.variation_id
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success && response.data.has_pricing) {
|
||||||
|
// Display the pricing table HTML
|
||||||
|
$pricingTableContainer.html(response.data.html).show();
|
||||||
|
|
||||||
|
// Re-initialize event handlers for the new content
|
||||||
|
initializePricingHandlers();
|
||||||
|
|
||||||
|
// Handle quantity restrictions
|
||||||
|
if (response.data.restrict_to_packages) {
|
||||||
|
$('input.qty').hide().closest('.quantity').hide();
|
||||||
|
$('<style>.quantity { display: none !important; }</style>').appendTo('head');
|
||||||
|
} else {
|
||||||
|
$('input.qty').show().closest('.quantity').show();
|
||||||
|
$('style:contains(".quantity { display: none")').remove();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No pricing for this variation
|
||||||
|
$pricingTableContainer.html('').hide();
|
||||||
|
$('input.qty').show().closest('.quantity').show();
|
||||||
|
$('style:contains(".quantity { display: none")').remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$pricingTableContainer.html('').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle variation reset
|
||||||
|
$variationsForm.on('reset_data', function() {
|
||||||
|
$pricingTableContainer.html('').hide();
|
||||||
|
$('input.qty').show().closest('.quantity').show();
|
||||||
|
$('style:contains(".quantity { display: none")').remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize pricing handlers for dynamically loaded content
|
||||||
|
function initializePricingHandlers() {
|
||||||
|
// Re-attach package selection handlers
|
||||||
|
$('.wc-tpp-select-package').off('click').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const $package = $(this).closest('.wc-tpp-package');
|
||||||
|
const qty = parseInt($package.data('qty'));
|
||||||
|
const $qtyInput = $('input.qty');
|
||||||
|
|
||||||
|
if ($qtyInput.length === 0 || $qtyInput.is(':hidden')) {
|
||||||
|
// Create hidden input for restricted products
|
||||||
|
if ($('.qty-hidden-input').length === 0) {
|
||||||
|
$('.single_add_to_cart_button').before('<input type="hidden" name="quantity" class="qty qty-hidden-input" value="1" />');
|
||||||
|
}
|
||||||
|
$('.qty-hidden-input').val(qty);
|
||||||
|
} else {
|
||||||
|
$qtyInput.val(qty).trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight selected package
|
||||||
|
$('.wc-tpp-package').removeClass('wc-tpp-selected');
|
||||||
|
$package.addClass('wc-tpp-selected');
|
||||||
|
|
||||||
|
// Scroll to add to cart button
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop: $('.single_add_to_cart_button').offset().top - 100
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-attach tier row click handlers
|
||||||
|
$('.wc-tpp-tier-pricing-table tbody tr').off('click').on('click', function() {
|
||||||
|
const minQty = parseInt($(this).data('min-qty'));
|
||||||
|
const $qtyInput = $('input.qty');
|
||||||
|
|
||||||
|
if ($qtyInput.length > 0 && $qtyInput.is(':visible')) {
|
||||||
|
$qtyInput.val(minQty).trigger('change');
|
||||||
|
|
||||||
|
// Scroll to quantity input
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop: $qtyInput.offset().top - 100
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "magdev/wc-tier-package-prices",
|
"name": "magdev/wc-tier-package-prices",
|
||||||
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
|
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
|
||||||
"version": "1.1.19",
|
|
||||||
"type": "wordpress-plugin",
|
"type": "wordpress-plugin",
|
||||||
"license": "GPL-2.0-or-later",
|
"license": "GPL-2.0-or-later",
|
||||||
"authors": [
|
"authors": [
|
||||||
@@ -10,13 +9,28 @@
|
|||||||
"homepage": "https://src.bundespruefstelle.ch/magdev"
|
"homepage": "https://src.bundespruefstelle.ch/magdev"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "lib/wc-licensed-product-client"
|
||||||
|
}
|
||||||
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.4",
|
"php": ">=8.3",
|
||||||
"twig/twig": "^3.0"
|
"twig/twig": "^3.0",
|
||||||
|
"magdev/wc-licensed-product-client": "^0.2"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"classmap": [
|
"classmap": [
|
||||||
"includes/"
|
"includes/"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"config": {
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"platform": {
|
||||||
|
"php": "8.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"prefer-stable": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ if (!class_exists('WC_TPP_Cart')) {
|
|||||||
|
|
||||||
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
|
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
|
||||||
$product_id = $cart_item['product_id'];
|
$product_id = $cart_item['product_id'];
|
||||||
|
$variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0;
|
||||||
|
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||||
$quantity = $cart_item['quantity'];
|
$quantity = $cart_item['quantity'];
|
||||||
$product = $cart_item['data'];
|
$product = $cart_item['data'];
|
||||||
|
|
||||||
@@ -48,10 +50,10 @@ if (!class_exists('WC_TPP_Cart')) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for exact package match first
|
// Check for exact package match first (pass product_id for parent fallback support)
|
||||||
$package_price = null;
|
$package_price = null;
|
||||||
if (get_option('wc_tpp_enable_package_pricing') === 'yes') {
|
if (get_option('wc_tpp_enable_package_pricing') === 'yes') {
|
||||||
$package_price = WC_TPP_Frontend::get_package_price($product_id, $quantity);
|
$package_price = WC_TPP_Frontend::get_package_price($product_id, $quantity, $variation_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($package_price !== null) {
|
if ($package_price !== null) {
|
||||||
@@ -62,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_pricing_type'] = 'package';
|
||||||
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_total_price'] = $package_price;
|
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_total_price'] = $package_price;
|
||||||
} else {
|
} else {
|
||||||
// Apply tier pricing if no package match
|
// Apply tier pricing if no package match (pass product_id for parent fallback support)
|
||||||
if (get_option('wc_tpp_enable_tier_pricing') === 'yes') {
|
if (get_option('wc_tpp_enable_tier_pricing') === 'yes') {
|
||||||
$tier_price = WC_TPP_Frontend::get_tier_price($product_id, $quantity);
|
$tier_price = WC_TPP_Frontend::get_tier_price($product_id, $quantity, $variation_id);
|
||||||
if ($tier_price !== null) {
|
if ($tier_price !== null) {
|
||||||
$product->set_price($tier_price);
|
$product->set_price($tier_price);
|
||||||
// Store pricing information in cart item for display
|
// Store pricing information in cart item for display
|
||||||
@@ -99,18 +101,18 @@ if (!class_exists('WC_TPP_Cart')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function validate_package_quantity($passed, $product_id, $quantity) {
|
public function validate_package_quantity($passed, $product_id, $quantity) {
|
||||||
// Check if restriction is enabled globally or for this product
|
// Check for variation ID in request (for variable products)
|
||||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
$variation_id = isset($_REQUEST['variation_id']) ? absint($_REQUEST['variation_id']) : 0;
|
||||||
$product_restrict = get_post_meta($product_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;
|
return $passed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get packages for this product
|
// Get packages for this product/variation (with parent fallback)
|
||||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
$packages = $this->get_packages_with_fallback($product_id, $variation_id);
|
||||||
|
|
||||||
if (empty($packages) || !is_array($packages)) {
|
if (!$packages) {
|
||||||
return $passed;
|
return $passed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,18 +149,13 @@ if (!class_exists('WC_TPP_Cart')) {
|
|||||||
|
|
||||||
public function maybe_hide_cart_quantity_input($product_quantity, $cart_item_key, $cart_item) {
|
public function maybe_hide_cart_quantity_input($product_quantity, $cart_item_key, $cart_item) {
|
||||||
$product_id = $cart_item['product_id'];
|
$product_id = $cart_item['product_id'];
|
||||||
|
$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
|
// Check if restriction is enabled (with parent fallback) and packages exist
|
||||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
|
||||||
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
|
||||||
|
|
||||||
// Get packages for this product
|
|
||||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
|
||||||
|
|
||||||
// If restriction is enabled and packages exist, show quantity as text only
|
|
||||||
if (($global_restrict || $product_restrict) && !empty($packages)) {
|
|
||||||
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s</span>',
|
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s</span>',
|
||||||
$product_id,
|
$effective_id,
|
||||||
$cart_item['quantity']
|
$cart_item['quantity']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -168,18 +165,13 @@ if (!class_exists('WC_TPP_Cart')) {
|
|||||||
|
|
||||||
public function maybe_hide_mini_cart_quantity_input($product_quantity, $cart_item, $cart_item_key) {
|
public function maybe_hide_mini_cart_quantity_input($product_quantity, $cart_item, $cart_item_key) {
|
||||||
$product_id = $cart_item['product_id'];
|
$product_id = $cart_item['product_id'];
|
||||||
|
$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
|
// Check if restriction is enabled (with parent fallback) and packages exist
|
||||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
|
||||||
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
|
||||||
|
|
||||||
// Get packages for this product
|
|
||||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
|
||||||
|
|
||||||
// If restriction is enabled and packages exist, show quantity as text only
|
|
||||||
if (($global_restrict || $product_restrict) && !empty($packages)) {
|
|
||||||
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s ×</span>',
|
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s ×</span>',
|
||||||
$product_id,
|
$effective_id,
|
||||||
$cart_item['quantity']
|
$cart_item['quantity']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -196,12 +188,12 @@ if (!class_exists('WC_TPP_Cart')) {
|
|||||||
$restricted_products = array();
|
$restricted_products = array();
|
||||||
foreach (WC()->cart->get_cart() as $cart_item) {
|
foreach (WC()->cart->get_cart() as $cart_item) {
|
||||||
$product_id = $cart_item['product_id'];
|
$product_id = $cart_item['product_id'];
|
||||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
$variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0;
|
||||||
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
|
||||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
|
||||||
|
|
||||||
if (($global_restrict || $product_restrict) && !empty($packages)) {
|
// Check if restriction is enabled (with parent fallback) and packages exist
|
||||||
$restricted_products[] = $product_id;
|
if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
|
||||||
|
$restricted_products[] = $effective_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +218,7 @@ if (!class_exists('WC_TPP_Cart')) {
|
|||||||
* Make quantity non-editable for restricted products in WooCommerce blocks
|
* Make quantity non-editable for restricted products in WooCommerce blocks
|
||||||
*
|
*
|
||||||
* @param bool $editable Whether the quantity is editable
|
* @param bool $editable Whether the quantity is editable
|
||||||
* @param WC_Product $product Product object
|
* @param WC_Product $product Product object (can be variation)
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function block_quantity_editable($editable, $product) {
|
public function block_quantity_editable($editable, $product) {
|
||||||
@@ -241,17 +233,61 @@ if (!class_exists('WC_TPP_Cart')) {
|
|||||||
return $editable;
|
return $editable;
|
||||||
}
|
}
|
||||||
|
|
||||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
// For variations, get parent product ID and variation ID
|
||||||
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
$variation_id = 0;
|
||||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
$parent_id = $product_id;
|
||||||
|
|
||||||
// If restriction is enabled and packages exist, make quantity non-editable
|
if ($product->is_type('variation')) {
|
||||||
if (($global_restrict || $product_restrict) && !empty($packages)) {
|
$variation_id = $product_id;
|
||||||
|
$parent_id = $product->get_parent_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $editable;
|
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();
|
new WC_TPP_Cart();
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ if (!class_exists('WC_TPP_Frontend')) {
|
|||||||
|
|
||||||
// Modify catalog add to cart button for restricted products
|
// Modify catalog add to cart button for restricted products
|
||||||
add_filter('woocommerce_loop_add_to_cart_link', array($this, 'modify_catalog_add_to_cart_button'), 10, 2);
|
add_filter('woocommerce_loop_add_to_cart_link', array($this, 'modify_catalog_add_to_cart_button'), 10, 2);
|
||||||
|
|
||||||
|
// AJAX endpoints for variation pricing
|
||||||
|
add_action('wp_ajax_wc_tpp_get_variation_pricing', array($this, 'ajax_get_variation_pricing'));
|
||||||
|
add_action('wp_ajax_nopriv_wc_tpp_get_variation_pricing', array($this, 'ajax_get_variation_pricing'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enqueue_scripts() {
|
public function enqueue_scripts() {
|
||||||
@@ -31,8 +35,10 @@ if (!class_exists('WC_TPP_Frontend')) {
|
|||||||
if (is_product()) {
|
if (is_product()) {
|
||||||
wp_enqueue_script('wc-tpp-frontend', WC_TPP_PLUGIN_URL . 'assets/js/frontend.js', array('jquery'), WC_TPP_VERSION, true);
|
wp_enqueue_script('wc-tpp-frontend', WC_TPP_PLUGIN_URL . 'assets/js/frontend.js', array('jquery'), WC_TPP_VERSION, true);
|
||||||
|
|
||||||
// Localize script with currency settings
|
// Localize script with currency settings and AJAX data
|
||||||
wp_localize_script('wc-tpp-frontend', 'wcTppData', array(
|
wp_localize_script('wc-tpp-frontend', 'wcTppData', array(
|
||||||
|
'ajax_url' => admin_url('admin-ajax.php'),
|
||||||
|
'nonce' => wp_create_nonce('wc_tpp_variation_pricing'),
|
||||||
'currency_symbol' => esc_js(get_woocommerce_currency_symbol()),
|
'currency_symbol' => esc_js(get_woocommerce_currency_symbol()),
|
||||||
'currency_position' => esc_js(get_option('woocommerce_currency_pos', 'left')),
|
'currency_position' => esc_js(get_option('woocommerce_currency_pos', 'left')),
|
||||||
'price_decimals' => absint(wc_get_price_decimals()),
|
'price_decimals' => absint(wc_get_price_decimals()),
|
||||||
@@ -67,6 +73,11 @@ if (!class_exists('WC_TPP_Frontend')) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For variable products, quantity hiding is handled per-variation via JS
|
||||||
|
if ($product->is_type('variable')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$product_id = $product->get_id();
|
$product_id = $product->get_id();
|
||||||
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
||||||
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||||
@@ -85,6 +96,13 @@ if (!class_exists('WC_TPP_Frontend')) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For variable products, show a placeholder that will be populated by JS when variation is selected
|
||||||
|
if ($product->is_type('variable')) {
|
||||||
|
echo '<div class="wc-tpp-pricing-table-container" data-product-type="variable" style="display:none;"></div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For simple products, display pricing table directly
|
||||||
$product_id = $product->get_id();
|
$product_id = $product->get_id();
|
||||||
$tiers = get_post_meta($product_id, '_wc_tpp_tiers', true);
|
$tiers = get_post_meta($product_id, '_wc_tpp_tiers', true);
|
||||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
||||||
@@ -103,8 +121,22 @@ if (!class_exists('WC_TPP_Frontend')) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_tier_price($product_id, $quantity) {
|
/**
|
||||||
$tiers = get_post_meta($product_id, '_wc_tpp_tiers', true);
|
* Get tier price for a product or variation
|
||||||
|
*
|
||||||
|
* @param int $product_id Product ID (parent for simple, parent for variable)
|
||||||
|
* @param int $quantity Quantity
|
||||||
|
* @param int $variation_id Variation ID (0 for simple products)
|
||||||
|
* @return float|null Tier price or null if not applicable
|
||||||
|
*/
|
||||||
|
public static function get_tier_price($product_id, $quantity, $variation_id = 0) {
|
||||||
|
$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)) {
|
if (empty($tiers) || !is_array($tiers)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -120,8 +152,22 @@ if (!class_exists('WC_TPP_Frontend')) {
|
|||||||
return $applicable_price;
|
return $applicable_price;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_package_price($product_id, $quantity) {
|
/**
|
||||||
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
|
* Get package price for a product or variation
|
||||||
|
*
|
||||||
|
* @param int $product_id Product ID (parent for simple, parent for variable)
|
||||||
|
* @param int $quantity Quantity
|
||||||
|
* @param int $variation_id Variation ID (0 for simple products)
|
||||||
|
* @return float|null Package price or null if not applicable
|
||||||
|
*/
|
||||||
|
public static function get_package_price($product_id, $quantity, $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);
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($packages) || !is_array($packages)) {
|
if (empty($packages) || !is_array($packages)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -164,8 +210,24 @@ if (!class_exists('WC_TPP_Frontend')) {
|
|||||||
|
|
||||||
$product_id = $product->get_id();
|
$product_id = $product->get_id();
|
||||||
|
|
||||||
// Check if product has quantity restrictions
|
// For variable products, check if ANY variation has restrictions
|
||||||
if (!self::has_quantity_restriction($product_id)) {
|
// For simple products, check the product itself
|
||||||
|
$has_restriction = false;
|
||||||
|
|
||||||
|
if ($product->is_type('variable')) {
|
||||||
|
// Check if any variation has package restrictions
|
||||||
|
$variations = $product->get_available_variations();
|
||||||
|
foreach ($variations as $variation_data) {
|
||||||
|
if (self::has_quantity_restriction($variation_data['variation_id'])) {
|
||||||
|
$has_restriction = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$has_restriction = self::has_quantity_restriction($product_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$has_restriction) {
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,15 +235,72 @@ if (!class_exists('WC_TPP_Frontend')) {
|
|||||||
$product_url = esc_url($product->get_permalink());
|
$product_url = esc_url($product->get_permalink());
|
||||||
$button_text = esc_html__('View Options', 'wc-tier-package-prices');
|
$button_text = esc_html__('View Options', 'wc-tier-package-prices');
|
||||||
|
|
||||||
|
// Use correct product type class
|
||||||
|
$product_type_class = $product->is_type('variable') ? 'product_type_variable' : 'product_type_simple';
|
||||||
|
|
||||||
$new_html = sprintf(
|
$new_html = sprintf(
|
||||||
'<a href="%s" class="button wc-tpp-view-options product_type_simple" aria-label="%s">%s</a>',
|
'<a href="%s" class="button wc-tpp-view-options %s" aria-label="%s">%s</a>',
|
||||||
$product_url,
|
$product_url,
|
||||||
|
esc_attr($product_type_class),
|
||||||
esc_attr(sprintf(__('View options for %s', 'wc-tier-package-prices'), $product->get_name())),
|
esc_attr(sprintf(__('View options for %s', 'wc-tier-package-prices'), $product->get_name())),
|
||||||
$button_text
|
$button_text
|
||||||
);
|
);
|
||||||
|
|
||||||
return $new_html;
|
return $new_html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AJAX handler to get variation pricing data
|
||||||
|
*/
|
||||||
|
public function ajax_get_variation_pricing() {
|
||||||
|
// Verify nonce
|
||||||
|
check_ajax_referer('wc_tpp_variation_pricing', 'nonce');
|
||||||
|
|
||||||
|
$variation_id = isset($_POST['variation_id']) ? absint($_POST['variation_id']) : 0;
|
||||||
|
|
||||||
|
if (!$variation_id) {
|
||||||
|
wp_send_json_error(array('message' => __('Invalid variation ID', 'wc-tier-package-prices')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get variation data
|
||||||
|
$variation = wc_get_product($variation_id);
|
||||||
|
|
||||||
|
if (!$variation || !$variation->is_type('variation')) {
|
||||||
|
wp_send_json_error(array('message' => __('Variation not found', 'wc-tier-package-prices')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tier and package pricing
|
||||||
|
$tiers = get_post_meta($variation_id, '_wc_tpp_tiers', true);
|
||||||
|
$packages = get_post_meta($variation_id, '_wc_tpp_packages', true);
|
||||||
|
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
|
||||||
|
$product_restrict = get_post_meta($variation_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
|
||||||
|
|
||||||
|
if (empty($tiers) && empty($packages)) {
|
||||||
|
// No pricing data for this variation
|
||||||
|
wp_send_json_success(array(
|
||||||
|
'has_pricing' => false,
|
||||||
|
'html' => ''
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the pricing table HTML
|
||||||
|
ob_start();
|
||||||
|
WC_TPP_Template_Loader::get_instance()->display('frontend/pricing-table.twig', array(
|
||||||
|
'product' => $variation,
|
||||||
|
'tiers' => $tiers,
|
||||||
|
'packages' => $packages,
|
||||||
|
'restrict_to_packages' => $global_restrict || $product_restrict
|
||||||
|
));
|
||||||
|
$html = ob_get_clean();
|
||||||
|
|
||||||
|
wp_send_json_success(array(
|
||||||
|
'has_pricing' => true,
|
||||||
|
'html' => $html,
|
||||||
|
'tiers' => $tiers ? $tiers : array(),
|
||||||
|
'packages' => $packages ? $packages : array(),
|
||||||
|
'restrict_to_packages' => $global_restrict || $product_restrict
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new WC_TPP_Frontend();
|
new WC_TPP_Frontend();
|
||||||
|
|||||||
333
includes/class-wc-tpp-license-checker.php
Normal file
333
includes/class-wc-tpp-license-checker.php
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* License Checker
|
||||||
|
*
|
||||||
|
* Handles license validation with localhost and self-licensing bypass
|
||||||
|
*
|
||||||
|
* @package WC_Tier_Package_Prices
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WC_TPP_License_Checker class
|
||||||
|
*/
|
||||||
|
if (!class_exists('WC_TPP_License_Checker')) {
|
||||||
|
class WC_TPP_License_Checker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton instance
|
||||||
|
*
|
||||||
|
* @var WC_TPP_License_Checker|null
|
||||||
|
*/
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache key for license status
|
||||||
|
*/
|
||||||
|
private const CACHE_KEY = 'wc_tpp_license_status';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache TTL for successful validation (1 hour)
|
||||||
|
*/
|
||||||
|
private const CACHE_TTL_SUCCESS = 3600;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache TTL for failed validation (5 minutes)
|
||||||
|
*/
|
||||||
|
private const CACHE_TTL_ERROR = 300;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localhost patterns
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private const LOCALHOST_HOSTS = ['localhost', '127.0.0.1', '::1'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localhost TLDs
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private const LOCALHOST_TLDS = ['.localhost', '.local', '.test', '.example', '.invalid'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get singleton instance
|
||||||
|
*
|
||||||
|
* @return WC_TPP_License_Checker
|
||||||
|
*/
|
||||||
|
public static function get_instance(): WC_TPP_License_Checker {
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
private function __construct() {
|
||||||
|
// Clear cache when license settings change
|
||||||
|
add_action('update_option_wc_tpp_license_key', array($this, 'clear_cache'));
|
||||||
|
add_action('update_option_wc_tpp_license_server_url', array($this, 'clear_cache'));
|
||||||
|
add_action('update_option_wc_tpp_license_server_secret', array($this, 'clear_cache'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current environment is localhost
|
||||||
|
*
|
||||||
|
* Matches patterns:
|
||||||
|
* - localhost
|
||||||
|
* - 127.0.0.1
|
||||||
|
* - ::1 (IPv6 localhost)
|
||||||
|
* - *.localhost (subdomains)
|
||||||
|
* - *.local (subdomains)
|
||||||
|
* - *.test (RFC 2606)
|
||||||
|
* - *.example (RFC 2606)
|
||||||
|
* - *.invalid (RFC 2606)
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function is_localhost(): bool {
|
||||||
|
$domain = $this->get_current_domain();
|
||||||
|
|
||||||
|
// Remove port number if present
|
||||||
|
$domain = preg_replace('/:[\d]+$/', '', $domain);
|
||||||
|
$domain = strtolower($domain);
|
||||||
|
|
||||||
|
// Check exact matches
|
||||||
|
if (in_array($domain, self::LOCALHOST_HOSTS, true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check TLD patterns
|
||||||
|
foreach (self::LOCALHOST_TLDS as $tld) {
|
||||||
|
if (str_ends_with($domain, $tld)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for private IP ranges (Docker, VMs, etc.)
|
||||||
|
if ($this->is_private_ip($domain)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if domain is a private IP address
|
||||||
|
*
|
||||||
|
* @param string $domain The domain to check.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function is_private_ip(string $domain): bool {
|
||||||
|
// Check if it's a valid IP first
|
||||||
|
if (!filter_var($domain, FILTER_VALIDATE_IP)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for private/reserved ranges
|
||||||
|
return !filter_var(
|
||||||
|
$domain,
|
||||||
|
FILTER_VALIDATE_IP,
|
||||||
|
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current site is self-licensing
|
||||||
|
*
|
||||||
|
* Returns true if the license server URL and site URL are on the same domain.
|
||||||
|
* This allows the license server itself to use the plugin without a license.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function is_self_licensing(): bool {
|
||||||
|
$server_url = get_option('wc_tpp_license_server_url', '');
|
||||||
|
|
||||||
|
if (empty($server_url)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$server_domain = $this->normalize_domain($server_url);
|
||||||
|
$site_domain = $this->normalize_domain(get_site_url());
|
||||||
|
|
||||||
|
return $server_domain === $site_domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a URL to its domain
|
||||||
|
*
|
||||||
|
* @param string $url The URL to normalize.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function normalize_domain(string $url): string {
|
||||||
|
$parsed = wp_parse_url($url);
|
||||||
|
$host = $parsed['host'] ?? '';
|
||||||
|
|
||||||
|
// Convert to lowercase
|
||||||
|
$host = strtolower($host);
|
||||||
|
|
||||||
|
// Remove www. prefix
|
||||||
|
$host = preg_replace('/^www\./', '', $host);
|
||||||
|
|
||||||
|
return $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current domain from site URL
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_current_domain(): string {
|
||||||
|
$site_url = get_site_url();
|
||||||
|
$parsed = wp_parse_url($site_url);
|
||||||
|
$host = $parsed['host'] ?? 'localhost';
|
||||||
|
|
||||||
|
// Include port if non-standard
|
||||||
|
if (isset($parsed['port'])) {
|
||||||
|
$host .= ':' . $parsed['port'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return strtolower($host);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the license is valid
|
||||||
|
*
|
||||||
|
* This is the main entry point for license validation.
|
||||||
|
* It implements the bypass logic for localhost and self-licensing.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function is_license_valid(): bool {
|
||||||
|
// Always valid on localhost
|
||||||
|
if ($this->is_localhost()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always valid for self-licensing
|
||||||
|
if ($this->is_self_licensing()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cached status
|
||||||
|
$cached = $this->get_cached_status();
|
||||||
|
if (false !== $cached) {
|
||||||
|
return !empty($cached['valid']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate against server
|
||||||
|
return $this->validate_license();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the license bypass reason if applicable
|
||||||
|
*
|
||||||
|
* @return string|null 'localhost', 'self_licensing', or null
|
||||||
|
*/
|
||||||
|
public function get_bypass_reason(): ?string {
|
||||||
|
if ($this->is_localhost()) {
|
||||||
|
return 'localhost';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->is_self_licensing()) {
|
||||||
|
return 'self_licensing';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cached license status
|
||||||
|
*
|
||||||
|
* @return array|false
|
||||||
|
*/
|
||||||
|
public function get_cached_status() {
|
||||||
|
return get_transient(self::CACHE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate license against the server
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function validate_license(): bool {
|
||||||
|
$license_key = get_option('wc_tpp_license_key', '');
|
||||||
|
$server_url = get_option('wc_tpp_license_server_url', '');
|
||||||
|
$server_secret = get_option('wc_tpp_license_server_secret', '');
|
||||||
|
|
||||||
|
// Can't validate without credentials
|
||||||
|
if (empty($license_key) || empty($server_url) || empty($server_secret)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$client = $this->get_license_client($server_url, $server_secret);
|
||||||
|
$domain = $this->get_current_domain();
|
||||||
|
|
||||||
|
// Remove port for validation
|
||||||
|
$domain = preg_replace('/:[\d]+$/', '', $domain);
|
||||||
|
|
||||||
|
$result = $client->validate($license_key, $domain);
|
||||||
|
|
||||||
|
// Cache successful validation
|
||||||
|
set_transient(self::CACHE_KEY, 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'),
|
||||||
|
), self::CACHE_TTL_SUCCESS);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Cache validation failure
|
||||||
|
set_transient(self::CACHE_KEY, array(
|
||||||
|
'valid' => false,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'checked_at' => current_time('mysql'),
|
||||||
|
), self::CACHE_TTL_ERROR);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get license client instance
|
||||||
|
*
|
||||||
|
* @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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the license cache
|
||||||
|
*/
|
||||||
|
public function clear_cache(): void {
|
||||||
|
delete_transient(self::CACHE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force revalidation of the license
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function revalidate(): bool {
|
||||||
|
$this->clear_cache();
|
||||||
|
return $this->is_license_valid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,13 +11,133 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
class WC_TPP_Product_Meta {
|
class WC_TPP_Product_Meta {
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
|
// Simple product hooks
|
||||||
add_action('woocommerce_product_options_pricing', array($this, 'add_tier_pricing_fields'));
|
add_action('woocommerce_product_options_pricing', array($this, 'add_tier_pricing_fields'));
|
||||||
add_action('woocommerce_product_options_pricing', array($this, 'add_package_pricing_fields'));
|
add_action('woocommerce_product_options_pricing', array($this, 'add_package_pricing_fields'));
|
||||||
add_action('woocommerce_process_product_meta', array($this, 'save_tier_package_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() {
|
public function add_tier_pricing_fields() {
|
||||||
global $post;
|
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">
|
<div class="options_group wc-tpp-tier-pricing">
|
||||||
<p class="form-field">
|
<p class="form-field">
|
||||||
@@ -25,18 +145,28 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
<span class="description"><?php _e('Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities.', 'wc-tier-package-prices'); ?></span>
|
<span class="description"><?php _e('Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities.', 'wc-tier-package-prices'); ?></span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="wc-tpp-tiers-container">
|
<table class="widefat wc-tpp-tiers-table">
|
||||||
<?php
|
<thead>
|
||||||
$tiers = get_post_meta($post->ID, '_wc_tpp_tiers', true);
|
<tr>
|
||||||
if (!is_array($tiers)) {
|
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
|
||||||
$tiers = array();
|
<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) {
|
foreach ($tiers as $index => $tier) {
|
||||||
$this->render_tier_row($index, $tier);
|
$this->render_tier_row($index, $tier);
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<p class="form-field">
|
<p class="form-field">
|
||||||
<button type="button" class="button wc-tpp-add-tier"><?php _e('Add Tier', 'wc-tier-package-prices'); ?></button>
|
<button type="button" class="button wc-tpp-add-tier"><?php _e('Add Tier', 'wc-tier-package-prices'); ?></button>
|
||||||
@@ -47,6 +177,13 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
|
|
||||||
public function add_package_pricing_fields() {
|
public function add_package_pricing_fields() {
|
||||||
global $post;
|
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">
|
<div class="options_group wc-tpp-package-pricing">
|
||||||
<p class="form-field">
|
<p class="form-field">
|
||||||
@@ -54,18 +191,28 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
<span class="description"><?php _e('Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100.', 'wc-tier-package-prices'); ?></span>
|
<span class="description"><?php _e('Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100.', 'wc-tier-package-prices'); ?></span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="wc-tpp-packages-container">
|
<table class="widefat wc-tpp-packages-table">
|
||||||
<?php
|
<thead>
|
||||||
$packages = get_post_meta($post->ID, '_wc_tpp_packages', true);
|
<tr>
|
||||||
if (!is_array($packages)) {
|
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
|
||||||
$packages = array();
|
<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) {
|
foreach ($packages as $index => $package) {
|
||||||
$this->render_package_row($index, $package);
|
$this->render_package_row($index, $package);
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<p class="form-field">
|
<p class="form-field">
|
||||||
<button type="button" class="button wc-tpp-add-package"><?php _e('Add Package', 'wc-tier-package-prices'); ?></button>
|
<button type="button" class="button wc-tpp-add-package"><?php _e('Add Package', 'wc-tier-package-prices'); ?></button>
|
||||||
@@ -94,14 +241,148 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
private function render_tier_row($index, $tier) {
|
private function render_tier_row($index, $tier) {
|
||||||
WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array(
|
WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array(
|
||||||
'index' => $index,
|
'index' => $index,
|
||||||
'tier' => $tier
|
'tier' => $tier,
|
||||||
|
'currency_symbol' => get_woocommerce_currency_symbol()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function render_package_row($index, $package) {
|
private function render_package_row($index, $package) {
|
||||||
WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array(
|
WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array(
|
||||||
'index' => $index,
|
'index' => $index,
|
||||||
'package' => $package
|
'package' => $package,
|
||||||
|
'currency_symbol' => get_woocommerce_currency_symbol()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add tier and package pricing fields to product variations
|
||||||
|
*
|
||||||
|
* @param int $loop Position in the loop
|
||||||
|
* @param array $variation_data Variation data
|
||||||
|
* @param WP_Post $variation Variation post object
|
||||||
|
*/
|
||||||
|
public function add_variation_pricing_fields($loop, $variation_data, $variation) {
|
||||||
|
$variation_id = $variation->ID;
|
||||||
|
|
||||||
|
// Retrieve variation-specific data
|
||||||
|
$tiers = get_post_meta($variation_id, '_wc_tpp_tiers', true);
|
||||||
|
$packages = get_post_meta($variation_id, '_wc_tpp_packages', true);
|
||||||
|
$restrict = get_post_meta($variation_id, '_wc_tpp_restrict_to_packages', true);
|
||||||
|
|
||||||
|
if (!is_array($tiers)) {
|
||||||
|
$tiers = array();
|
||||||
|
}
|
||||||
|
if (!is_array($packages)) {
|
||||||
|
$packages = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="form-row form-row-full wc-tpp-variation-pricing" data-variation-loop="<?php echo esc_attr($loop); ?>">
|
||||||
|
<h4><?php _e('Tier & Package Pricing', 'wc-tier-package-prices'); ?></h4>
|
||||||
|
|
||||||
|
<!-- Tier Pricing Section -->
|
||||||
|
<div class="wc-tpp-variation-tiers">
|
||||||
|
<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 foreach ($tiers as $index => $tier) : ?>
|
||||||
|
<?php $this->render_variation_tier_row($loop, $index, $tier); ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button type="button" class="button wc-tpp-add-tier" data-loop="<?php echo esc_attr($loop); ?>">
|
||||||
|
<?php _e('Add Tier', 'wc-tier-package-prices'); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Package Pricing Section -->
|
||||||
|
<div class="wc-tpp-variation-packages" style="margin-top: 15px;">
|
||||||
|
<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 foreach ($packages as $index => $package) : ?>
|
||||||
|
<?php $this->render_variation_package_row($loop, $index, $package); ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button type="button" class="button wc-tpp-add-package" data-loop="<?php echo esc_attr($loop); ?>">
|
||||||
|
<?php _e('Add Package', 'wc-tier-package-prices'); ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Restriction Checkbox -->
|
||||||
|
<div style="margin-top: 15px;">
|
||||||
|
<?php
|
||||||
|
woocommerce_wp_checkbox(array(
|
||||||
|
'id' => 'wc_tpp_restrict_to_packages_' . $loop,
|
||||||
|
'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'),
|
||||||
|
'desc_tip' => true,
|
||||||
|
'value' => $restrict,
|
||||||
|
'cbvalue' => 'yes',
|
||||||
|
'wrapper_class' => 'form-row form-row-full'
|
||||||
|
));
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Templates for JavaScript -->
|
||||||
|
<script type="text/html" id="wc-tpp-variation-tier-row-template-<?php echo esc_attr($loop); ?>">
|
||||||
|
<?php $this->render_variation_tier_row($loop, '{{INDEX}}', array('min_qty' => '', 'price' => '', 'label' => '')); ?>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" id="wc-tpp-variation-package-row-template-<?php echo esc_attr($loop); ?>">
|
||||||
|
<?php $this->render_variation_package_row($loop, '{{INDEX}}', array('qty' => '', 'price' => '', 'label' => '')); ?>
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a tier row for variations
|
||||||
|
*
|
||||||
|
* @param int $loop Variation loop index
|
||||||
|
* @param int $index Tier index
|
||||||
|
* @param array $tier Tier data
|
||||||
|
*/
|
||||||
|
private function render_variation_tier_row($loop, $index, $tier) {
|
||||||
|
WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array(
|
||||||
|
'index' => $index,
|
||||||
|
'tier' => $tier,
|
||||||
|
'field_prefix' => 'wc_tpp_tiers[' . $loop . ']',
|
||||||
|
'currency_symbol' => get_woocommerce_currency_symbol()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a package row for variations
|
||||||
|
*
|
||||||
|
* @param int $loop Variation loop index
|
||||||
|
* @param int $index Package index
|
||||||
|
* @param array $package Package data
|
||||||
|
*/
|
||||||
|
private function render_variation_package_row($loop, $index, $package) {
|
||||||
|
WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array(
|
||||||
|
'index' => $index,
|
||||||
|
'package' => $package,
|
||||||
|
'field_prefix' => 'wc_tpp_packages[' . $loop . ']',
|
||||||
|
'currency_symbol' => get_woocommerce_currency_symbol()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +418,12 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
usort($tiers, function($a, $b) {
|
usort($tiers, function($a, $b) {
|
||||||
return $a['min_qty'] - $b['min_qty'];
|
return $a['min_qty'] - $b['min_qty'];
|
||||||
});
|
});
|
||||||
update_post_meta($post_id, '_wc_tpp_tiers', $tiers);
|
// 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 {
|
} else {
|
||||||
delete_post_meta($post_id, '_wc_tpp_tiers');
|
delete_post_meta($post_id, '_wc_tpp_tiers');
|
||||||
}
|
}
|
||||||
@@ -158,7 +444,12 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
usort($packages, function($a, $b) {
|
usort($packages, function($a, $b) {
|
||||||
return $a['qty'] - $b['qty'];
|
return $a['qty'] - $b['qty'];
|
||||||
});
|
});
|
||||||
update_post_meta($post_id, '_wc_tpp_packages', $packages);
|
// 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 {
|
} else {
|
||||||
delete_post_meta($post_id, '_wc_tpp_packages');
|
delete_post_meta($post_id, '_wc_tpp_packages');
|
||||||
}
|
}
|
||||||
@@ -167,6 +458,74 @@ if (!class_exists('WC_TPP_Product_Meta')) {
|
|||||||
$restrict_to_packages = isset($_POST['_wc_tpp_restrict_to_packages']) ? 'yes' : 'no';
|
$restrict_to_packages = isset($_POST['_wc_tpp_restrict_to_packages']) ? 'yes' : 'no';
|
||||||
update_post_meta($post_id, '_wc_tpp_restrict_to_packages', $restrict_to_packages);
|
update_post_meta($post_id, '_wc_tpp_restrict_to_packages', $restrict_to_packages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save tier and package pricing for variations
|
||||||
|
*
|
||||||
|
* @param int $variation_id Variation ID
|
||||||
|
* @param int $loop Position in loop
|
||||||
|
*/
|
||||||
|
public function save_variation_pricing_fields($variation_id, $loop) {
|
||||||
|
// Security check
|
||||||
|
if (!current_user_can('edit_products')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save tier pricing for this variation
|
||||||
|
$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(
|
||||||
|
'min_qty' => absint($tier['min_qty']),
|
||||||
|
'price' => wc_format_decimal($tier['price']),
|
||||||
|
'label' => sanitize_text_field($tier['label'] ?? '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort by minimum quantity
|
||||||
|
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
|
||||||
|
$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(
|
||||||
|
'qty' => absint($package['qty']),
|
||||||
|
'price' => wc_format_decimal($package['price']),
|
||||||
|
'label' => sanitize_text_field($package['label'] ?? '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort by quantity
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save restriction setting for this variation
|
||||||
|
if (isset($_POST['wc_tpp_restrict_to_packages'][$loop]) && $_POST['wc_tpp_restrict_to_packages'][$loop] === 'yes') {
|
||||||
|
update_post_meta($variation_id, '_wc_tpp_restrict_to_packages', 'yes');
|
||||||
|
} else {
|
||||||
|
delete_post_meta($variation_id, '_wc_tpp_restrict_to_packages');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new WC_TPP_Product_Meta();
|
new WC_TPP_Product_Meta();
|
||||||
|
|||||||
697
includes/class-wc-tpp-settings.php
Normal file → Executable file
697
includes/class-wc-tpp-settings.php
Normal file → Executable file
@@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* WooCommerce Settings Integration
|
* 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
|
* @package WC_Tier_Package_Prices
|
||||||
*/
|
*/
|
||||||
@@ -21,40 +21,43 @@ if (!class_exists('WC_Settings_Page')) {
|
|||||||
if (!class_exists('WC_TPP_Settings')) {
|
if (!class_exists('WC_TPP_Settings')) {
|
||||||
class WC_TPP_Settings extends WC_Settings_Page {
|
class WC_TPP_Settings extends WC_Settings_Page {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->id = 'tier_package_prices';
|
$this->id = 'tier_package_prices';
|
||||||
$this->label = __('Tier & Package Prices', 'wc-tier-package-prices');
|
$this->label = __('Tier & Package Prices', 'wc-tier-package-prices');
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Add AJAX handlers for license validation
|
||||||
* Get sections
|
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'));
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function get_sections() {
|
|
||||||
$sections = array(
|
|
||||||
'' => __('General', 'wc-tier-package-prices'),
|
|
||||||
);
|
|
||||||
|
|
||||||
return apply_filters('woocommerce_get_sections_' . $this->id, $sections);
|
// Add AJAX handler for update checks
|
||||||
}
|
add_action('wp_ajax_wc_tpp_check_updates', array($this, 'ajax_check_updates'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get settings array
|
* Get own sections - Modern WooCommerce pattern
|
||||||
*
|
*
|
||||||
* @param string $current_section Current section name.
|
* @return array
|
||||||
* @return array
|
*/
|
||||||
*/
|
protected function get_own_sections() {
|
||||||
public function get_settings($current_section = '') {
|
return array(
|
||||||
$settings = array();
|
'' => __('General', 'wc-tier-package-prices'),
|
||||||
|
'license' => __('License', 'wc-tier-package-prices'),
|
||||||
|
'updates' => __('Auto-Updates', 'wc-tier-package-prices'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ('' === $current_section) {
|
/**
|
||||||
$settings = array(
|
* Get settings for the default (General) section
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function get_settings_for_default_section() {
|
||||||
|
return array(
|
||||||
array(
|
array(
|
||||||
'title' => __('Tier & Package Prices Settings', 'wc-tier-package-prices'),
|
'title' => __('Tier & Package Prices Settings', 'wc-tier-package-prices'),
|
||||||
'type' => 'title',
|
'type' => 'title',
|
||||||
@@ -121,23 +124,625 @@ 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(
|
||||||
* Output the settings
|
'title' => __('License Server URL', 'wc-tier-package-prices'),
|
||||||
*/
|
'desc' => __('The URL of the license server.', 'wc-tier-package-prices'),
|
||||||
public function output() {
|
'id' => 'wc_tpp_license_server_url',
|
||||||
$settings = $this->get_settings();
|
'type' => 'url',
|
||||||
WC_Admin_Settings::output_fields($settings);
|
'default' => '',
|
||||||
}
|
'css' => 'min-width:400px;',
|
||||||
|
'desc_tip' => true,
|
||||||
|
),
|
||||||
|
|
||||||
/**
|
array(
|
||||||
* Save settings
|
'title' => __('License Key', 'wc-tier-package-prices'),
|
||||||
*/
|
'desc' => __('Your license key for this plugin.', 'wc-tier-package-prices'),
|
||||||
public function save() {
|
'id' => 'wc_tpp_license_key',
|
||||||
$settings = $this->get_settings();
|
'type' => 'text',
|
||||||
WC_Admin_Settings::save_fields($settings);
|
'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 settings for the Auto-Updates section
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function get_settings_for_updates_section() {
|
||||||
|
// Check if auto-install is available (requires valid license or bypass)
|
||||||
|
$license_checker = WC_TPP_License_Checker::get_instance();
|
||||||
|
$auto_install_disabled = !$license_checker->is_license_valid();
|
||||||
|
$auto_install_desc = __('Automatically install updates when available.', 'wc-tier-package-prices');
|
||||||
|
|
||||||
|
if ($auto_install_disabled) {
|
||||||
|
$auto_install_desc .= ' ' . __('(Requires a valid license)', 'wc-tier-package-prices');
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'title' => __('Auto-Update Settings', 'wc-tier-package-prices'),
|
||||||
|
'type' => 'title',
|
||||||
|
'desc' => __('Configure automatic plugin updates from the license server.', 'wc-tier-package-prices'),
|
||||||
|
'id' => 'wc_tpp_update_settings',
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'title' => __('Enable Update Notifications', 'wc-tier-package-prices'),
|
||||||
|
'desc' => __('Check for available plugin updates.', 'wc-tier-package-prices'),
|
||||||
|
'id' => 'wc_tpp_update_notification_enabled',
|
||||||
|
'default' => 'yes',
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'desc_tip' => __('When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin.', 'wc-tier-package-prices'),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'title' => __('Automatically Install Updates', 'wc-tier-package-prices'),
|
||||||
|
'desc' => $auto_install_desc,
|
||||||
|
'id' => 'wc_tpp_auto_install_enabled',
|
||||||
|
'default' => 'no',
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'desc_tip' => __('When enabled, updates will be automatically installed when WordPress performs background updates.', 'wc-tier-package-prices'),
|
||||||
|
'custom_attributes' => $auto_install_disabled ? array('disabled' => 'disabled') : array(),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'title' => __('Check Frequency (Hours)', 'wc-tier-package-prices'),
|
||||||
|
'desc' => __('How often to check for updates.', 'wc-tier-package-prices'),
|
||||||
|
'id' => 'wc_tpp_update_check_frequency',
|
||||||
|
'default' => '12',
|
||||||
|
'type' => 'number',
|
||||||
|
'css' => 'width: 80px;',
|
||||||
|
'desc_tip' => __('Number of hours between update checks. Default is 12 hours.', 'wc-tier-package-prices'),
|
||||||
|
'custom_attributes' => array(
|
||||||
|
'min' => '1',
|
||||||
|
'max' => '168',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'title' => __('Update Status', 'wc-tier-package-prices'),
|
||||||
|
'type' => 'wc_tpp_update_status',
|
||||||
|
'id' => 'wc_tpp_update_status_display',
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'type' => 'sectionend',
|
||||||
|
'id' => 'wc_tpp_update_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() {
|
||||||
|
global $current_section;
|
||||||
|
|
||||||
|
// Register custom field types
|
||||||
|
add_action('woocommerce_admin_field_wc_tpp_license_status', array($this, 'output_license_status_field'));
|
||||||
|
add_action('woocommerce_admin_field_wc_tpp_update_status', array($this, 'output_update_status_field'));
|
||||||
|
|
||||||
|
parent::output();
|
||||||
|
|
||||||
|
// Add JavaScript for license section
|
||||||
|
if ('license' === $current_section) {
|
||||||
|
$this->output_license_scripts();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add JavaScript for updates section
|
||||||
|
if ('updates' === $current_section) {
|
||||||
|
$this->output_updates_scripts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output license status custom field
|
||||||
|
*
|
||||||
|
* @param array $value Field configuration.
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
// Check for license bypass
|
||||||
|
$license_checker = WC_TPP_License_Checker::get_instance();
|
||||||
|
$bypass_reason = $license_checker->get_bypass_reason();
|
||||||
|
|
||||||
|
if ($bypass_reason) {
|
||||||
|
echo '<span class="wc-tpp-license-active">' . esc_html__('License Active', 'wc-tier-package-prices') . '</span>';
|
||||||
|
if ('localhost' === $bypass_reason) {
|
||||||
|
echo '<br><small>' . esc_html__('(Localhost environment - license validation bypassed)', 'wc-tier-package-prices') . '</small>';
|
||||||
|
} elseif ('self_licensing' === $bypass_reason) {
|
||||||
|
echo '<br><small>' . esc_html__('(Self-licensing server - license validation bypassed)', 'wc-tier-package-prices') . '</small>';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 update status custom field
|
||||||
|
*
|
||||||
|
* @param array $value Field configuration.
|
||||||
|
*/
|
||||||
|
public function output_update_status_field($value) {
|
||||||
|
$update_checker = WC_TPP_Update_Checker::get_instance();
|
||||||
|
$available_version = $update_checker->get_available_version();
|
||||||
|
?>
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row" class="titledesc">
|
||||||
|
<label><?php esc_html_e('Update Status', 'wc-tier-package-prices'); ?></label>
|
||||||
|
</th>
|
||||||
|
<td class="forminp">
|
||||||
|
<div id="wc-tpp-update-status-container">
|
||||||
|
<?php $this->render_update_status_html($available_version); ?>
|
||||||
|
</div>
|
||||||
|
<p class="description" style="margin-top: 10px;">
|
||||||
|
<button type="button" class="button" id="wc-tpp-check-updates">
|
||||||
|
<?php esc_html_e('Check for Updates', 'wc-tier-package-prices'); ?>
|
||||||
|
</button>
|
||||||
|
<span class="spinner" id="wc-tpp-update-spinner"></span>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render update status HTML
|
||||||
|
*
|
||||||
|
* @param string|null $available_version Available update version.
|
||||||
|
*/
|
||||||
|
private function render_update_status_html(?string $available_version) {
|
||||||
|
echo '<p><strong>' . esc_html__('Current Version:', 'wc-tier-package-prices') . '</strong> ' . esc_html(WC_TPP_VERSION) . '</p>';
|
||||||
|
|
||||||
|
if ($available_version) {
|
||||||
|
echo '<p class="wc-tpp-update-available"><strong>' . esc_html__('Update Available:', 'wc-tier-package-prices') . '</strong> ' . esc_html($available_version) . '</p>';
|
||||||
|
echo '<p><a href="' . esc_url(admin_url('update-core.php')) . '" class="button button-primary">' . esc_html__('Update Now', 'wc-tier-package-prices') . '</a></p>';
|
||||||
|
} else {
|
||||||
|
echo '<p class="wc-tpp-up-to-date">' . esc_html__('You are running the latest version.', 'wc-tier-package-prices') . '</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AJAX handler for checking updates
|
||||||
|
*/
|
||||||
|
public function ajax_check_updates() {
|
||||||
|
check_ajax_referer('wc_tpp_update_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_woocommerce')) {
|
||||||
|
wp_send_json_error(array('message' => __('Permission denied.', 'wc-tier-package-prices')));
|
||||||
|
}
|
||||||
|
|
||||||
|
$update_checker = WC_TPP_Update_Checker::get_instance();
|
||||||
|
$update_info = $update_checker->force_check();
|
||||||
|
|
||||||
|
if ($update_info && !empty($update_info['update_available'])) {
|
||||||
|
wp_send_json_success(array(
|
||||||
|
'message' => sprintf(
|
||||||
|
/* translators: %s: Version number */
|
||||||
|
__('Update available: version %s', 'wc-tier-package-prices'),
|
||||||
|
$update_info['version']
|
||||||
|
),
|
||||||
|
'update_available' => true,
|
||||||
|
'version' => $update_info['version'],
|
||||||
|
'current_version' => WC_TPP_VERSION,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
wp_send_json_success(array(
|
||||||
|
'message' => __('You are running the latest version.', 'wc-tier-package-prices'),
|
||||||
|
'update_available' => false,
|
||||||
|
'current_version' => WC_TPP_VERSION,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output JavaScript for update management
|
||||||
|
*/
|
||||||
|
private function output_updates_scripts() {
|
||||||
|
$nonce = wp_create_nonce('wc_tpp_update_nonce');
|
||||||
|
?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
jQuery(function($) {
|
||||||
|
var $checkBtn = $('#wc-tpp-check-updates');
|
||||||
|
var $spinner = $('#wc-tpp-update-spinner');
|
||||||
|
var $container = $('#wc-tpp-update-status-container');
|
||||||
|
|
||||||
|
$checkBtn.on('click', function() {
|
||||||
|
$spinner.addClass('is-active');
|
||||||
|
$checkBtn.prop('disabled', true);
|
||||||
|
|
||||||
|
$.post(ajaxurl, {
|
||||||
|
action: 'wc_tpp_check_updates',
|
||||||
|
nonce: '<?php echo esc_js($nonce); ?>'
|
||||||
|
})
|
||||||
|
.done(function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
var html = '<p><strong><?php echo esc_js(__('Current Version:', 'wc-tier-package-prices')); ?></strong> ' + response.data.current_version + '</p>';
|
||||||
|
|
||||||
|
if (response.data.update_available) {
|
||||||
|
html += '<p class="wc-tpp-update-available"><strong><?php echo esc_js(__('Update Available:', 'wc-tier-package-prices')); ?></strong> ' + response.data.version + '</p>';
|
||||||
|
html += '<p><a href="<?php echo esc_url(admin_url('update-core.php')); ?>" class="button button-primary"><?php echo esc_js(__('Update Now', 'wc-tier-package-prices')); ?></a></p>';
|
||||||
|
} else {
|
||||||
|
html += '<p class="wc-tpp-up-to-date"><?php echo esc_js(__('You are running the latest version.', 'wc-tier-package-prices')); ?></p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$container.html(html);
|
||||||
|
} else {
|
||||||
|
alert(response.data.message || '<?php echo esc_js(__('Failed to check for updates.', 'wc-tier-package-prices')); ?>');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function() {
|
||||||
|
alert('<?php echo esc_js(__('Request failed. Please try again.', 'wc-tier-package-prices')); ?>');
|
||||||
|
})
|
||||||
|
.always(function() {
|
||||||
|
$spinner.removeClass('is-active');
|
||||||
|
$checkBtn.prop('disabled', false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
468
includes/class-wc-tpp-update-checker.php
Normal file
468
includes/class-wc-tpp-update-checker.php
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Update Checker
|
||||||
|
*
|
||||||
|
* Handles WordPress plugin updates from the license server
|
||||||
|
*
|
||||||
|
* @package WC_Tier_Package_Prices
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WC_TPP_Update_Checker class
|
||||||
|
*/
|
||||||
|
if (!class_exists('WC_TPP_Update_Checker')) {
|
||||||
|
class WC_TPP_Update_Checker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton instance
|
||||||
|
*
|
||||||
|
* @var WC_TPP_Update_Checker|null
|
||||||
|
*/
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin slug
|
||||||
|
*/
|
||||||
|
private const PLUGIN_SLUG = 'wc-tier-and-package-prices';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update check cache key
|
||||||
|
*/
|
||||||
|
private const CACHE_KEY = 'wc_tpp_update_info';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default check frequency in hours
|
||||||
|
*/
|
||||||
|
private const DEFAULT_CHECK_FREQUENCY = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get singleton instance
|
||||||
|
*
|
||||||
|
* @return WC_TPP_Update_Checker
|
||||||
|
*/
|
||||||
|
public static function get_instance(): WC_TPP_Update_Checker {
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
private function __construct() {
|
||||||
|
// Only register if updates are not disabled
|
||||||
|
if ($this->is_disabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->register_hooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if auto-updates are disabled via constant
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function is_disabled(): bool {
|
||||||
|
return defined('WC_TPP_DISABLE_AUTO_UPDATE') && WC_TPP_DISABLE_AUTO_UPDATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register WordPress hooks
|
||||||
|
*/
|
||||||
|
private function register_hooks(): void {
|
||||||
|
// Check for updates
|
||||||
|
add_filter('pre_set_site_transient_update_plugins', array($this, 'check_for_updates'));
|
||||||
|
|
||||||
|
// Provide plugin information for update modal
|
||||||
|
add_filter('plugins_api', array($this, 'get_plugin_info'), 10, 3);
|
||||||
|
|
||||||
|
// Add authentication headers to download requests
|
||||||
|
add_filter('http_request_args', array($this, 'add_auth_headers'), 10, 2);
|
||||||
|
|
||||||
|
// Handle auto-install setting
|
||||||
|
add_filter('auto_update_plugin', array($this, 'handle_auto_install'), 10, 2);
|
||||||
|
|
||||||
|
// Clear cache when settings change
|
||||||
|
add_action('update_option_wc_tpp_license_key', array($this, 'clear_cache'));
|
||||||
|
add_action('update_option_wc_tpp_license_server_url', array($this, 'clear_cache'));
|
||||||
|
add_action('update_option_wc_tpp_update_notification_enabled', array($this, 'clear_cache'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if update notifications are enabled
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_update_notification_enabled(): bool {
|
||||||
|
return get_option('wc_tpp_update_notification_enabled', 'yes') === 'yes';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if auto-install is enabled
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_auto_install_enabled(): bool {
|
||||||
|
return get_option('wc_tpp_auto_install_enabled', 'no') === 'yes';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get update check frequency in hours
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function get_check_frequency(): int {
|
||||||
|
$frequency = (int) get_option('wc_tpp_update_check_frequency', self::DEFAULT_CHECK_FREQUENCY);
|
||||||
|
return max(1, min(168, $frequency)); // Clamp between 1 and 168 hours
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for plugin updates
|
||||||
|
*
|
||||||
|
* @param object $transient WordPress update transient.
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function check_for_updates($transient) {
|
||||||
|
if (empty($transient->checked)) {
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if notifications disabled
|
||||||
|
if (!self::is_update_notification_enabled()) {
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get update info (cached)
|
||||||
|
$update_info = $this->get_update_info();
|
||||||
|
|
||||||
|
if (empty($update_info) || !$update_info['update_available']) {
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build update object
|
||||||
|
$update_obj = $this->build_update_object($update_info);
|
||||||
|
|
||||||
|
if ($update_obj) {
|
||||||
|
$transient->response[WC_TPP_PLUGIN_BASENAME] = $update_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get plugin information for update modal
|
||||||
|
*
|
||||||
|
* @param false|object|array $result The result object or array.
|
||||||
|
* @param string $action The type of information being requested.
|
||||||
|
* @param object $args Plugin API arguments.
|
||||||
|
* @return false|object
|
||||||
|
*/
|
||||||
|
public function get_plugin_info($result, $action, $args) {
|
||||||
|
if ('plugin_information' !== $action) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($args->slug) || self::PLUGIN_SLUG !== $args->slug) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get update info
|
||||||
|
$update_info = $this->get_update_info(true); // Force fetch for full info
|
||||||
|
|
||||||
|
if (empty($update_info)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build plugin info object
|
||||||
|
return $this->build_plugin_info_object($update_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add authentication headers to download requests
|
||||||
|
*
|
||||||
|
* @param array $args HTTP request arguments.
|
||||||
|
* @param string $url The request URL.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function add_auth_headers(array $args, string $url): array {
|
||||||
|
$server_url = get_option('wc_tpp_license_server_url', '');
|
||||||
|
|
||||||
|
// Only add headers for requests to our license server
|
||||||
|
if (empty($server_url) || strpos($url, $server_url) !== 0) {
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
$license_key = get_option('wc_tpp_license_key', '');
|
||||||
|
|
||||||
|
if (!empty($license_key)) {
|
||||||
|
$args['headers']['X-License-Key'] = $license_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle auto-install setting
|
||||||
|
*
|
||||||
|
* @param bool|null $update Whether to auto-update.
|
||||||
|
* @param object $item The plugin update object.
|
||||||
|
* @return bool|null
|
||||||
|
*/
|
||||||
|
public function handle_auto_install($update, $item) {
|
||||||
|
// Check if this is our plugin
|
||||||
|
if (!isset($item->plugin) || WC_TPP_PLUGIN_BASENAME !== $item->plugin) {
|
||||||
|
return $update;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return our setting, or default behavior
|
||||||
|
if (self::is_auto_install_enabled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $update;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get update info from cache or server
|
||||||
|
*
|
||||||
|
* @param bool $force_fetch Force fetching from server.
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
private function get_update_info(bool $force_fetch = false): ?array {
|
||||||
|
// Check cache first
|
||||||
|
if (!$force_fetch) {
|
||||||
|
$cached = get_transient(self::CACHE_KEY);
|
||||||
|
if (false !== $cached) {
|
||||||
|
return $cached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch from server
|
||||||
|
$update_info = $this->fetch_update_info();
|
||||||
|
|
||||||
|
if ($update_info) {
|
||||||
|
// Cache the result
|
||||||
|
$cache_ttl = self::get_check_frequency() * HOUR_IN_SECONDS;
|
||||||
|
set_transient(self::CACHE_KEY, $update_info, $cache_ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $update_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch update info from license server
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
private function fetch_update_info(): ?array {
|
||||||
|
$server_url = get_option('wc_tpp_license_server_url', '');
|
||||||
|
$license_key = get_option('wc_tpp_license_key', '');
|
||||||
|
$server_secret = get_option('wc_tpp_license_server_secret', '');
|
||||||
|
|
||||||
|
if (empty($server_url)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the update check endpoint
|
||||||
|
$endpoint = trailingslashit($server_url) . 'wp-json/wc-licensed-product/v1/update-check';
|
||||||
|
|
||||||
|
// Get license checker for domain
|
||||||
|
$license_checker = WC_TPP_License_Checker::get_instance();
|
||||||
|
$domain = $license_checker->get_current_domain();
|
||||||
|
|
||||||
|
// Remove port for API call
|
||||||
|
$domain = preg_replace('/:[\d]+$/', '', $domain);
|
||||||
|
|
||||||
|
// Prepare request body
|
||||||
|
$body = array(
|
||||||
|
'license_key' => $license_key,
|
||||||
|
'domain' => $domain,
|
||||||
|
'plugin_slug' => self::PLUGIN_SLUG,
|
||||||
|
'current_version' => WC_TPP_VERSION,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make the request
|
||||||
|
$response = wp_remote_post($endpoint, array(
|
||||||
|
'timeout' => 15,
|
||||||
|
'headers' => array(
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
),
|
||||||
|
'body' => wp_json_encode($body),
|
||||||
|
));
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response_code = wp_remote_retrieve_response_code($response);
|
||||||
|
|
||||||
|
// Handle rate limiting
|
||||||
|
if (429 === $response_code) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle other errors
|
||||||
|
if ($response_code < 200 || $response_code >= 300) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
$data = json_decode($body, true);
|
||||||
|
|
||||||
|
if (empty($data) || !isset($data['success'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build WordPress update object
|
||||||
|
*
|
||||||
|
* @param array $update_info Update information from server.
|
||||||
|
* @return object|null
|
||||||
|
*/
|
||||||
|
private function build_update_object(array $update_info): ?object {
|
||||||
|
if (empty($update_info['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if update is actually available
|
||||||
|
if (version_compare(WC_TPP_VERSION, $update_info['version'], '>=')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$obj = new \stdClass();
|
||||||
|
$obj->id = $update_info['id'] ?? self::PLUGIN_SLUG;
|
||||||
|
$obj->slug = $update_info['slug'] ?? self::PLUGIN_SLUG;
|
||||||
|
$obj->plugin = $update_info['plugin'] ?? WC_TPP_PLUGIN_BASENAME;
|
||||||
|
$obj->new_version = $update_info['version'];
|
||||||
|
$obj->url = $update_info['url'] ?? '';
|
||||||
|
$obj->package = $update_info['download_url'] ?? $update_info['package'] ?? '';
|
||||||
|
$obj->tested = $update_info['tested'] ?? '';
|
||||||
|
$obj->requires = $update_info['requires'] ?? '6.0';
|
||||||
|
$obj->requires_php = $update_info['requires_php'] ?? '8.3';
|
||||||
|
|
||||||
|
// Icons
|
||||||
|
if (!empty($update_info['icons'])) {
|
||||||
|
$obj->icons = $update_info['icons'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Banners
|
||||||
|
if (!empty($update_info['banners'])) {
|
||||||
|
$obj->banners = $update_info['banners'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build plugin info object for update modal
|
||||||
|
*
|
||||||
|
* @param array $update_info Update information from server.
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
private function build_plugin_info_object(array $update_info): object {
|
||||||
|
$obj = new \stdClass();
|
||||||
|
|
||||||
|
$obj->name = $update_info['name'] ?? 'WooCommerce Tier and Package Prices';
|
||||||
|
$obj->slug = $update_info['slug'] ?? self::PLUGIN_SLUG;
|
||||||
|
$obj->version = $update_info['version'] ?? WC_TPP_VERSION;
|
||||||
|
$obj->author = $update_info['author'] ?? '<a href="https://src.bundespruefstelle.ch/magdev">Marco Graetsch</a>';
|
||||||
|
$obj->author_profile = $update_info['author_profile'] ?? 'https://src.bundespruefstelle.ch/magdev';
|
||||||
|
$obj->homepage = $update_info['homepage'] ?? $update_info['url'] ?? '';
|
||||||
|
$obj->requires = $update_info['requires'] ?? '6.0';
|
||||||
|
$obj->tested = $update_info['tested'] ?? '';
|
||||||
|
$obj->requires_php = $update_info['requires_php'] ?? '8.3';
|
||||||
|
$obj->last_updated = $update_info['last_updated'] ?? '';
|
||||||
|
$obj->download_link = $update_info['download_url'] ?? $update_info['package'] ?? '';
|
||||||
|
|
||||||
|
// Sections (description, changelog, etc.)
|
||||||
|
$obj->sections = array();
|
||||||
|
|
||||||
|
if (!empty($update_info['sections']['description'])) {
|
||||||
|
$obj->sections['description'] = $update_info['sections']['description'];
|
||||||
|
} else {
|
||||||
|
$obj->sections['description'] = __('Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices.', 'wc-tier-package-prices');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($update_info['sections']['changelog'])) {
|
||||||
|
$obj->sections['changelog'] = $update_info['sections']['changelog'];
|
||||||
|
} elseif (!empty($update_info['changelog'])) {
|
||||||
|
$obj->sections['changelog'] = $update_info['changelog'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($update_info['sections']['installation'])) {
|
||||||
|
$obj->sections['installation'] = $update_info['sections']['installation'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Icons
|
||||||
|
if (!empty($update_info['icons'])) {
|
||||||
|
$obj->icons = $update_info['icons'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Banners
|
||||||
|
if (!empty($update_info['banners'])) {
|
||||||
|
$obj->banners = $update_info['banners'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear update cache
|
||||||
|
*/
|
||||||
|
public function clear_cache(): void {
|
||||||
|
delete_transient(self::CACHE_KEY);
|
||||||
|
|
||||||
|
// Also clear WordPress plugin update transient to force recheck
|
||||||
|
delete_site_transient('update_plugins');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force check for updates
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function force_check(): ?array {
|
||||||
|
$this->clear_cache();
|
||||||
|
return $this->get_update_info(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the available update version if any
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function get_available_version(): ?string {
|
||||||
|
$update_info = $this->get_update_info();
|
||||||
|
|
||||||
|
if (empty($update_info) || empty($update_info['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's actually newer
|
||||||
|
if (version_compare(WC_TPP_VERSION, $update_info['version'], '>=')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $update_info['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an update is available
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function is_update_available(): bool {
|
||||||
|
return null !== $this->get_available_version();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
languages/wc-tier-package-prices-de_CH.mo
Normal file
BIN
languages/wc-tier-package-prices-de_CH.mo
Normal file
Binary file not shown.
495
languages/wc-tier-package-prices-de_CH.po
Normal file
495
languages/wc-tier-package-prices-de_CH.po
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
# German (Switzerland) translation for WooCommerce Tier and Package Prices
|
||||||
|
# Copyright (C) 2026 Marco Graetsch
|
||||||
|
# This file is distributed under the GPL v2 or later.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||||
|
"POT-Creation-Date: 2026-02-03 00:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-02-03 00:00+0000\n"
|
||||||
|
"Last-Translator: Marco Graetsch\n"
|
||||||
|
"Language-Team: German (Switzerland)\n"
|
||||||
|
"Language: de_CH\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 3.0\n"
|
||||||
|
"X-Domain: wc-tier-package-prices\n"
|
||||||
|
|
||||||
|
#: wc-tier-and-package-prices.php:41
|
||||||
|
msgid "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active."
|
||||||
|
msgstr "WooCommerce Staffel- und Paketpreise benötigt eine installierte und aktive WooCommerce-Installation."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:21
|
||||||
|
#: includes/class-wc-tpp-admin.php:22
|
||||||
|
#: includes/class-wc-tpp-settings.php:28
|
||||||
|
msgid "Tier & Package Prices"
|
||||||
|
msgstr "Staffel- & Paketpreise"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:40
|
||||||
|
msgid "General"
|
||||||
|
msgstr "Allgemein"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:58
|
||||||
|
msgid "Tier & Package Prices Settings"
|
||||||
|
msgstr "Staffel- & Paketpreise Einstellungen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:60
|
||||||
|
msgid "Configure tier pricing and package pricing options for your WooCommerce products."
|
||||||
|
msgstr "Konfigurieren Sie Staffelpreise und Paketpreise für Ihre WooCommerce-Produkte."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:54
|
||||||
|
#: includes/class-wc-tpp-settings.php:65
|
||||||
|
msgid "Enable Tier Pricing"
|
||||||
|
msgstr "Staffelpreise aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:58
|
||||||
|
#: includes/class-wc-tpp-settings.php:66
|
||||||
|
msgid "Enable tier pricing for products"
|
||||||
|
msgstr "Staffelpreise für Produkte aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:70
|
||||||
|
msgid "Allow quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities."
|
||||||
|
msgstr "Ermöglicht mengenbasierte Preisstaffeln. Kunden erhalten reduzierte Preise beim Kauf grösserer Mengen."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:63
|
||||||
|
#: includes/class-wc-tpp-settings.php:74
|
||||||
|
msgid "Enable Package Pricing"
|
||||||
|
msgstr "Paketpreise aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:67
|
||||||
|
#: includes/class-wc-tpp-settings.php:75
|
||||||
|
msgid "Enable fixed-price packages for products"
|
||||||
|
msgstr "Festpreis-Pakete für Produkte aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:79
|
||||||
|
msgid "Allow fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100."
|
||||||
|
msgstr "Ermöglicht Festpreis-Pakete mit bestimmten Mengen. Zum Beispiel: 10 Stück für CHF 50.-, 25 Stück für CHF 100.-."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:72
|
||||||
|
#: includes/class-wc-tpp-settings.php:83
|
||||||
|
msgid "Display Pricing Table"
|
||||||
|
msgstr "Preistabelle anzeigen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:76
|
||||||
|
#: includes/class-wc-tpp-settings.php:84
|
||||||
|
msgid "Show tier and package pricing table on product pages"
|
||||||
|
msgstr "Staffel- und Paketpreis-Tabelle auf Produktseiten anzeigen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:88
|
||||||
|
msgid "Display the pricing table to customers on product pages."
|
||||||
|
msgstr "Zeigt die Preistabelle den Kunden auf Produktseiten an."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:81
|
||||||
|
#: includes/class-wc-tpp-settings.php:92
|
||||||
|
msgid "Display Position"
|
||||||
|
msgstr "Anzeigeposition"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:93
|
||||||
|
msgid "Choose where to display the pricing table on product pages."
|
||||||
|
msgstr "Wählen Sie, wo die Preistabelle auf Produktseiten angezeigt werden soll."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:101
|
||||||
|
msgid "Before Add to Cart Button"
|
||||||
|
msgstr "Vor \"In den Warenkorb\"-Button"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:102
|
||||||
|
msgid "After Add to Cart Button"
|
||||||
|
msgstr "Nach \"In den Warenkorb\"-Button"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:85
|
||||||
|
msgid "Before Add to Cart"
|
||||||
|
msgstr "Vor \"In den Warenkorb\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:86
|
||||||
|
msgid "After Add to Cart"
|
||||||
|
msgstr "Nach \"In den Warenkorb\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:87
|
||||||
|
#: includes/class-wc-tpp-settings.php:103
|
||||||
|
msgid "After Price"
|
||||||
|
msgstr "Nach dem Preis"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:108
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:76
|
||||||
|
msgid "Restrict to Package Quantities"
|
||||||
|
msgstr "Auf Paketmengen beschränken"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:109
|
||||||
|
msgid "Limit quantities to defined package sizes only"
|
||||||
|
msgstr "Mengen nur auf definierte Paketgrössen beschränken"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:113
|
||||||
|
msgid "When enabled, customers can only purchase products in the exact quantities defined in packages. The quantity input field will be hidden and replaced with package selection buttons."
|
||||||
|
msgstr "Wenn aktiviert, können Kunden Produkte nur in den genau definierten Paketmengen kaufen. Das Mengeneingabefeld wird ausgeblendet und durch Paketauswahl-Buttons ersetzt."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:23
|
||||||
|
msgid "Tier Pricing"
|
||||||
|
msgstr "Staffelpreise"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:24
|
||||||
|
msgid "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities."
|
||||||
|
msgstr "Mengenbasierte Preisstaffeln festlegen. Kunden erhalten Rabatte beim Kauf grösserer Mengen."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:41
|
||||||
|
msgid "Add Tier"
|
||||||
|
msgstr "Staffel hinzufügen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:52
|
||||||
|
msgid "Package Pricing"
|
||||||
|
msgstr "Paketpreise"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:53
|
||||||
|
msgid "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100."
|
||||||
|
msgstr "Festpreis-Pakete mit bestimmten Mengen festlegen. Zum Beispiel: 10 Stück für CHF 50.-, 25 Stück für CHF 100.-."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:70
|
||||||
|
msgid "Add Package"
|
||||||
|
msgstr "Paket hinzufügen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:77
|
||||||
|
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"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:91
|
||||||
|
msgid "e.g., 10"
|
||||||
|
msgstr "z.B. 10"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:95
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:114
|
||||||
|
msgid "e.g., 9.99"
|
||||||
|
msgstr "z.B. 9.90"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:97
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:120
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr "Entfernen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:109
|
||||||
|
#: includes/class-wc-tpp-frontend.php:75
|
||||||
|
msgid "Quantity"
|
||||||
|
msgstr "Menge"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:113
|
||||||
|
msgid "Fixed Price"
|
||||||
|
msgstr "Festpreis"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:117
|
||||||
|
msgid "Label (Optional)"
|
||||||
|
msgstr "Bezeichnung (Optional)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:118
|
||||||
|
msgid "e.g., Starter Pack"
|
||||||
|
msgstr "z.B. Starter-Paket"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:71
|
||||||
|
msgid "Volume Discounts"
|
||||||
|
msgstr "Mengenrabatte"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:94
|
||||||
|
#: includes/class-wc-tpp-frontend.php:76
|
||||||
|
msgid "Price per Unit"
|
||||||
|
msgstr "Preis pro Einheit"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:77
|
||||||
|
msgid "You Save"
|
||||||
|
msgstr "Sie sparen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:110
|
||||||
|
msgid "Package Deals"
|
||||||
|
msgstr "Paketangebote"
|
||||||
|
|
||||||
|
#: templates/frontend/package-pricing-display.twig:11
|
||||||
|
msgid "Choose a package size below"
|
||||||
|
msgstr "Wählen Sie unten eine Paketgrösse"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:123
|
||||||
|
msgid "pieces"
|
||||||
|
msgstr "Stück"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:129
|
||||||
|
msgid "per unit"
|
||||||
|
msgstr "pro Einheit"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:133
|
||||||
|
msgid "Select Package"
|
||||||
|
msgstr "Paket auswählen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:63
|
||||||
|
msgid "Package price"
|
||||||
|
msgstr "Paketpreis"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:66
|
||||||
|
msgid "Volume discount"
|
||||||
|
msgstr "Mengenrabatt"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:124
|
||||||
|
msgid "this product"
|
||||||
|
msgstr "dieses Produkt"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:128
|
||||||
|
msgid "The quantity %1$d is not available for %2$s. Please choose from the available package sizes: %3$s"
|
||||||
|
msgstr "Die Menge %1$d ist für %2$s nicht verfügbar. Bitte wählen Sie aus den verfügbaren Paketgrössen: %3$s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:173
|
||||||
|
msgid "View Options"
|
||||||
|
msgstr "Optionen 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."
|
||||||
|
|
||||||
|
#. v1.4.1 - Auto-Updates and License Bypass
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Updates"
|
||||||
|
msgstr "Auto-Updates"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Update Settings"
|
||||||
|
msgstr "Auto-Update Einstellungen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Configure automatic plugin updates from the license server."
|
||||||
|
msgstr "Konfigurieren Sie automatische Plugin-Updates vom Lizenzserver."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Enable Update Notifications"
|
||||||
|
msgstr "Update-Benachrichtigungen aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for available plugin updates."
|
||||||
|
msgstr "Nach verfügbaren Plugin-Updates suchen."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
msgstr "Wenn aktiviert, prüft das Plugin auf Updates vom Lizenzserver und zeigt Benachrichtigungen im WordPress-Admin an."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically Install Updates"
|
||||||
|
msgstr "Updates automatisch installieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically install updates when available."
|
||||||
|
msgstr "Updates automatisch installieren, wenn verfügbar."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Requires a valid license)"
|
||||||
|
msgstr "(Erfordert eine gültige Lizenz)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
msgstr "Wenn aktiviert, werden Updates automatisch installiert, wenn WordPress Hintergrund-Updates durchführt."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check Frequency (Hours)"
|
||||||
|
msgstr "Prüfhäufigkeit (Stunden)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "How often to check for updates."
|
||||||
|
msgstr "Wie oft nach Updates gesucht werden soll."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Number of hours between update checks. Default is 12 hours."
|
||||||
|
msgstr "Anzahl der Stunden zwischen Update-Prüfungen. Standard ist 12 Stunden."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Status"
|
||||||
|
msgstr "Update-Status"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for Updates"
|
||||||
|
msgstr "Nach Updates suchen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Current Version:"
|
||||||
|
msgstr "Aktuelle Version:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Available:"
|
||||||
|
msgstr "Update verfügbar:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "You are running the latest version."
|
||||||
|
msgstr "Sie verwenden die neueste Version."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Now"
|
||||||
|
msgstr "Jetzt aktualisieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
#. translators: %s: Version number
|
||||||
|
msgid "Update available: version %s"
|
||||||
|
msgstr "Update verfügbar: Version %s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Failed to check for updates."
|
||||||
|
msgstr "Fehler beim Suchen nach Updates."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Localhost environment - license validation bypassed)"
|
||||||
|
msgstr "(Localhost-Umgebung - Lizenzvalidierung übersprungen)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Self-licensing server - license validation bypassed)"
|
||||||
|
msgstr "(Selbstlizenzierender Server - Lizenzvalidierung übersprungen)"
|
||||||
Binary file not shown.
@@ -1,12 +1,12 @@
|
|||||||
# German (Switzerland, Informal) translation for WooCommerce Tier and Package Prices
|
# German (Switzerland, Informal) translation for WooCommerce Tier and Package Prices
|
||||||
# Copyright (C) 2025 Marco Graetsch
|
# Copyright (C) 2026 Marco Graetsch
|
||||||
# This file is distributed under the GPL v2 or later.
|
# This file is distributed under the GPL v2 or later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.6\n"
|
"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
|
||||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\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-02-03 00:00+0000\n"
|
||||||
"PO-Revision-Date: 2025-12-21 00:00+0000\n"
|
"PO-Revision-Date: 2026-02-03 00:00+0000\n"
|
||||||
"Last-Translator: Marco Graetsch\n"
|
"Last-Translator: Marco Graetsch\n"
|
||||||
"Language-Team: German (Switzerland)\n"
|
"Language-Team: German (Switzerland)\n"
|
||||||
"Language: de_CH_informal\n"
|
"Language: de_CH_informal\n"
|
||||||
@@ -152,6 +152,26 @@ msgstr "Paket hinzufügen"
|
|||||||
msgid "Only allow quantities defined in packages above"
|
msgid "Only allow quantities defined in packages above"
|
||||||
msgstr "Nur oben definierte Paketmengen zulassen"
|
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
|
#: includes/class-wc-tpp-product-meta.php:90
|
||||||
msgid "Minimum Quantity"
|
msgid "Minimum Quantity"
|
||||||
msgstr "Mindestmenge"
|
msgstr "Mindestmenge"
|
||||||
@@ -240,6 +260,236 @@ msgstr "Die Menge %1$d ist für %2$s nicht verfügbar. Bitte wähle aus den verf
|
|||||||
msgid "View Options"
|
msgid "View Options"
|
||||||
msgstr "Optionen ansehen"
|
msgstr "Optionen ansehen"
|
||||||
|
|
||||||
#: includes/class-wc-tpp-frontend.php:178
|
|
||||||
msgid "View options for %s"
|
#: includes/class-wc-tpp-product-meta.php:36
|
||||||
msgstr "Optionen für %s ansehen"
|
#: 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."
|
||||||
|
|
||||||
|
#. v1.4.1 - Auto-Updates and License Bypass
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Updates"
|
||||||
|
msgstr "Auto-Updates"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Update Settings"
|
||||||
|
msgstr "Auto-Update Einstellungen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Configure automatic plugin updates from the license server."
|
||||||
|
msgstr "Konfiguriere automatische Plugin-Updates vom Lizenzserver."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Enable Update Notifications"
|
||||||
|
msgstr "Update-Benachrichtigungen aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for available plugin updates."
|
||||||
|
msgstr "Nach verfügbaren Plugin-Updates suchen."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
msgstr "Wenn aktiviert, prüft das Plugin auf Updates vom Lizenzserver und zeigt Benachrichtigungen im WordPress-Admin an."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically Install Updates"
|
||||||
|
msgstr "Updates automatisch installieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically install updates when available."
|
||||||
|
msgstr "Updates automatisch installieren, wenn verfügbar."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Requires a valid license)"
|
||||||
|
msgstr "(Erfordert eine gültige Lizenz)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
msgstr "Wenn aktiviert, werden Updates automatisch installiert, wenn WordPress Hintergrund-Updates durchführt."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check Frequency (Hours)"
|
||||||
|
msgstr "Prüfhäufigkeit (Stunden)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "How often to check for updates."
|
||||||
|
msgstr "Wie oft nach Updates gesucht werden soll."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Number of hours between update checks. Default is 12 hours."
|
||||||
|
msgstr "Anzahl der Stunden zwischen Update-Prüfungen. Standard ist 12 Stunden."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Status"
|
||||||
|
msgstr "Update-Status"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for Updates"
|
||||||
|
msgstr "Nach Updates suchen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Current Version:"
|
||||||
|
msgstr "Aktuelle Version:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Available:"
|
||||||
|
msgstr "Update verfügbar:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "You are running the latest version."
|
||||||
|
msgstr "Du verwendest die neueste Version."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Now"
|
||||||
|
msgstr "Jetzt aktualisieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
#. translators: %s: Version number
|
||||||
|
msgid "Update available: version %s"
|
||||||
|
msgstr "Update verfügbar: Version %s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Failed to check for updates."
|
||||||
|
msgstr "Fehler beim Suchen nach Updates."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Localhost environment - license validation bypassed)"
|
||||||
|
msgstr "(Localhost-Umgebung - Lizenzvalidierung übersprungen)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Self-licensing server - license validation bypassed)"
|
||||||
|
msgstr "(Selbstlizenzierender Server - Lizenzvalidierung übersprungen)"
|
||||||
|
|||||||
Binary file not shown.
@@ -1,12 +1,12 @@
|
|||||||
# German (Germany) translation for WooCommerce Tier and Package Prices
|
# German (Germany) translation for WooCommerce Tier and Package Prices
|
||||||
# Copyright (C) 2025 Marco Graetsch
|
# Copyright (C) 2026 Marco Graetsch
|
||||||
# This file is distributed under the GPL v2 or later.
|
# This file is distributed under the GPL v2 or later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.6\n"
|
"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
|
||||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\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-02-03 00:00+0000\n"
|
||||||
"PO-Revision-Date: 2025-12-21 00:00+0000\n"
|
"PO-Revision-Date: 2026-02-03 00:00+0000\n"
|
||||||
"Last-Translator: Marco Graetsch\n"
|
"Last-Translator: Marco Graetsch\n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Language: de_DE\n"
|
"Language: de_DE\n"
|
||||||
@@ -152,6 +152,26 @@ msgstr "Paket hinzufügen"
|
|||||||
msgid "Only allow quantities defined in packages above"
|
msgid "Only allow quantities defined in packages above"
|
||||||
msgstr "Nur oben definierte Paketmengen zulassen"
|
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
|
#: includes/class-wc-tpp-product-meta.php:90
|
||||||
msgid "Minimum Quantity"
|
msgid "Minimum Quantity"
|
||||||
msgstr "Mindestmenge"
|
msgstr "Mindestmenge"
|
||||||
@@ -240,6 +260,236 @@ msgstr "Die Menge %1$d ist für %2$s nicht verfügbar. Bitte wählen Sie aus den
|
|||||||
msgid "View Options"
|
msgid "View Options"
|
||||||
msgstr "Optionen ansehen"
|
msgstr "Optionen ansehen"
|
||||||
|
|
||||||
#: includes/class-wc-tpp-frontend.php:178
|
|
||||||
msgid "View options for %s"
|
#: includes/class-wc-tpp-product-meta.php:36
|
||||||
msgstr "Optionen für %s ansehen"
|
#: 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."
|
||||||
|
|
||||||
|
#. v1.4.1 - Auto-Updates and License Bypass
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Updates"
|
||||||
|
msgstr "Auto-Updates"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Update Settings"
|
||||||
|
msgstr "Auto-Update Einstellungen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Configure automatic plugin updates from the license server."
|
||||||
|
msgstr "Konfigurieren Sie automatische Plugin-Updates vom Lizenzserver."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Enable Update Notifications"
|
||||||
|
msgstr "Update-Benachrichtigungen aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for available plugin updates."
|
||||||
|
msgstr "Nach verfügbaren Plugin-Updates suchen."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
msgstr "Wenn aktiviert, prüft das Plugin auf Updates vom Lizenzserver und zeigt Benachrichtigungen im WordPress-Admin an."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically Install Updates"
|
||||||
|
msgstr "Updates automatisch installieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically install updates when available."
|
||||||
|
msgstr "Updates automatisch installieren, wenn verfügbar."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Requires a valid license)"
|
||||||
|
msgstr "(Erfordert eine gültige Lizenz)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
msgstr "Wenn aktiviert, werden Updates automatisch installiert, wenn WordPress Hintergrund-Updates durchführt."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check Frequency (Hours)"
|
||||||
|
msgstr "Prüfhäufigkeit (Stunden)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "How often to check for updates."
|
||||||
|
msgstr "Wie oft nach Updates gesucht werden soll."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Number of hours between update checks. Default is 12 hours."
|
||||||
|
msgstr "Anzahl der Stunden zwischen Update-Prüfungen. Standard ist 12 Stunden."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Status"
|
||||||
|
msgstr "Update-Status"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for Updates"
|
||||||
|
msgstr "Nach Updates suchen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Current Version:"
|
||||||
|
msgstr "Aktuelle Version:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Available:"
|
||||||
|
msgstr "Update verfügbar:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "You are running the latest version."
|
||||||
|
msgstr "Sie verwenden die neueste Version."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Now"
|
||||||
|
msgstr "Jetzt aktualisieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
#. translators: %s: Version number
|
||||||
|
msgid "Update available: version %s"
|
||||||
|
msgstr "Update verfügbar: Version %s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Failed to check for updates."
|
||||||
|
msgstr "Fehler beim Suchen nach Updates."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Localhost environment - license validation bypassed)"
|
||||||
|
msgstr "(Localhost-Umgebung - Lizenzvalidierung übersprungen)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Self-licensing server - license validation bypassed)"
|
||||||
|
msgstr "(Selbstlizenzierender Server - Lizenzvalidierung übersprungen)"
|
||||||
|
|||||||
BIN
languages/wc-tier-package-prices-de_DE_informal.mo
Normal file
BIN
languages/wc-tier-package-prices-de_DE_informal.mo
Normal file
Binary file not shown.
495
languages/wc-tier-package-prices-de_DE_informal.po
Normal file
495
languages/wc-tier-package-prices-de_DE_informal.po
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
# German (Germany, Informal) translation for WooCommerce Tier and Package Prices
|
||||||
|
# Copyright (C) 2026 Marco Graetsch
|
||||||
|
# This file is distributed under the GPL v2 or later.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||||
|
"POT-Creation-Date: 2026-02-03 00:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-02-03 00:00+0000\n"
|
||||||
|
"Last-Translator: Marco Graetsch\n"
|
||||||
|
"Language-Team: German (Germany)\n"
|
||||||
|
"Language: de_DE_informal\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 3.0\n"
|
||||||
|
"X-Domain: wc-tier-package-prices\n"
|
||||||
|
|
||||||
|
#: wc-tier-and-package-prices.php:41
|
||||||
|
msgid "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active."
|
||||||
|
msgstr "WooCommerce Staffel- und Paketpreise erfordert, dass WooCommerce installiert und aktiviert ist."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:21
|
||||||
|
#: includes/class-wc-tpp-admin.php:22
|
||||||
|
#: includes/class-wc-tpp-settings.php:28
|
||||||
|
msgid "Tier & Package Prices"
|
||||||
|
msgstr "Staffel- & Paketpreise"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:40
|
||||||
|
msgid "General"
|
||||||
|
msgstr "Allgemein"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:58
|
||||||
|
msgid "Tier & Package Prices Settings"
|
||||||
|
msgstr "Staffel- & Paketpreise Einstellungen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:60
|
||||||
|
msgid "Configure tier pricing and package pricing options for your WooCommerce products."
|
||||||
|
msgstr "Konfiguriere Staffelpreise und Paketpreise für deine WooCommerce-Produkte."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:54
|
||||||
|
#: includes/class-wc-tpp-settings.php:65
|
||||||
|
msgid "Enable Tier Pricing"
|
||||||
|
msgstr "Staffelpreise aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:58
|
||||||
|
#: includes/class-wc-tpp-settings.php:66
|
||||||
|
msgid "Enable tier pricing for products"
|
||||||
|
msgstr "Staffelpreise für Produkte aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:70
|
||||||
|
msgid "Allow quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities."
|
||||||
|
msgstr "Ermöglicht mengenbasierte Preisstaffeln. Kunden erhalten reduzierte Preise beim Kauf größerer Mengen."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:63
|
||||||
|
#: includes/class-wc-tpp-settings.php:74
|
||||||
|
msgid "Enable Package Pricing"
|
||||||
|
msgstr "Paketpreise aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:67
|
||||||
|
#: includes/class-wc-tpp-settings.php:75
|
||||||
|
msgid "Enable fixed-price packages for products"
|
||||||
|
msgstr "Festpreis-Pakete für Produkte aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:79
|
||||||
|
msgid "Allow fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100."
|
||||||
|
msgstr "Ermöglicht Festpreis-Pakete mit bestimmten Mengen. Zum Beispiel: 10 Stück für 50€, 25 Stück für 100€."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:72
|
||||||
|
#: includes/class-wc-tpp-settings.php:83
|
||||||
|
msgid "Display Pricing Table"
|
||||||
|
msgstr "Preistabelle anzeigen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:76
|
||||||
|
#: includes/class-wc-tpp-settings.php:84
|
||||||
|
msgid "Show tier and package pricing table on product pages"
|
||||||
|
msgstr "Staffel- und Paketpreis-Tabelle auf Produktseiten anzeigen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:88
|
||||||
|
msgid "Display the pricing table to customers on product pages."
|
||||||
|
msgstr "Zeigt die Preistabelle den Kunden auf Produktseiten an."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:81
|
||||||
|
#: includes/class-wc-tpp-settings.php:92
|
||||||
|
msgid "Display Position"
|
||||||
|
msgstr "Anzeigeposition"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:93
|
||||||
|
msgid "Choose where to display the pricing table on product pages."
|
||||||
|
msgstr "Wähle, wo die Preistabelle auf Produktseiten angezeigt werden soll."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:101
|
||||||
|
msgid "Before Add to Cart Button"
|
||||||
|
msgstr "Vor \"In den Warenkorb\"-Button"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:102
|
||||||
|
msgid "After Add to Cart Button"
|
||||||
|
msgstr "Nach \"In den Warenkorb\"-Button"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:85
|
||||||
|
msgid "Before Add to Cart"
|
||||||
|
msgstr "Vor \"In den Warenkorb\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:86
|
||||||
|
msgid "After Add to Cart"
|
||||||
|
msgstr "Nach \"In den Warenkorb\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:87
|
||||||
|
#: includes/class-wc-tpp-settings.php:103
|
||||||
|
msgid "After Price"
|
||||||
|
msgstr "Nach dem Preis"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:108
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:76
|
||||||
|
msgid "Restrict to Package Quantities"
|
||||||
|
msgstr "Auf Paketmengen beschränken"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:109
|
||||||
|
msgid "Limit quantities to defined package sizes only"
|
||||||
|
msgstr "Mengen nur auf definierte Paketgrößen beschränken"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:113
|
||||||
|
msgid "When enabled, customers can only purchase products in the exact quantities defined in packages. The quantity input field will be hidden and replaced with package selection buttons."
|
||||||
|
msgstr "Wenn aktiviert, kannst du Produkte nur in den genau definierten Paketmengen kaufen. Das Mengeneingabefeld wird ausgeblendet und durch Paketauswahl-Buttons ersetzt."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:23
|
||||||
|
msgid "Tier Pricing"
|
||||||
|
msgstr "Staffelpreise"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:24
|
||||||
|
msgid "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities."
|
||||||
|
msgstr "Mengenbasierte Preisstaffeln festlegen. Kunden erhalten Rabatte beim Kauf größerer Mengen."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:41
|
||||||
|
msgid "Add Tier"
|
||||||
|
msgstr "Staffel hinzufügen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:52
|
||||||
|
msgid "Package Pricing"
|
||||||
|
msgstr "Paketpreise"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:53
|
||||||
|
msgid "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100."
|
||||||
|
msgstr "Festpreis-Pakete mit bestimmten Mengen festlegen. Zum Beispiel: 10 Stück für 50€, 25 Stück für 100€."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:70
|
||||||
|
msgid "Add Package"
|
||||||
|
msgstr "Paket hinzufügen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:77
|
||||||
|
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"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:91
|
||||||
|
msgid "e.g., 10"
|
||||||
|
msgstr "z.B. 10"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:95
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:114
|
||||||
|
msgid "e.g., 9.99"
|
||||||
|
msgstr "z.B. 9,99"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:97
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:120
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr "Entfernen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:109
|
||||||
|
#: includes/class-wc-tpp-frontend.php:75
|
||||||
|
msgid "Quantity"
|
||||||
|
msgstr "Menge"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:113
|
||||||
|
msgid "Fixed Price"
|
||||||
|
msgstr "Festpreis"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:117
|
||||||
|
msgid "Label (Optional)"
|
||||||
|
msgstr "Bezeichnung (Optional)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:118
|
||||||
|
msgid "e.g., Starter Pack"
|
||||||
|
msgstr "z.B. Starter-Paket"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:71
|
||||||
|
msgid "Volume Discounts"
|
||||||
|
msgstr "Mengenrabatte"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:94
|
||||||
|
#: includes/class-wc-tpp-frontend.php:76
|
||||||
|
msgid "Price per Unit"
|
||||||
|
msgstr "Preis pro Einheit"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:77
|
||||||
|
msgid "You Save"
|
||||||
|
msgstr "Du sparst"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:110
|
||||||
|
msgid "Package Deals"
|
||||||
|
msgstr "Paketangebote"
|
||||||
|
|
||||||
|
#: templates/frontend/package-pricing-display.twig:11
|
||||||
|
msgid "Choose a package size below"
|
||||||
|
msgstr "Wähle unten eine Paketgröße"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:123
|
||||||
|
msgid "pieces"
|
||||||
|
msgstr "Stück"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:129
|
||||||
|
msgid "per unit"
|
||||||
|
msgstr "pro Einheit"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:133
|
||||||
|
msgid "Select Package"
|
||||||
|
msgstr "Paket auswählen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:63
|
||||||
|
msgid "Package price"
|
||||||
|
msgstr "Paketpreis"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:66
|
||||||
|
msgid "Volume discount"
|
||||||
|
msgstr "Mengenrabatt"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:124
|
||||||
|
msgid "this product"
|
||||||
|
msgstr "dieses Produkt"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:128
|
||||||
|
msgid "The quantity %1$d is not available for %2$s. Please choose from the available package sizes: %3$s"
|
||||||
|
msgstr "Die Menge %1$d ist für %2$s nicht verfügbar. Bitte wähle aus den verfügbaren Paketgrößen: %3$s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:173
|
||||||
|
msgid "View Options"
|
||||||
|
msgstr "Optionen 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."
|
||||||
|
|
||||||
|
#. v1.4.1 - Auto-Updates and License Bypass
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Updates"
|
||||||
|
msgstr "Auto-Updates"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Update Settings"
|
||||||
|
msgstr "Auto-Update Einstellungen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Configure automatic plugin updates from the license server."
|
||||||
|
msgstr "Konfiguriere automatische Plugin-Updates vom Lizenzserver."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Enable Update Notifications"
|
||||||
|
msgstr "Update-Benachrichtigungen aktivieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for available plugin updates."
|
||||||
|
msgstr "Nach verfügbaren Plugin-Updates suchen."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
msgstr "Wenn aktiviert, prüft das Plugin auf Updates vom Lizenzserver und zeigt Benachrichtigungen im WordPress-Admin an."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically Install Updates"
|
||||||
|
msgstr "Updates automatisch installieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically install updates when available."
|
||||||
|
msgstr "Updates automatisch installieren, wenn verfügbar."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Requires a valid license)"
|
||||||
|
msgstr "(Erfordert eine gültige Lizenz)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
msgstr "Wenn aktiviert, werden Updates automatisch installiert, wenn WordPress Hintergrund-Updates durchführt."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check Frequency (Hours)"
|
||||||
|
msgstr "Prüfhäufigkeit (Stunden)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "How often to check for updates."
|
||||||
|
msgstr "Wie oft nach Updates gesucht werden soll."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Number of hours between update checks. Default is 12 hours."
|
||||||
|
msgstr "Anzahl der Stunden zwischen Update-Prüfungen. Standard ist 12 Stunden."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Status"
|
||||||
|
msgstr "Update-Status"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for Updates"
|
||||||
|
msgstr "Nach Updates suchen"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Current Version:"
|
||||||
|
msgstr "Aktuelle Version:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Available:"
|
||||||
|
msgstr "Update verfügbar:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "You are running the latest version."
|
||||||
|
msgstr "Du verwendest die neueste Version."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Now"
|
||||||
|
msgstr "Jetzt aktualisieren"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
#. translators: %s: Version number
|
||||||
|
msgid "Update available: version %s"
|
||||||
|
msgstr "Update verfügbar: Version %s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Failed to check for updates."
|
||||||
|
msgstr "Fehler beim Suchen nach Updates."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Localhost environment - license validation bypassed)"
|
||||||
|
msgstr "(Localhost-Umgebung - Lizenzvalidierung übersprungen)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Self-licensing server - license validation bypassed)"
|
||||||
|
msgstr "(Selbstlizenzierender Server - Lizenzvalidierung übersprungen)"
|
||||||
Binary file not shown.
@@ -1,12 +1,12 @@
|
|||||||
# English (US) translation for WooCommerce Tier and Package Prices
|
# English (US) translation for WooCommerce Tier and Package Prices
|
||||||
# Copyright (C) 2025 Marco Graetsch
|
# Copyright (C) 2026 Marco Graetsch
|
||||||
# This file is distributed under the GPL v2 or later.
|
# This file is distributed under the GPL v2 or later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.6\n"
|
"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
|
||||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\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-02-03 00:00+0000\n"
|
||||||
"PO-Revision-Date: 2025-12-21 00:00+0000\n"
|
"PO-Revision-Date: 2026-02-03 00:00+0000\n"
|
||||||
"Last-Translator: Marco Graetsch\n"
|
"Last-Translator: Marco Graetsch\n"
|
||||||
"Language-Team: English\n"
|
"Language-Team: English\n"
|
||||||
"Language: en_US\n"
|
"Language: en_US\n"
|
||||||
@@ -152,6 +152,26 @@ msgstr "Add Package"
|
|||||||
msgid "Only allow quantities defined in packages above"
|
msgid "Only allow quantities defined in packages above"
|
||||||
msgstr "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
|
#: includes/class-wc-tpp-product-meta.php:90
|
||||||
msgid "Minimum Quantity"
|
msgid "Minimum Quantity"
|
||||||
msgstr "Minimum Quantity"
|
msgstr "Minimum Quantity"
|
||||||
@@ -240,6 +260,236 @@ msgstr "The quantity %1$d is not available for %2$s. Please choose from the avai
|
|||||||
msgid "View Options"
|
msgid "View Options"
|
||||||
msgstr "View Options"
|
msgstr "View Options"
|
||||||
|
|
||||||
#: includes/class-wc-tpp-frontend.php:178
|
|
||||||
msgid "View options for %s"
|
#: includes/class-wc-tpp-product-meta.php:36
|
||||||
msgstr "View options for %s"
|
#: 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."
|
||||||
|
|
||||||
|
#. v1.4.1 - Auto-Updates and License Bypass
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Updates"
|
||||||
|
msgstr "Auto-Updates"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Update Settings"
|
||||||
|
msgstr "Auto-Update Settings"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Configure automatic plugin updates from the license server."
|
||||||
|
msgstr "Configure automatic plugin updates from the license server."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Enable Update Notifications"
|
||||||
|
msgstr "Enable Update Notifications"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for available plugin updates."
|
||||||
|
msgstr "Check for available plugin updates."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
msgstr "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically Install Updates"
|
||||||
|
msgstr "Automatically Install Updates"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically install updates when available."
|
||||||
|
msgstr "Automatically install updates when available."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Requires a valid license)"
|
||||||
|
msgstr "(Requires a valid license)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
msgstr "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check Frequency (Hours)"
|
||||||
|
msgstr "Check Frequency (Hours)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "How often to check for updates."
|
||||||
|
msgstr "How often to check for updates."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Number of hours between update checks. Default is 12 hours."
|
||||||
|
msgstr "Number of hours between update checks. Default is 12 hours."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Status"
|
||||||
|
msgstr "Update Status"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for Updates"
|
||||||
|
msgstr "Check for Updates"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Current Version:"
|
||||||
|
msgstr "Current Version:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Available:"
|
||||||
|
msgstr "Update Available:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "You are running the latest version."
|
||||||
|
msgstr "You are running the latest version."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Now"
|
||||||
|
msgstr "Update Now"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
#. translators: %s: Version number
|
||||||
|
msgid "Update available: version %s"
|
||||||
|
msgstr "Update available: version %s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Failed to check for updates."
|
||||||
|
msgstr "Failed to check for updates."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Localhost environment - license validation bypassed)"
|
||||||
|
msgstr "(Localhost environment - license validation bypassed)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Self-licensing server - license validation bypassed)"
|
||||||
|
msgstr "(Self-licensing server - license validation bypassed)"
|
||||||
|
|||||||
BIN
languages/wc-tier-package-prices-fr_CH.mo
Normal file
BIN
languages/wc-tier-package-prices-fr_CH.mo
Normal file
Binary file not shown.
495
languages/wc-tier-package-prices-fr_CH.po
Normal file
495
languages/wc-tier-package-prices-fr_CH.po
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
# French (Switzerland) translation for WooCommerce Tier and Package Prices
|
||||||
|
# Copyright (C) 2026 Marco Graetsch
|
||||||
|
# This file is distributed under the GPL v2 or later.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||||
|
"POT-Creation-Date: 2026-02-03 00:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-02-03 00:00+0000\n"
|
||||||
|
"Last-Translator: Marco Graetsch\n"
|
||||||
|
"Language-Team: French (Switzerland)\n"
|
||||||
|
"Language: fr_CH\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
"X-Generator: Poedit 3.0\n"
|
||||||
|
"X-Domain: wc-tier-package-prices\n"
|
||||||
|
|
||||||
|
#: wc-tier-and-package-prices.php:41
|
||||||
|
msgid "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active."
|
||||||
|
msgstr "WooCommerce Prix Échelonnés et Forfaitaires nécessite que WooCommerce soit installé et actif."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:21
|
||||||
|
#: includes/class-wc-tpp-admin.php:22
|
||||||
|
#: includes/class-wc-tpp-settings.php:28
|
||||||
|
msgid "Tier & Package Prices"
|
||||||
|
msgstr "Prix Échelonnés & Forfaitaires"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:40
|
||||||
|
msgid "General"
|
||||||
|
msgstr "Général"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:58
|
||||||
|
msgid "Tier & Package Prices Settings"
|
||||||
|
msgstr "Paramètres Prix Échelonnés & Forfaitaires"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:60
|
||||||
|
msgid "Configure tier pricing and package pricing options for your WooCommerce products."
|
||||||
|
msgstr "Configurez les options de prix échelonnés et forfaitaires pour vos produits WooCommerce."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:54
|
||||||
|
#: includes/class-wc-tpp-settings.php:65
|
||||||
|
msgid "Enable Tier Pricing"
|
||||||
|
msgstr "Activer les prix échelonnés"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:58
|
||||||
|
#: includes/class-wc-tpp-settings.php:66
|
||||||
|
msgid "Enable tier pricing for products"
|
||||||
|
msgstr "Activer les prix échelonnés pour les produits"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:70
|
||||||
|
msgid "Allow quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities."
|
||||||
|
msgstr "Permet des paliers de prix basés sur la quantité. Les clients bénéficient de prix réduits lors de l'achat de quantités plus importantes."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:63
|
||||||
|
#: includes/class-wc-tpp-settings.php:74
|
||||||
|
msgid "Enable Package Pricing"
|
||||||
|
msgstr "Activer les prix forfaitaires"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:67
|
||||||
|
#: includes/class-wc-tpp-settings.php:75
|
||||||
|
msgid "Enable fixed-price packages for products"
|
||||||
|
msgstr "Activer les forfaits à prix fixe pour les produits"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:79
|
||||||
|
msgid "Allow fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100."
|
||||||
|
msgstr "Permet des forfaits à prix fixe avec des quantités spécifiques. Par exemple: 10 pièces pour CHF 50.-, 25 pièces pour CHF 100.-."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:72
|
||||||
|
#: includes/class-wc-tpp-settings.php:83
|
||||||
|
msgid "Display Pricing Table"
|
||||||
|
msgstr "Afficher le tableau des prix"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:76
|
||||||
|
#: includes/class-wc-tpp-settings.php:84
|
||||||
|
msgid "Show tier and package pricing table on product pages"
|
||||||
|
msgstr "Afficher le tableau des prix échelonnés et forfaitaires sur les pages produits"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:88
|
||||||
|
msgid "Display the pricing table to customers on product pages."
|
||||||
|
msgstr "Affiche le tableau des prix aux clients sur les pages produits."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:81
|
||||||
|
#: includes/class-wc-tpp-settings.php:92
|
||||||
|
msgid "Display Position"
|
||||||
|
msgstr "Position d'affichage"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:93
|
||||||
|
msgid "Choose where to display the pricing table on product pages."
|
||||||
|
msgstr "Choisissez où afficher le tableau des prix sur les pages produits."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:101
|
||||||
|
msgid "Before Add to Cart Button"
|
||||||
|
msgstr "Avant le bouton \"Ajouter au panier\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:102
|
||||||
|
msgid "After Add to Cart Button"
|
||||||
|
msgstr "Après le bouton \"Ajouter au panier\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:85
|
||||||
|
msgid "Before Add to Cart"
|
||||||
|
msgstr "Avant \"Ajouter au panier\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:86
|
||||||
|
msgid "After Add to Cart"
|
||||||
|
msgstr "Après \"Ajouter au panier\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:87
|
||||||
|
#: includes/class-wc-tpp-settings.php:103
|
||||||
|
msgid "After Price"
|
||||||
|
msgstr "Après le prix"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:108
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:76
|
||||||
|
msgid "Restrict to Package Quantities"
|
||||||
|
msgstr "Restreindre aux quantités forfaitaires"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:109
|
||||||
|
msgid "Limit quantities to defined package sizes only"
|
||||||
|
msgstr "Limiter les quantités aux tailles de forfaits définies uniquement"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:113
|
||||||
|
msgid "When enabled, customers can only purchase products in the exact quantities defined in packages. The quantity input field will be hidden and replaced with package selection buttons."
|
||||||
|
msgstr "Lorsque cette option est activée, les clients ne peuvent acheter les produits que dans les quantités exactes définies dans les forfaits. Le champ de saisie de quantité sera masqué et remplacé par des boutons de sélection de forfait."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:23
|
||||||
|
msgid "Tier Pricing"
|
||||||
|
msgstr "Prix échelonnés"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:24
|
||||||
|
msgid "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities."
|
||||||
|
msgstr "Définir des paliers de prix basés sur la quantité. Les clients bénéficient de réductions lors de l'achat de quantités plus importantes."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:41
|
||||||
|
msgid "Add Tier"
|
||||||
|
msgstr "Ajouter un palier"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:52
|
||||||
|
msgid "Package Pricing"
|
||||||
|
msgstr "Prix forfaitaires"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:53
|
||||||
|
msgid "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100."
|
||||||
|
msgstr "Définir des forfaits à prix fixe avec des quantités spécifiques. Par exemple: 10 pièces pour CHF 50.-, 25 pièces pour CHF 100.-."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:70
|
||||||
|
msgid "Add Package"
|
||||||
|
msgstr "Ajouter un forfait"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:77
|
||||||
|
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"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:91
|
||||||
|
msgid "e.g., 10"
|
||||||
|
msgstr "p.ex. 10"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:95
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:114
|
||||||
|
msgid "e.g., 9.99"
|
||||||
|
msgstr "p.ex. 9.90"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:97
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:120
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr "Supprimer"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:109
|
||||||
|
#: includes/class-wc-tpp-frontend.php:75
|
||||||
|
msgid "Quantity"
|
||||||
|
msgstr "Quantité"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:113
|
||||||
|
msgid "Fixed Price"
|
||||||
|
msgstr "Prix fixe"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:117
|
||||||
|
msgid "Label (Optional)"
|
||||||
|
msgstr "Étiquette (Optionnel)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:118
|
||||||
|
msgid "e.g., Starter Pack"
|
||||||
|
msgstr "p.ex. Pack de démarrage"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:71
|
||||||
|
msgid "Volume Discounts"
|
||||||
|
msgstr "Remises sur quantité"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:94
|
||||||
|
#: includes/class-wc-tpp-frontend.php:76
|
||||||
|
msgid "Price per Unit"
|
||||||
|
msgstr "Prix par unité"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:77
|
||||||
|
msgid "You Save"
|
||||||
|
msgstr "Vous économisez"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:110
|
||||||
|
msgid "Package Deals"
|
||||||
|
msgstr "Offres forfaitaires"
|
||||||
|
|
||||||
|
#: templates/frontend/package-pricing-display.twig:11
|
||||||
|
msgid "Choose a package size below"
|
||||||
|
msgstr "Choisissez une taille de forfait ci-dessous"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:123
|
||||||
|
msgid "pieces"
|
||||||
|
msgstr "pièces"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:129
|
||||||
|
msgid "per unit"
|
||||||
|
msgstr "par unité"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:133
|
||||||
|
msgid "Select Package"
|
||||||
|
msgstr "Sélectionner le forfait"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:63
|
||||||
|
msgid "Package price"
|
||||||
|
msgstr "Prix forfaitaire"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:66
|
||||||
|
msgid "Volume discount"
|
||||||
|
msgstr "Remise sur quantité"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:124
|
||||||
|
msgid "this product"
|
||||||
|
msgstr "ce produit"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:128
|
||||||
|
msgid "The quantity %1$d is not available for %2$s. Please choose from the available package sizes: %3$s"
|
||||||
|
msgstr "La quantité %1$d n'est pas disponible pour %2$s. Veuillez choisir parmi les tailles de forfait disponibles: %3$s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:173
|
||||||
|
msgid "View Options"
|
||||||
|
msgstr "Voir les options"
|
||||||
|
|
||||||
|
|
||||||
|
#: 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."
|
||||||
|
|
||||||
|
#. v1.4.1 - Auto-Updates and License Bypass
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Updates"
|
||||||
|
msgstr "Mises à jour automatiques"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Update Settings"
|
||||||
|
msgstr "Paramètres de mise à jour automatique"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Configure automatic plugin updates from the license server."
|
||||||
|
msgstr "Configurez les mises à jour automatiques du plugin depuis le serveur de licence."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Enable Update Notifications"
|
||||||
|
msgstr "Activer les notifications de mise à jour"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for available plugin updates."
|
||||||
|
msgstr "Rechercher les mises à jour disponibles du plugin."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
msgstr "Lorsque cette option est activée, le plugin vérifiera les mises à jour depuis le serveur de licence et affichera des notifications dans l'administration WordPress."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically Install Updates"
|
||||||
|
msgstr "Installer automatiquement les mises à jour"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically install updates when available."
|
||||||
|
msgstr "Installer automatiquement les mises à jour lorsqu'elles sont disponibles."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Requires a valid license)"
|
||||||
|
msgstr "(Nécessite une licence valide)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
msgstr "Lorsque cette option est activée, les mises à jour seront automatiquement installées lors des mises à jour en arrière-plan de WordPress."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check Frequency (Hours)"
|
||||||
|
msgstr "Fréquence de vérification (heures)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "How often to check for updates."
|
||||||
|
msgstr "Fréquence de recherche des mises à jour."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Number of hours between update checks. Default is 12 hours."
|
||||||
|
msgstr "Nombre d'heures entre les vérifications de mise à jour. Par défaut: 12 heures."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Status"
|
||||||
|
msgstr "Statut de mise à jour"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for Updates"
|
||||||
|
msgstr "Rechercher des mises à jour"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Current Version:"
|
||||||
|
msgstr "Version actuelle:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Available:"
|
||||||
|
msgstr "Mise à jour disponible:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "You are running the latest version."
|
||||||
|
msgstr "Vous utilisez la dernière version."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Now"
|
||||||
|
msgstr "Mettre à jour maintenant"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
#. translators: %s: Version number
|
||||||
|
msgid "Update available: version %s"
|
||||||
|
msgstr "Mise à jour disponible: version %s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Failed to check for updates."
|
||||||
|
msgstr "Échec de la recherche de mises à jour."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Localhost environment - license validation bypassed)"
|
||||||
|
msgstr "(Environnement localhost - validation de licence ignorée)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Self-licensing server - license validation bypassed)"
|
||||||
|
msgstr "(Serveur auto-licencié - validation de licence ignorée)"
|
||||||
BIN
languages/wc-tier-package-prices-it_CH.mo
Normal file
BIN
languages/wc-tier-package-prices-it_CH.mo
Normal file
Binary file not shown.
495
languages/wc-tier-package-prices-it_CH.po
Normal file
495
languages/wc-tier-package-prices-it_CH.po
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
# Italian (Switzerland) translation for WooCommerce Tier and Package Prices
|
||||||
|
# Copyright (C) 2026 Marco Graetsch
|
||||||
|
# This file is distributed under the GPL v2 or later.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
|
||||||
|
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n"
|
||||||
|
"POT-Creation-Date: 2026-02-03 00:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2026-02-03 00:00+0000\n"
|
||||||
|
"Last-Translator: Marco Graetsch\n"
|
||||||
|
"Language-Team: Italian (Switzerland)\n"
|
||||||
|
"Language: it_CH\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 3.0\n"
|
||||||
|
"X-Domain: wc-tier-package-prices\n"
|
||||||
|
|
||||||
|
#: wc-tier-and-package-prices.php:41
|
||||||
|
msgid "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active."
|
||||||
|
msgstr "WooCommerce Prezzi Scaglionati e Pacchetti richiede che WooCommerce sia installato e attivo."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:21
|
||||||
|
#: includes/class-wc-tpp-admin.php:22
|
||||||
|
#: includes/class-wc-tpp-settings.php:28
|
||||||
|
msgid "Tier & Package Prices"
|
||||||
|
msgstr "Prezzi Scaglionati & Pacchetti"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:40
|
||||||
|
msgid "General"
|
||||||
|
msgstr "Generale"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:58
|
||||||
|
msgid "Tier & Package Prices Settings"
|
||||||
|
msgstr "Impostazioni Prezzi Scaglionati & Pacchetti"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:60
|
||||||
|
msgid "Configure tier pricing and package pricing options for your WooCommerce products."
|
||||||
|
msgstr "Configura le opzioni di prezzi scaglionati e pacchetti per i tuoi prodotti WooCommerce."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:54
|
||||||
|
#: includes/class-wc-tpp-settings.php:65
|
||||||
|
msgid "Enable Tier Pricing"
|
||||||
|
msgstr "Attiva prezzi scaglionati"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:58
|
||||||
|
#: includes/class-wc-tpp-settings.php:66
|
||||||
|
msgid "Enable tier pricing for products"
|
||||||
|
msgstr "Attiva prezzi scaglionati per i prodotti"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:70
|
||||||
|
msgid "Allow quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities."
|
||||||
|
msgstr "Permette scaglioni di prezzo basati sulla quantità. I clienti ottengono prezzi scontati quando acquistano quantità maggiori."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:63
|
||||||
|
#: includes/class-wc-tpp-settings.php:74
|
||||||
|
msgid "Enable Package Pricing"
|
||||||
|
msgstr "Attiva prezzi pacchetto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:67
|
||||||
|
#: includes/class-wc-tpp-settings.php:75
|
||||||
|
msgid "Enable fixed-price packages for products"
|
||||||
|
msgstr "Attiva pacchetti a prezzo fisso per i prodotti"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:79
|
||||||
|
msgid "Allow fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100."
|
||||||
|
msgstr "Permette pacchetti a prezzo fisso con quantità specifiche. Ad esempio: 10 pezzi per CHF 50.-, 25 pezzi per CHF 100.-."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:72
|
||||||
|
#: includes/class-wc-tpp-settings.php:83
|
||||||
|
msgid "Display Pricing Table"
|
||||||
|
msgstr "Visualizza tabella prezzi"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:76
|
||||||
|
#: includes/class-wc-tpp-settings.php:84
|
||||||
|
msgid "Show tier and package pricing table on product pages"
|
||||||
|
msgstr "Mostra la tabella dei prezzi scaglionati e pacchetti nelle pagine prodotto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:88
|
||||||
|
msgid "Display the pricing table to customers on product pages."
|
||||||
|
msgstr "Visualizza la tabella prezzi ai clienti nelle pagine prodotto."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:81
|
||||||
|
#: includes/class-wc-tpp-settings.php:92
|
||||||
|
msgid "Display Position"
|
||||||
|
msgstr "Posizione visualizzazione"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:93
|
||||||
|
msgid "Choose where to display the pricing table on product pages."
|
||||||
|
msgstr "Scegli dove visualizzare la tabella prezzi nelle pagine prodotto."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:101
|
||||||
|
msgid "Before Add to Cart Button"
|
||||||
|
msgstr "Prima del pulsante \"Aggiungi al carrello\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:102
|
||||||
|
msgid "After Add to Cart Button"
|
||||||
|
msgstr "Dopo il pulsante \"Aggiungi al carrello\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:85
|
||||||
|
msgid "Before Add to Cart"
|
||||||
|
msgstr "Prima di \"Aggiungi al carrello\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:86
|
||||||
|
msgid "After Add to Cart"
|
||||||
|
msgstr "Dopo \"Aggiungi al carrello\""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-admin.php:87
|
||||||
|
#: includes/class-wc-tpp-settings.php:103
|
||||||
|
msgid "After Price"
|
||||||
|
msgstr "Dopo il prezzo"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:108
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:76
|
||||||
|
msgid "Restrict to Package Quantities"
|
||||||
|
msgstr "Limita alle quantità pacchetto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:109
|
||||||
|
msgid "Limit quantities to defined package sizes only"
|
||||||
|
msgstr "Limita le quantità solo alle dimensioni pacchetto definite"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php:113
|
||||||
|
msgid "When enabled, customers can only purchase products in the exact quantities defined in packages. The quantity input field will be hidden and replaced with package selection buttons."
|
||||||
|
msgstr "Quando attivato, i clienti possono acquistare prodotti solo nelle quantità esatte definite nei pacchetti. Il campo di inserimento quantità verrà nascosto e sostituito con pulsanti di selezione pacchetto."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:23
|
||||||
|
msgid "Tier Pricing"
|
||||||
|
msgstr "Prezzi scaglionati"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:24
|
||||||
|
msgid "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities."
|
||||||
|
msgstr "Imposta scaglioni di prezzo basati sulla quantità. I clienti ottengono sconti quando acquistano quantità maggiori."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:41
|
||||||
|
msgid "Add Tier"
|
||||||
|
msgstr "Aggiungi scaglione"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:52
|
||||||
|
msgid "Package Pricing"
|
||||||
|
msgstr "Prezzi pacchetto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:53
|
||||||
|
msgid "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100."
|
||||||
|
msgstr "Imposta pacchetti a prezzo fisso con quantità specifiche. Ad esempio: 10 pezzi per CHF 50.-, 25 pezzi per CHF 100.-."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:70
|
||||||
|
msgid "Add Package"
|
||||||
|
msgstr "Aggiungi pacchetto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:77
|
||||||
|
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"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:91
|
||||||
|
msgid "e.g., 10"
|
||||||
|
msgstr "es. 10"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:95
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:114
|
||||||
|
msgid "e.g., 9.99"
|
||||||
|
msgstr "es. 9.90"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:97
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:120
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr "Rimuovi"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:109
|
||||||
|
#: includes/class-wc-tpp-frontend.php:75
|
||||||
|
msgid "Quantity"
|
||||||
|
msgstr "Quantità"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:113
|
||||||
|
msgid "Fixed Price"
|
||||||
|
msgstr "Prezzo fisso"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:117
|
||||||
|
msgid "Label (Optional)"
|
||||||
|
msgstr "Etichetta (Opzionale)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:118
|
||||||
|
msgid "e.g., Starter Pack"
|
||||||
|
msgstr "es. Pacchetto starter"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:71
|
||||||
|
msgid "Volume Discounts"
|
||||||
|
msgstr "Sconti quantità"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-product-meta.php:94
|
||||||
|
#: includes/class-wc-tpp-frontend.php:76
|
||||||
|
msgid "Price per Unit"
|
||||||
|
msgstr "Prezzo per unità"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:77
|
||||||
|
msgid "You Save"
|
||||||
|
msgstr "Risparmi"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:110
|
||||||
|
msgid "Package Deals"
|
||||||
|
msgstr "Offerte pacchetto"
|
||||||
|
|
||||||
|
#: templates/frontend/package-pricing-display.twig:11
|
||||||
|
msgid "Choose a package size below"
|
||||||
|
msgstr "Scegli una dimensione pacchetto qui sotto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:123
|
||||||
|
msgid "pieces"
|
||||||
|
msgstr "pezzi"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:129
|
||||||
|
msgid "per unit"
|
||||||
|
msgstr "per unità"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:133
|
||||||
|
msgid "Select Package"
|
||||||
|
msgstr "Seleziona pacchetto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:63
|
||||||
|
msgid "Package price"
|
||||||
|
msgstr "Prezzo pacchetto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:66
|
||||||
|
msgid "Volume discount"
|
||||||
|
msgstr "Sconto quantità"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:124
|
||||||
|
msgid "this product"
|
||||||
|
msgstr "questo prodotto"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-cart.php:128
|
||||||
|
msgid "The quantity %1$d is not available for %2$s. Please choose from the available package sizes: %3$s"
|
||||||
|
msgstr "La quantità %1$d non è disponibile per %2$s. Si prega di scegliere tra le dimensioni pacchetto disponibili: %3$s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-frontend.php:173
|
||||||
|
msgid "View Options"
|
||||||
|
msgstr "Visualizza opzioni"
|
||||||
|
|
||||||
|
|
||||||
|
#: 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."
|
||||||
|
|
||||||
|
#. v1.4.1 - Auto-Updates and License Bypass
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Updates"
|
||||||
|
msgstr "Aggiornamenti automatici"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Update Settings"
|
||||||
|
msgstr "Impostazioni aggiornamento automatico"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Configure automatic plugin updates from the license server."
|
||||||
|
msgstr "Configura gli aggiornamenti automatici del plugin dal server di licenza."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Enable Update Notifications"
|
||||||
|
msgstr "Abilita notifiche di aggiornamento"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for available plugin updates."
|
||||||
|
msgstr "Verifica la disponibilità di aggiornamenti del plugin."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
msgstr "Se abilitato, il plugin verificherà la presenza di aggiornamenti dal server di licenza e mostrerà le notifiche nell'amministrazione WordPress."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically Install Updates"
|
||||||
|
msgstr "Installa automaticamente gli aggiornamenti"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically install updates when available."
|
||||||
|
msgstr "Installa automaticamente gli aggiornamenti quando disponibili."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Requires a valid license)"
|
||||||
|
msgstr "(Richiede una licenza valida)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
msgstr "Se abilitato, gli aggiornamenti verranno installati automaticamente quando WordPress esegue gli aggiornamenti in background."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check Frequency (Hours)"
|
||||||
|
msgstr "Frequenza di controllo (ore)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "How often to check for updates."
|
||||||
|
msgstr "Con quale frequenza controllare gli aggiornamenti."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Number of hours between update checks. Default is 12 hours."
|
||||||
|
msgstr "Numero di ore tra i controlli degli aggiornamenti. Predefinito: 12 ore."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Status"
|
||||||
|
msgstr "Stato aggiornamento"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for Updates"
|
||||||
|
msgstr "Verifica aggiornamenti"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Current Version:"
|
||||||
|
msgstr "Versione attuale:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Available:"
|
||||||
|
msgstr "Aggiornamento disponibile:"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "You are running the latest version."
|
||||||
|
msgstr "Stai utilizzando l'ultima versione."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Now"
|
||||||
|
msgstr "Aggiorna ora"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
#. translators: %s: Version number
|
||||||
|
msgid "Update available: version %s"
|
||||||
|
msgstr "Aggiornamento disponibile: versione %s"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Failed to check for updates."
|
||||||
|
msgstr "Impossibile verificare la presenza di aggiornamenti."
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Localhost environment - license validation bypassed)"
|
||||||
|
msgstr "(Ambiente localhost - verifica della licenza saltata)"
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Self-licensing server - license validation bypassed)"
|
||||||
|
msgstr "(Server auto-licenziato - verifica della licenza saltata)"
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Copyright (C) 2025 Marco Graetsch
|
# Copyright (C) 2026 Marco Graetsch
|
||||||
# This file is distributed under the GPL v2 or later.
|
# This file is distributed under the GPL v2 or later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: WooCommerce Tier and Package Prices 1.1.6\n"
|
"Project-Id-Version: WooCommerce Tier and Package Prices 1.4.1\n"
|
||||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\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-02-03 00:00+0000\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
@@ -132,6 +132,26 @@ msgstr ""
|
|||||||
msgid "Only allow quantities defined in packages above"
|
msgid "Only allow quantities defined in packages above"
|
||||||
msgstr ""
|
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
|
#: templates/admin/tier-row.twig:9
|
||||||
msgid "Minimum Quantity"
|
msgid "Minimum Quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -230,3 +250,219 @@ msgstr ""
|
|||||||
#: includes/class-wc-tpp-frontend.php:178
|
#: includes/class-wc-tpp-frontend.php:178
|
||||||
msgid "View options for %s"
|
msgid "View options for %s"
|
||||||
msgstr ""
|
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 ""
|
||||||
|
|
||||||
|
#. v1.4.1 - Auto-Updates and License Bypass
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Updates"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Auto-Update Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Configure automatic plugin updates from the license server."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Enable Update Notifications"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for available plugin updates."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, the plugin will check for updates from the license server and show notifications in the WordPress admin."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically Install Updates"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Automatically install updates when available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Requires a valid license)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "When enabled, updates will be automatically installed when WordPress performs background updates."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check Frequency (Hours)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "How often to check for updates."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Number of hours between update checks. Default is 12 hours."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Check for Updates"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Current Version:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Available:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "You are running the latest version."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Update Now"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
#. translators: %s: Version number
|
||||||
|
msgid "Update available: version %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "Failed to check for updates."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Localhost environment - license validation bypassed)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-wc-tpp-settings.php
|
||||||
|
msgid "(Self-licensing server - license validation bypassed)"
|
||||||
|
msgstr ""
|
||||||
|
|||||||
1
lib/wc-licensed-product-client
Submodule
1
lib/wc-licensed-product-client
Submodule
Submodule lib/wc-licensed-product-client added at 56abe8a97c
@@ -1,174 +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.0.1** - December 21, 2025
|
|
||||||
|
|
||||||
### Quick Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Download the package
|
|
||||||
wget https://your-domain.com/releases/wc-tier-and-package-prices-1.0.1.zip
|
|
||||||
|
|
||||||
# Verify checksum (optional but recommended)
|
|
||||||
sha256sum wc-tier-and-package-prices-1.0.1.zip
|
|
||||||
# Should match: 92c1385d92527e77646e37f23c1bd1555a4290a5ec9314c0ee6ed896ded55e88
|
|
||||||
|
|
||||||
# Install via WordPress admin or WP-CLI
|
|
||||||
wp plugin install wc-tier-and-package-prices-1.0.1.zip --activate
|
|
||||||
```
|
|
||||||
|
|
||||||
## Files in This Directory
|
|
||||||
|
|
||||||
### Release Packages
|
|
||||||
|
|
||||||
| File | Description | Size |
|
|
||||||
|------|-------------|------|
|
|
||||||
| `wc-tier-and-package-prices-1.0.1.zip` | Production plugin package | 395 KB |
|
|
||||||
| `wc-tier-and-package-prices-1.0.1.zip.sha256` | SHA-256 checksum | - |
|
|
||||||
| `wc-tier-and-package-prices-1.0.1.zip.md5` | MD5 checksum | - |
|
|
||||||
| `RELEASE-INFO-1.0.1.md` | Detailed release information | - |
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
### Verify Package Integrity
|
|
||||||
|
|
||||||
**Using SHA-256:**
|
|
||||||
```bash
|
|
||||||
sha256sum -c wc-tier-and-package-prices-1.0.1.zip.sha256
|
|
||||||
```
|
|
||||||
|
|
||||||
**Using MD5:**
|
|
||||||
```bash
|
|
||||||
md5sum -c wc-tier-and-package-prices-1.0.1.zip.md5
|
|
||||||
```
|
|
||||||
|
|
||||||
### Expected Checksums
|
|
||||||
|
|
||||||
**SHA-256:**
|
|
||||||
```
|
|
||||||
92c1385d92527e77646e37f23c1bd1555a4290a5ec9314c0ee6ed896ded55e88
|
|
||||||
```
|
|
||||||
|
|
||||||
**MD5:**
|
|
||||||
```
|
|
||||||
e6cfc9b88df9e7763be0cd56517ce8ab
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation Methods
|
|
||||||
|
|
||||||
### Method 1: WordPress Admin (Recommended for most users)
|
|
||||||
|
|
||||||
1. Download `wc-tier-and-package-prices-1.0.1.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.0.1.zip --activate
|
|
||||||
```
|
|
||||||
|
|
||||||
### Method 3: Manual Installation (Advanced)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Extract to wp-content/plugins/
|
|
||||||
unzip wc-tier-and-package-prices-1.0.1.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 (quantity-based discounts)
|
|
||||||
- ✅ Package pricing (fixed-price bundles)
|
|
||||||
- ✅ Twig template engine
|
|
||||||
- ✅ WooCommerce HPOS compatible
|
|
||||||
- ✅ Multilingual support
|
|
||||||
|
|
||||||
### 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.0
|
|
||||||
|
|
||||||
## Package Contents
|
|
||||||
|
|
||||||
```
|
|
||||||
wc-tier-and-package-prices/
|
|
||||||
├── assets/ # CSS and JavaScript
|
|
||||||
│ ├── css/
|
|
||||||
│ └── js/
|
|
||||||
├── includes/ # PHP classes
|
|
||||||
│ ├── class-wc-tpp-admin.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.*
|
|
||||||
│ ├── wc-tier-package-prices-de_DE.*
|
|
||||||
│ ├── wc-tier-package-prices-en_US.*
|
|
||||||
│ └── wc-tier-package-prices.pot
|
|
||||||
├── templates/ # Twig templates
|
|
||||||
│ ├── admin/
|
|
||||||
│ └── frontend/
|
|
||||||
├── vendor/ # Composer dependencies
|
|
||||||
│ └── twig/twig/
|
|
||||||
├── CHANGELOG.md
|
|
||||||
├── README.md
|
|
||||||
├── composer.json
|
|
||||||
└── wc-tier-and-package-prices.php
|
|
||||||
```
|
|
||||||
|
|
||||||
## System Requirements
|
|
||||||
|
|
||||||
| Requirement | Minimum Version |
|
|
||||||
|-------------|----------------|
|
|
||||||
| WordPress | 6.0+ |
|
|
||||||
| PHP | 7.4+ |
|
|
||||||
| WooCommerce | 8.0+ |
|
|
||||||
| MySQL | 5.6+ |
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
- **Documentation:** See main README.md
|
|
||||||
- **Issues:** https://src.bundespruefstelle.ch/wc-tier-package-prices/issues
|
|
||||||
- **Author:** Marco Graetsch
|
|
||||||
|
|
||||||
## Version History
|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
## 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 @@
|
|||||||
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
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user