From fa26247d1b008b0c264ea6f504d8c1a4f0af5d60 Mon Sep 17 00:00:00 2001 From: magdev Date: Tue, 3 Feb 2026 11:55:41 +0100 Subject: [PATCH] Version 1.4.1 - Localhost license bypass and auto-updates Added localhost/self-licensing license bypass and WordPress auto-update integration from license server. Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 57 +++ CLAUDE.md | 59 ++- includes/class-wc-tpp-license-checker.php | 333 +++++++++++++ includes/class-wc-tpp-settings.php | 221 ++++++++- includes/class-wc-tpp-update-checker.php | 468 ++++++++++++++++++ languages/wc-tier-package-prices-de_CH.mo | Bin 9257 -> 11585 bytes languages/wc-tier-package-prices-de_CH.po | 103 +++- .../wc-tier-package-prices-de_CH_informal.mo | Bin 9214 -> 11537 bytes .../wc-tier-package-prices-de_CH_informal.po | 103 +++- languages/wc-tier-package-prices-de_DE.mo | Bin 9225 -> 11553 bytes languages/wc-tier-package-prices-de_DE.po | 103 +++- .../wc-tier-package-prices-de_DE_informal.mo | Bin 9181 -> 11504 bytes .../wc-tier-package-prices-de_DE_informal.po | 103 +++- languages/wc-tier-package-prices-en_US.mo | Bin 8865 -> 11101 bytes languages/wc-tier-package-prices-en_US.po | 103 +++- languages/wc-tier-package-prices-fr_CH.mo | Bin 9820 -> 12398 bytes languages/wc-tier-package-prices-fr_CH.po | 103 +++- languages/wc-tier-package-prices-it_CH.mo | Bin 9522 -> 12010 bytes languages/wc-tier-package-prices-it_CH.po | 103 +++- languages/wc-tier-package-prices.pot | 101 +++- wc-tier-and-package-prices.php | 18 +- 21 files changed, 1940 insertions(+), 38 deletions(-) create mode 100644 includes/class-wc-tpp-license-checker.php create mode 100644 includes/class-wc-tpp-update-checker.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f2cc61..262f780 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,63 @@ All notable changes to WooCommerce Tier and Package Prices will be documented in The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.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 diff --git a/CLAUDE.md b/CLAUDE.md index 7b80416..34c15a1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,7 @@ # WooCommerce Tier and Package Prices - AI Context Document -**Last Updated:** 2026-01-29 -**Current Version:** 1.4.0 +**Last Updated:** 2026-02-03 +**Current Version:** 1.4.1 **Author:** Marco Graetsch **Project Status:** Production-ready WordPress plugin @@ -20,9 +20,9 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w **Note for AI Assistants:** Clean this section after the specific features are done or new releases are made. Effective changes are tracked in `CHANGELOG.md`. Do not add completed versions here - document them in the Session History section at the end of this file. Always keep the `Known Bugs` section and create a section with the next bugfix and minor version after a release. -### Version 1.4.1 +### Version 1.4.2 -- No planned changes yet +- No planned features yet ## Technical Stack @@ -1270,6 +1270,57 @@ The plugin architecture is solid and well-tested. Most bugs arise from: - Uses `actions/checkout@v4` with `submodules: recursive` - Modeled after working wp-fedistream implementation +### v1.4.1 Release Session (2026-02-03) + +**Accomplished:** + +1. Implemented localhost license bypass: + - Created `WC_TPP_License_Checker` class (singleton pattern) + - `is_localhost()` detects localhost environments: localhost, 127.0.0.1, ::1, `*.localhost`, `*.local`, `*.test`, `*.example`, `*.invalid`, and private IP ranges + - `is_self_licensing()` compares license server URL with site URL + - `is_license_valid()` returns true for localhost/self-licensing without server check + - Updated license status display to show bypass reason + +2. Implemented auto-update system: + - Created `WC_TPP_Update_Checker` class (singleton pattern) + - Hooks into WordPress plugin update system (`pre_set_site_transient_update_plugins`, `plugins_api`, `http_request_args`, `auto_update_plugin`) + - Fetches update info from license server API endpoint + - Supports auto-install when WordPress performs background updates + - Configurable check frequency (1-168 hours) + +3. Added Auto-Updates settings tab: + - Enable Update Notifications (checkbox, default: yes) + - Automatically Install Updates (checkbox, default: no, requires valid license) + - Check Frequency in Hours (number, default: 12) + - Update Status display with "Check for Updates" button + - AJAX handler for manual update checks + +4. Updated all translation files: + - Added 23 new translation strings for v1.4.1 + - All 7 language variants updated (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH, it_CH, en_US) + - Compiled all .mo files + +**Key Implementation Details:** + +- License checker uses 1-hour cache for successful validation, 5-minute cache for errors +- Update checker respects `WC_TPP_DISABLE_AUTO_UPDATE` constant to disable updates +- Update server endpoint: `POST /wp-json/wc-licensed-product/v1/update-check` +- Both new classes are instantiated in main plugin's `includes()` method +- Auto-install requires valid license (disabled when no license active) + +**New Files:** + +- `includes/class-wc-tpp-license-checker.php` +- `includes/class-wc-tpp-update-checker.php` + +**Modified Files:** + +- `wc-tier-and-package-prices.php` - Version bump, include new classes +- `includes/class-wc-tpp-settings.php` - Added Auto-Updates tab, updated license status display +- All translation files (.pot, .po, .mo) +- `CHANGELOG.md` - v1.4.1 release notes +- `CLAUDE.md` - Updated version and session history + --- Always refer to this document when starting work on this project. Good luck! diff --git a/includes/class-wc-tpp-license-checker.php b/includes/class-wc-tpp-license-checker.php new file mode 100644 index 0000000..0a8e69e --- /dev/null +++ b/includes/class-wc-tpp-license-checker.php @@ -0,0 +1,333 @@ +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(); + } + } +} diff --git a/includes/class-wc-tpp-settings.php b/includes/class-wc-tpp-settings.php index 6783ab4..5600795 100755 --- a/includes/class-wc-tpp-settings.php +++ b/includes/class-wc-tpp-settings.php @@ -33,6 +33,9 @@ if (!class_exists('WC_TPP_Settings')) { // Add AJAX handlers for license validation add_action('wp_ajax_wc_tpp_validate_license', array($this, 'ajax_validate_license')); add_action('wp_ajax_wc_tpp_activate_license', array($this, 'ajax_activate_license')); + + // Add AJAX handler for update checks + add_action('wp_ajax_wc_tpp_check_updates', array($this, 'ajax_check_updates')); } /** @@ -44,6 +47,7 @@ if (!class_exists('WC_TPP_Settings')) { return array( '' => __('General', 'wc-tier-package-prices'), 'license' => __('License', 'wc-tier-package-prices'), + 'updates' => __('Auto-Updates', 'wc-tier-package-prices'), ); } @@ -177,6 +181,75 @@ if (!class_exists('WC_TPP_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 * @@ -368,8 +441,9 @@ if (!class_exists('WC_TPP_Settings')) { public function output() { global $current_section; - // Register custom field type for license status display + // 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(); @@ -377,6 +451,11 @@ if (!class_exists('WC_TPP_Settings')) { if ('license' === $current_section) { $this->output_license_scripts(); } + + // Add JavaScript for updates section + if ('updates' === $current_section) { + $this->output_updates_scripts(); + } } /** @@ -415,6 +494,20 @@ if (!class_exists('WC_TPP_Settings')) { * @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 '' . esc_html__('License Active', 'wc-tier-package-prices') . ''; + if ('localhost' === $bypass_reason) { + echo '
' . esc_html__('(Localhost environment - license validation bypassed)', 'wc-tier-package-prices') . ''; + } elseif ('self_licensing' === $bypass_reason) { + echo '
' . esc_html__('(Self-licensing server - license validation bypassed)', 'wc-tier-package-prices') . ''; + } + return; + } + if (empty($status)) { echo '' . esc_html__('No license activated', 'wc-tier-package-prices') . ''; return; @@ -443,6 +536,50 @@ if (!class_exists('WC_TPP_Settings')) { } } + /** + * 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(); + ?> + + + + + +
+ render_update_status_html($available_version); ?> +
+

