versionManager = $versionManager; $this->registerHooks(); } /** * Register WordPress hooks */ private function registerHooks(): void { // Add versions meta box to licensed products add_action('add_meta_boxes', [$this, 'addVersionsMetaBox']); // Handle AJAX actions for version management add_action('wp_ajax_wc_licensed_product_add_version', [$this, 'ajaxAddVersion']); add_action('wp_ajax_wc_licensed_product_delete_version', [$this, 'ajaxDeleteVersion']); add_action('wp_ajax_wc_licensed_product_toggle_version', [$this, 'ajaxToggleVersion']); // Enqueue scripts for product edit page add_action('admin_enqueue_scripts', [$this, 'enqueueScripts']); } /** * Add versions meta box to product edit page */ public function addVersionsMetaBox(): void { global $post; // Only add meta box for licensed products or new products if ($post && $post->post_type === 'product') { $product = wc_get_product($post->ID); // Show for licensed products or new products (where type might be selected later) if (!$product || $product->is_type('licensed') || $post->post_status === 'auto-draft') { add_meta_box( 'wc_licensed_product_versions', __('Product Versions', 'wc-licensed-product'), [$this, 'renderVersionsMetaBox'], 'product', 'normal', 'high' ); } } } /** * Render versions meta box */ public function renderVersionsMetaBox(\WP_Post $post): void { $versions = $this->versionManager->getVersionsByProduct($post->ID); wp_nonce_field('wc_licensed_product_versions', 'wc_licensed_product_versions_nonce'); ?>


getVersion()); ?> getEffectiveDownloadUrl(); $filename = $version->getDownloadFilename(); if ($effectiveUrl): ?> getAttachmentId()): ?> getFileHash()): ?> getFileHash(), 0, 12)); ?>... getReleaseNotes() ? wp_trim_words($version->getReleaseNotes(), 10) : '—'); ?> isActive() ? esc_html__('Active', 'wc-licensed-product') : esc_html__('Inactive', 'wc-licensed-product'); ?> getReleasedAt()->format(get_option('date_format'))); ?>
post_type !== 'product') { return; } // Enqueue WordPress media uploader wp_enqueue_media(); wp_enqueue_script( 'wc-licensed-product-versions', WC_LICENSED_PRODUCT_PLUGIN_URL . 'assets/js/versions.js', ['jquery'], WC_LICENSED_PRODUCT_VERSION, true ); wp_localize_script('wc-licensed-product-versions', 'wcLicensedProductVersions', [ 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wc_licensed_product_versions'), 'strings' => [ 'confirmDelete' => __('Are you sure you want to delete this version?', 'wc-licensed-product'), 'versionRequired' => __('Please enter a version number.', 'wc-licensed-product'), 'versionInvalid' => __('Please enter a valid version number (e.g., 1.0.0).', 'wc-licensed-product'), 'error' => __('An error occurred. Please try again.', 'wc-licensed-product'), 'selectFile' => __('Select Download File', 'wc-licensed-product'), 'useThisFile' => __('Use this file', 'wc-licensed-product'), 'invalidChecksumFile' => __('Invalid checksum file format. File must contain a 64-character SHA256 hash.', 'wc-licensed-product'), 'checksumReadError' => __('Failed to read checksum file.', 'wc-licensed-product'), ], ]); wp_enqueue_style( 'wc-licensed-product-admin', WC_LICENSED_PRODUCT_PLUGIN_URL . 'assets/css/admin.css', [], WC_LICENSED_PRODUCT_VERSION ); } /** * AJAX handler for adding a version */ public function ajaxAddVersion(): void { check_ajax_referer('wc_licensed_product_versions', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_send_json_error(['message' => __('Permission denied.', 'wc-licensed-product')]); } $productId = absint($_POST['product_id'] ?? 0); $version = sanitize_text_field($_POST['version'] ?? ''); $releaseNotes = sanitize_textarea_field($_POST['release_notes'] ?? ''); $attachmentId = absint($_POST['attachment_id'] ?? 0); $fileHash = sanitize_text_field($_POST['file_hash'] ?? ''); if (!$productId || !$version) { wp_send_json_error(['message' => __('Product ID and version are required.', 'wc-licensed-product')]); } // Validate version format if (!preg_match('/^\d+\.\d+\.\d+$/', $version)) { wp_send_json_error(['message' => __('Invalid version format. Use semantic versioning (e.g., 1.0.0).', 'wc-licensed-product')]); } // Check if version already exists if ($this->versionManager->versionExists($productId, $version)) { wp_send_json_error(['message' => __('This version already exists.', 'wc-licensed-product')]); } // Verify product exists and is of type licensed $product = wc_get_product($productId); if (!$product) { wp_send_json_error(['message' => __('Product not found.', 'wc-licensed-product')]); } if (!$product->is_type('licensed')) { wp_send_json_error(['message' => __('This product is not a licensed product.', 'wc-licensed-product')]); } try { $newVersion = $this->versionManager->createVersion( $productId, $version, $releaseNotes ?: null, $attachmentId ?: null, $fileHash ?: null ); } catch (\InvalidArgumentException $e) { wp_send_json_error(['message' => $e->getMessage()]); } if (!$newVersion) { global $wpdb; $errorMessage = __('Failed to create version.', 'wc-licensed-product'); if (!empty($wpdb->last_error)) { error_log('WC Licensed Product: DB error - ' . $wpdb->last_error); } wp_send_json_error(['message' => $errorMessage]); } wp_send_json_success([ 'message' => __('Version added successfully.', 'wc-licensed-product'), 'version' => $newVersion->toArray(), 'html' => $this->getVersionRowHtml($newVersion), ]); } /** * AJAX handler for deleting a version */ public function ajaxDeleteVersion(): void { check_ajax_referer('wc_licensed_product_versions', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_send_json_error(['message' => __('Permission denied.', 'wc-licensed-product')]); } $versionId = absint($_POST['version_id'] ?? 0); if (!$versionId) { wp_send_json_error(['message' => __('Version ID is required.', 'wc-licensed-product')]); } $result = $this->versionManager->deleteVersion($versionId); if (!$result) { wp_send_json_error(['message' => __('Failed to delete version.', 'wc-licensed-product')]); } wp_send_json_success(['message' => __('Version deleted successfully.', 'wc-licensed-product')]); } /** * AJAX handler for toggling version status */ public function ajaxToggleVersion(): void { check_ajax_referer('wc_licensed_product_versions', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_send_json_error(['message' => __('Permission denied.', 'wc-licensed-product')]); } $versionId = absint($_POST['version_id'] ?? 0); $currentlyActive = (bool) ($_POST['currently_active'] ?? false); if (!$versionId) { wp_send_json_error(['message' => __('Version ID is required.', 'wc-licensed-product')]); } $result = $this->versionManager->updateVersion($versionId, null, !$currentlyActive, null); if (!$result) { wp_send_json_error(['message' => __('Failed to update version.', 'wc-licensed-product')]); } wp_send_json_success([ 'message' => __('Version updated successfully.', 'wc-licensed-product'), 'isActive' => !$currentlyActive, ]); } /** * Get HTML for a version table row */ private function getVersionRowHtml(\Jeremias\WcLicensedProduct\Product\ProductVersion $version): string { ob_start(); ?> getVersion()); ?> getEffectiveDownloadUrl(); $filename = $version->getDownloadFilename(); if ($effectiveUrl): ?> getAttachmentId()): ?> getFileHash()): ?> getFileHash(), 0, 12)); ?>... getReleaseNotes() ? wp_trim_words($version->getReleaseNotes(), 10) : '—'); ?> isActive() ? esc_html__('Active', 'wc-licensed-product') : esc_html__('Inactive', 'wc-licensed-product'); ?> getReleasedAt()->format(get_option('date_format'))); ?>