+ + +

+ + + ' . esc_html__('Current Version:', 'wc-tier-package-prices') . ' ' . esc_html(WC_TPP_VERSION) . '

'; + + if ($available_version) { + echo '

' . esc_html__('Update Available:', 'wc-tier-package-prices') . ' ' . esc_html($available_version) . '

'; + echo '

' . esc_html__('Update Now', 'wc-tier-package-prices') . '

'; + } else { + echo '

' . esc_html__('You are running the latest version.', 'wc-tier-package-prices') . '

'; + } + } + /** * Output JavaScript for license management */ @@ -525,5 +662,87 @@ if (!class_exists('WC_TPP_Settings')) { __('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'); + ?> + + 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'] ?? 'Marco Graetsch'; + $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(); + } + } +} diff --git a/languages/wc-tier-package-prices-de_CH.mo b/languages/wc-tier-package-prices-de_CH.mo index 289760614f8d94bc9a6bb9dbce8173a5b52b90eb..460f8b9461336220dc1b9582d518117c2cdb81f3 100644 GIT binary patch delta 4423 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 MZL9XQeWiBdKZZQqumAu6 diff --git a/languages/wc-tier-package-prices-de_CH.po b/languages/wc-tier-package-prices-de_CH.po index a61bc70..2a278b0 100644 --- a/languages/wc-tier-package-prices-de_CH.po +++ b/languages/wc-tier-package-prices-de_CH.po @@ -1,12 +1,12 @@ # German (Switzerland) 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. msgid "" msgstr "" -"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\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" -"POT-Creation-Date: 2025-12-30 00:00+0000\n" -"PO-Revision-Date: 2025-12-30 00:00+0000\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" @@ -398,3 +398,98 @@ 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)" diff --git a/languages/wc-tier-package-prices-de_CH_informal.mo b/languages/wc-tier-package-prices-de_CH_informal.mo index 64b6c143c74ffcde5abcfbced02a4309e718d594..868dbca3a7b338213fb5c27bf46903ec2a0feeb6 100644 GIT binary patch delta 4417 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 u8fD8~ra;CPyr9~;q^823? delta 2165 zcmX}se@xVM9LMp`omYS&aMuZfi3c14$sNB$AW}rZ!{~<~m7)`ze9>#fF09rlcb0#Y zR<`-oRaE{cw`{sq`}zm9(AlbG+1jZ7u~u3u=T@6D|0rv%=j-lkpYguO=X2lheSW+@ zpASd+cXuU!2>1>d$_C42sLmNmt#9> z*PcfH%(Hxub@LJ~#>03sev2jeJtn)T{7a=6WAv_+4I^Kf7f}Np#Bn@ z$5&CmuV9&*aTTt_W2i0q4fWj1Y_-r5R0g5}_FpS#b074&4+fC2Oaf2hPTYizY-}Iy zN3HBKY9&|Qb}(oR$HUA=eqt(7@3o><-hrAxH%@wtSr;V#OwRZ@IXZ-8sCFCDr|H20 zv`{P9fy&HlIEu${8y4|W40j=4nJ;iY{)AlxQ+)O$apR(J)KsT}@!bYLke^;>Zj-$8^SD;S!tEeqH;UllJ?l75mtyC76RcGo8o~<|tO+8PozVr)2-H zQ+b&$W^q|Z^dfFVf5@2IaTLj_c^l*SG2V?)PM%isB!c zm8LUNfX5i$gsBYTmpFh?PK+|J7fF$M1@#Z=Bh(5%N2T~1RK_l$w(t+s;k=F>R8c=Y zgtlTOaii#rX;KKEhsR^nm#U#!=KrAQL5Xw>; zaUY=xYnycFRJsWccrtZ8l(t$z>0Cyrv=U_O|Du(t%pg{$YUx$$_S6u{P&KiEP|=xD z(H7JZOVw~o45O(!>%YzYl4Fo6x?b!=Y7bk|Uzsk{p}Wg%H@fy>glHy0gi1M~gQhc+ zNpO?RT4DvE?W-bG7U{!2kdbR2&hT}tr=feI4ecj%Nxg)Qyoyd(eX5qe@T%TV=!C8! z?jW3|nF)_QC%eYpmpy1lXMgPka>_mS+T3RX@yD&9(3-LM7R$t}QEP1Th*Om}?s0zf zZ}d64@-sd5(A?8@tnl?2_0cB#QsGcWW2CM=(opO47WqBS-}653*tNxt_Ehmsd&h!b zokv4qkA14-xPSX2i7_j_-L#L4Y_%S;;-k(dr4t_eVyM#jDtyZ4T&SY&aCOAKvgn}w HUUlL>w};#A diff --git a/languages/wc-tier-package-prices-de_DE.po b/languages/wc-tier-package-prices-de_DE.po index ee2d551..4490949 100644 --- a/languages/wc-tier-package-prices-de_DE.po +++ b/languages/wc-tier-package-prices-de_DE.po @@ -1,12 +1,12 @@ # 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. msgid "" msgstr "" -"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\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" -"POT-Creation-Date: 2025-12-30 00:00+0000\n" -"PO-Revision-Date: 2025-12-30 00:00+0000\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\n" "Language: de_DE\n" @@ -398,3 +398,98 @@ 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)" diff --git a/languages/wc-tier-package-prices-de_DE_informal.mo b/languages/wc-tier-package-prices-de_DE_informal.mo index b338813dce3a9d9739490df7410fc3494f4befbe..ec27219e6cad51cfc0f38f22fe10cf1046918329 100644 GIT binary patch delta 4425 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 Y8~In6-Y1*hC!5|Uo8rUmyY|WEzxUg{k^lez delta 2162 zcmbu=%Wsrb7{~EvhT2x;<~V4}*gCbA*6K`Yp`~R63bwR5UTCdI#X?Ct4JAfK7GP@P zNMizN1eCYL1fyJJK?u>s%fc=Q8yBE##GuAdBQXXMSV&-@F~;xjeGmQvp7fc|Iq&6M zpP5dtJ!bh+9#!0-nE%56amRv$=QEYUP^rW&de@eXAeU?(YM@VW3{T<+)-wGe+>7e? zChGlKmbnR6<5PGXbwt-u_oK{K3#~%!Ksv_xYXvLZjb3+SBQlod@f5y;8?c#!?ZZjb z%C4eTa?`craWm4xDv(#K4)t6Hwek+s1hP04Fir*ZmVWB4P#NRG zZB%yWd0N^1J*wjj%Txz>RCez}W%nPbBWg~D_p_*#Zb9w94%7m6qpp91!Oox-I)$eS zRL)V^fNzt^eK?I;S<@roN;0VS04lpjP#wSFuD^>~`5x3jlSpz0Mb9hIv=B}DPSFH3 z8I2zlB~K@@l6Zl5p6DR%6isZ2YjA`?DdJOGs>1FE`S3Ef6Kja)h!w<3geLq95hql# z1iM$TYAQPag@iJEIiZpv*ogm%&P-(n@pMoNSG|aO1EDN`f>=+etP3$tHTZ@tb8XQ@ zq=Po=uMaBW8LC=Fu!F(ZtW6gv-Gs9ISv4rlA=Zm2LYbc=RBDJ?f-g>RYj(!?CJbmY0A^4%ND4~>Bd6Z}hYGwek|f&OKknWo&O6d3EN5l diff --git a/languages/wc-tier-package-prices-en_US.po b/languages/wc-tier-package-prices-en_US.po index 1ff2228..ae2f68c 100644 --- a/languages/wc-tier-package-prices-en_US.po +++ b/languages/wc-tier-package-prices-en_US.po @@ -1,12 +1,12 @@ # 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. msgid "" msgstr "" -"Project-Id-Version: WooCommerce Tier and Package Prices 1.2.7\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" -"POT-Creation-Date: 2025-12-30 00:00+0000\n" -"PO-Revision-Date: 2025-12-30 00:00+0000\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: English\n" "Language: en_US\n" @@ -398,3 +398,98 @@ 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)" diff --git a/languages/wc-tier-package-prices-fr_CH.mo b/languages/wc-tier-package-prices-fr_CH.mo index b058265a0e3e4baa84e1eb36a69ac5d392a1bd6d..2402f966dd99992a9fb85504ff917d5c0cfdaf2a 100644 GIT binary patch delta 4637 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