Explorar el Código

add section Delete category products v.1.3

Dalibor Votruba hace 9 meses
padre
commit
752b4f45c2

+ 173 - 0
studiou-wc-product-cat-manage/CLAUDE.md

@@ -0,0 +1,173 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+**QDR - Studiou WC Export/Import Product Categories** is a WordPress plugin that extends WooCommerce to provide batch export/import functionality for product categories and category manipulation tools.
+
+**Current Version:** 1.3.0
+
+## Requirements
+
+- WordPress 6.8.1+
+- PHP 8.2+
+- WooCommerce 9.8+
+- WooCommerce Protected Categories 2.7+ (optional, for visibility features)
+
+## Plugin Architecture
+
+### Core Files
+
+- **studiou-wc-product-cat-manage.php** - Main plugin file containing:
+  - Plugin initialization and dependency loading
+  - Admin menu registration under Products menu
+  - Asset enqueueing for admin pages
+  - WooCommerce HPOS compatibility declaration
+  - Text domain loading for translations
+
+### Class Structure (includes/)
+
+The plugin follows a modular class-based architecture:
+
+- **class-studiou-wc-product-cat-manage-db.php** - Database operations layer
+  - Handles all database queries for product categories
+  - Manages category hierarchy (parent/child relationships)
+  - Provides category formatting and data retrieval
+  - Interfaces with WooCommerce taxonomy and WooCommerce Protected Categories plugin
+
+- **class-studiou-wc-product-cat-manage-export.php** - Export functionality
+  - Generates CSV files from product categories
+  - Includes all category properties (name, slug, parent, description, display-type, visibility, protected-passwords)
+  - Handles UTF-8 encoding and proper CSV formatting
+
+- **class-studiou-wc-product-cat-manage-import.php** - Import functionality
+  - Processes CSV files with operations: 'A' (add), 'U' (update), 'D' (delete)
+  - Validates data and handles errors
+  - Creates/updates/deletes categories based on operation column
+  - Manages WooCommerce Protected Categories settings
+
+- **class-studiou-wc-product-cat-manage-manipulator.php** - Category manipulation operations
+  - Move products between categories (removes from source, adds to target)
+  - Batch description updates for multiple categories
+  - Delete products in category (permanently deletes products and variants)
+  - Uses direct database queries for reliable product detection
+  - Batch processing for delete operations (10 products per batch)
+
+### Views (views/)
+
+- **import.php** - Import page UI
+- **export.php** - Export page UI
+- **manipulations.php** - Category manipulations page UI with three operations:
+  - Move products between categories (two dropdowns + "Do" button)
+  - Batch description between categories (checkbox list + textarea + "Do" button)
+  - Delete products in category (checkbox list + "Delete" button + progress bar)
+
+### Assets
+
+- **assets/css/admin.css** - Admin styling including:
+  - Progress bar styling
+  - Warning message styling (yellow background for destructive operations)
+  - Danger button styling (red for delete operations)
+- **assets/js/admin.js** - Admin JavaScript including:
+  - AJAX handlers for all operations
+  - Batch processing logic for delete operations
+  - Real-time progress bar updates
+  - Confirmation dialogs for destructive operations
+
+### Translations
+
+- **languages/** - Contains .pot, .po, and .mo files
+- Text domain: `studiou-wc-product-cat-manage`
+- All user-facing strings must be wrapped in translation functions: `__()`, `_e()`, `sprintf()`
+
+## CSV Format
+
+CSV files use comma separators, double-quote string isolation, UTF-8 encoding:
+
+| Column | Description |
+|--------|-------------|
+| name | Category name (required, used as identifier) |
+| url-name | URL slug |
+| parent-name | Parent category name (empty if root) |
+| description | Category description |
+| display-type | Values: 'default', 'products', 'subcategories', 'both' |
+| visibility | Values: 'public', 'protected' (WooCommerce Protected Categories) |
+| protected-passwords | Pipe-separated passwords (|) if visibility='protected' |
+| operation | 'A' (add), 'U' (update), 'D' (delete) |
+
+## Development Guidelines
+
+### When Modifying Code
+
+1. **Translations**: All new user-facing strings must use `__()` or `_e()` with text domain `'studiou-wc-product-cat-manage'`
+2. **Security**: Always validate and sanitize user input, use nonces for form submissions
+3. **WooCommerce Integration**: Use WooCommerce taxonomy 'product_cat' for all category operations
+4. **Error Handling**: Provide clear error messages and validation feedback
+5. **Compatibility**: Maintain HPOS (High-Performance Order Storage) compatibility declaration
+6. **Direct Exits**: Always include `if (!defined('WPINC')) { die; }` at top of PHP files
+7. **Batch Processing**: For operations on large datasets, use batch processing (10 items per batch recommended)
+8. **Destructive Operations**: Always include confirmation dialogs and warning messages for operations that cannot be undone
+9. **Progress Tracking**: For long-running operations, implement real-time progress bars
+
+### Plugin Constants
+
+```php
+STUDIOU_WCPCM_VERSION        // Plugin version
+STUDIOU_WCPCM_PLUGIN_DIR     // Plugin directory path
+STUDIOU_WCPCM_PLUGIN_URL     // Plugin URL
+STUDIOU_WCPCM_PLUGIN_BASENAME // Plugin basename
+```
+
+### Admin Menu Structure
+
+All pages are added under **Products** menu:
+- Import Product Categories (slug: `studiou-import-product-categories`)
+- Export Product Categories (slug: `studiou-export-product-categories`)
+- Category Manipulations (slug: `studiou-category-manipulations`)
+  - Move products between categories
+  - Batch description between categories
+  - Delete products in category
+
+### Key Integration Points
+
+- **WooCommerce Protected Categories**: Plugin extends category visibility with 'public'/'protected' settings and password protection
+- **Product Assignment**: When moving products, the plugin modifies product-category term relationships while preserving other category assignments
+- **Hierarchy Management**: Categories maintain parent-child relationships via `parent-name` field
+- **Product Deletion**: Uses WooCommerce `wc_get_product()` and `product->delete(true)` for proper product deletion
+  - Automatically handles variable products by deleting all variations first
+  - Force delete (true parameter) ensures permanent removal bypassing trash
+  - Processes in batches to prevent PHP timeouts and memory issues
+
+## Testing Notes
+
+- Test import/export with categories that have special characters, quotes, commas
+- Test category hierarchy (parent/child relationships)
+- Test moving products between categories when products have multiple category assignments
+- Test batch description updates with multiline text
+- Test delete products operation with:
+  - Simple products
+  - Variable products with multiple variations
+  - Products in multiple categories
+  - Large numbers of products (test batch processing)
+  - Edge cases: empty categories, categories with only variations
+- Verify translations are properly loaded
+- Verify progress bar updates correctly during delete operations
+- Verify confirmation dialogs appear for destructive operations
+
+## AJAX Handlers
+
+The plugin registers the following AJAX actions:
+
+1. **studiou_wcpcm_import** - Handles CSV import
+2. **studiou_wcpcm_export** - Generates CSV export
+3. **studiou_wcpcm_move_products** - Moves products between categories
+4. **studiou_wcpcm_batch_description** - Updates descriptions for multiple categories
+5. **studiou_wcpcm_delete_products** - Initial request to get product IDs for deletion
+6. **studiou_wcpcm_delete_products_batch** - Processes product deletion in batches
+
+All AJAX handlers include:
+- Nonce verification (`studiou-wcpcm-nonce`)
+- Permission checks (`manage_woocommerce` capability)
+- Output buffer cleaning to prevent JSON corruption
+- Comprehensive error logging when WP_DEBUG is enabled

+ 91 - 0
studiou-wc-product-cat-manage/assets/css/admin.css

@@ -176,4 +176,95 @@
 .studiou-wcpcm-card ul li {
     margin-bottom: 8px;
     line-height: 1.6;
+}
+
+/* Delete products warning message */
+.studiou-wcpcm-warning-message {
+    background-color: #fff3cd;
+    border: 2px solid #ffc107;
+    border-radius: 4px;
+    padding: 15px;
+    margin: 20px 0;
+    color: #856404;
+}
+
+.studiou-wcpcm-warning-message p {
+    margin: 0;
+}
+
+/* Delete button danger styling */
+.button-danger {
+    background-color: #dc3545 !important;
+    border-color: #dc3545 !important;
+    color: #fff !important;
+}
+
+.button-danger:hover {
+    background-color: #c82333 !important;
+    border-color: #bd2130 !important;
+}
+
+/* Progress bar styling */
+.studiou-wcpcm-delete-progress,
+.studiou-wcpcm-delete-results {
+    margin-top: 30px;
+    padding: 20px;
+    background: #f9f9f9;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+}
+
+.studiou-wcpcm-delete-progress h3,
+.studiou-wcpcm-delete-results h3 {
+    margin-top: 0;
+    margin-bottom: 15px;
+}
+
+.studiou-wcpcm-progress-bar-container {
+    width: 100%;
+    height: 30px;
+    background-color: #e9ecef;
+    border-radius: 4px;
+    overflow: hidden;
+    margin-bottom: 10px;
+    position: relative;
+}
+
+.studiou-wcpcm-progress-bar {
+    height: 100%;
+    background-color: #28a745;
+    transition: width 0.3s ease;
+    position: relative;
+}
+
+.studiou-wcpcm-progress-bar::after {
+    content: attr(data-progress);
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    color: #fff;
+    font-weight: bold;
+    font-size: 14px;
+}
+
+.studiou-wcpcm-progress-text {
+    text-align: center;
+    font-weight: 500;
+    color: #333;
+    margin-top: 10px;
+}
+
+.studiou-wcpcm-delete-results ul {
+    list-style: none;
+    padding-left: 0;
+}
+
+.studiou-wcpcm-delete-results ul li {
+    padding: 8px 0;
+    border-bottom: 1px solid #e0e0e0;
+}
+
+.studiou-wcpcm-delete-results ul li:last-child {
+    border-bottom: none;
 }

+ 258 - 5
studiou-wc-product-cat-manage/assets/js/admin.js

@@ -35,7 +35,13 @@
             console.log('STUDIOU WC: Batch description form found');
             initBatchDescriptionForm();
         }
-        
+
+        // Delete products form handling
+        if ($('#studiou-wcpcm-delete-products-form').length) {
+            console.log('STUDIOU WC: Delete products form found');
+            initDeleteProductsForm();
+        }
+
         // Sample CSV download handler
         if ($('#studiou-wcpcm-download-sample').length) {
             initSampleDownload();
@@ -359,22 +365,268 @@
         });
     }
     
+    /**
+     * Initialize delete products form
+     */
+    function initDeleteProductsForm() {
+        console.log('STUDIOU WC: Initializing delete products form handlers');
+        console.log('STUDIOU WC: Form element:', $('#studiou-wcpcm-delete-products-form'));
+        console.log('STUDIOU WC: Check all button:', $('#studiou-wcpcm-delete-check-all'));
+        console.log('STUDIOU WC: Delete button:', $('#studiou-wcpcm-delete-products-button'));
+
+        // Check all button handler
+        $('#studiou-wcpcm-delete-check-all').on('click', function(e) {
+            e.preventDefault();
+            console.log('STUDIOU WC: Delete check all button clicked');
+            $('#delete_categories_list input[type="checkbox"]').prop('checked', true);
+        });
+
+        // Uncheck all button handler
+        $('#studiou-wcpcm-delete-uncheck-all').on('click', function(e) {
+            e.preventDefault();
+            console.log('STUDIOU WC: Delete uncheck all button clicked');
+            $('#delete_categories_list input[type="checkbox"]').prop('checked', false);
+        });
+
+        // Form submit handler
+        $('#studiou-wcpcm-delete-products-form').on('submit', function(e) {
+            e.preventDefault();
+
+            console.log('STUDIOU WC: Delete products form submitted');
+
+            // Get selected categories
+            var selectedCategories = [];
+            $('#delete_categories_list input[type="checkbox"]:checked').each(function() {
+                selectedCategories.push($(this).val());
+            });
+
+            console.log('STUDIOU WC: Selected categories:', selectedCategories);
+
+            // Validate input
+            if (selectedCategories.length === 0) {
+                console.log('STUDIOU WC: No categories selected');
+                showNotice('error', studiouWcpcm.i18n.selectAtLeastOneCategory || 'Please select at least one category');
+                return;
+            }
+
+            // Confirm deletion
+            if (!confirm(studiouWcpcm.i18n.confirmDelete || 'Are you sure you want to permanently delete ALL products in the selected categories? This action cannot be undone!')) {
+                console.log('STUDIOU WC: User cancelled deletion');
+                return;
+            }
+
+            // Show spinner
+            var $submitBtn = $(this).find('#studiou-wcpcm-delete-products-button');
+            var $spinner = $(this).find('.spinner');
+
+            $submitBtn.prop('disabled', true);
+            $spinner.css('visibility', 'visible');
+
+            // Clear previous notices
+            $('.studiou-wcpcm-notice-area').empty();
+
+            // Hide results, show progress
+            $('.studiou-wcpcm-delete-results').hide();
+            $('.studiou-wcpcm-delete-progress').show();
+            $('#studiou-wcpcm-delete-progress-text').text('Initializing...');
+            updateProgressBar(0);
+
+            // Debug: Log AJAX request details
+            console.log('STUDIOU WC: Sending delete products AJAX request to:', studiouWcpcm.ajaxUrl);
+            console.log('STUDIOU WC: Nonce:', studiouWcpcm.nonce);
+
+            // Send AJAX request to get product IDs
+            $.ajax({
+                url: studiouWcpcm.ajaxUrl,
+                type: 'POST',
+                data: {
+                    action: 'studiou_wcpcm_delete_products',
+                    delete_categories: selectedCategories,
+                    nonce: studiouWcpcm.nonce
+                },
+                success: function(response) {
+                    console.log('STUDIOU WC: Delete products AJAX response:', response);
+
+                    if (response.success) {
+                        var productIds = response.data.product_ids;
+                        var totalCount = response.data.total_count;
+
+                        console.log('STUDIOU WC: Total products to delete:', totalCount);
+
+                        if (totalCount === 0) {
+                            showNotice('success', studiouWcpcm.i18n.noProductsFound || 'No products found in selected categories');
+                            $('.studiou-wcpcm-delete-progress').hide();
+                            $submitBtn.prop('disabled', false);
+                            $spinner.css('visibility', 'hidden');
+                            return;
+                        }
+
+                        // Process products in batches
+                        processDeletionBatches(productIds, totalCount, $submitBtn, $spinner);
+
+                    } else {
+                        showNotice('error', response.data.message);
+                        $('.studiou-wcpcm-delete-progress').hide();
+                        $submitBtn.prop('disabled', false);
+                        $spinner.css('visibility', 'hidden');
+                    }
+                },
+                error: function(xhr, status, error) {
+                    console.error('STUDIOU WC: Delete products AJAX error:', status, error);
+                    console.error('STUDIOU WC: Response text:', xhr.responseText);
+                    showNotice('error', (studiouWcpcm.i18n.deleteError || 'Delete products operation error') + ': ' + error);
+                    $('.studiou-wcpcm-delete-progress').hide();
+                    $submitBtn.prop('disabled', false);
+                    $spinner.css('visibility', 'hidden');
+                }
+            });
+        });
+    }
+
+    /**
+     * Process deletion in batches
+     */
+    function processDeletionBatches(productIds, totalCount, $submitBtn, $spinner) {
+        var batchSize = 10; // Process 10 products at a time
+        var batches = [];
+        var totalDeleted = 0;
+        var totalFailed = 0;
+        var failedProducts = [];
+
+        // Split products into batches
+        for (var i = 0; i < productIds.length; i += batchSize) {
+            batches.push(productIds.slice(i, i + batchSize));
+        }
+
+        console.log('STUDIOU WC: Processing ' + batches.length + ' batches of products');
+
+        var currentBatch = 0;
+
+        function processBatch() {
+            if (currentBatch >= batches.length) {
+                // All batches completed
+                console.log('STUDIOU WC: All batches completed');
+                onDeletionComplete(totalDeleted, totalFailed, failedProducts, $submitBtn, $spinner);
+                return;
+            }
+
+            var batch = batches[currentBatch];
+            var progress = Math.round((currentBatch / batches.length) * 100);
+
+            $('#studiou-wcpcm-delete-progress-text').text(
+                'Deleting products... ' + (currentBatch * batchSize) + ' of ' + totalCount + ' processed'
+            );
+            updateProgressBar(progress);
+
+            // Send batch delete request
+            $.ajax({
+                url: studiouWcpcm.ajaxUrl,
+                type: 'POST',
+                data: {
+                    action: 'studiou_wcpcm_delete_products_batch',
+                    product_ids: batch,
+                    nonce: studiouWcpcm.nonce
+                },
+                success: function(response) {
+                    if (response.success) {
+                        totalDeleted += response.data.deleted_count;
+                        totalFailed += response.data.failed_count;
+                        failedProducts = failedProducts.concat(response.data.failed_products);
+
+                        console.log('STUDIOU WC: Batch ' + currentBatch + ' completed - Deleted: ' +
+                            response.data.deleted_count + ', Failed: ' + response.data.failed_count);
+
+                        // Process next batch
+                        currentBatch++;
+                        processBatch();
+                    } else {
+                        showNotice('error', response.data.message);
+                        $('.studiou-wcpcm-delete-progress').hide();
+                        $submitBtn.prop('disabled', false);
+                        $spinner.css('visibility', 'hidden');
+                    }
+                },
+                error: function(xhr, status, error) {
+                    console.error('STUDIOU WC: Batch delete AJAX error:', status, error);
+                    showNotice('error', 'Error deleting batch: ' + error);
+                    $('.studiou-wcpcm-delete-progress').hide();
+                    $submitBtn.prop('disabled', false);
+                    $spinner.css('visibility', 'hidden');
+                }
+            });
+        }
+
+        // Start processing batches
+        processBatch();
+    }
+
+    /**
+     * Handle deletion completion
+     */
+    function onDeletionComplete(totalDeleted, totalFailed, failedProducts, $submitBtn, $spinner) {
+        // Update progress bar to 100%
+        updateProgressBar(100);
+        $('#studiou-wcpcm-delete-progress-text').text('Deletion complete!');
+
+        // Show results
+        var message = '<p><strong>Deletion Summary:</strong></p>';
+        message += '<ul>';
+        message += '<li>Total products deleted: ' + totalDeleted + '</li>';
+        if (totalFailed > 0) {
+            message += '<li>Failed to delete: ' + totalFailed + '</li>';
+            if (failedProducts.length > 0) {
+                message += '<li>Failed product IDs: ' + failedProducts.join(', ') + '</li>';
+            }
+        }
+        message += '</ul>';
+
+        $('#studiou-wcpcm-delete-message').html(message);
+        $('.studiou-wcpcm-delete-results').show();
+
+        // Hide progress after a delay
+        setTimeout(function() {
+            $('.studiou-wcpcm-delete-progress').fadeOut();
+        }, 2000);
+
+        if (totalDeleted > 0) {
+            showNotice('success', (studiouWcpcm.i18n.deleteSuccess || 'Successfully deleted') + ' ' + totalDeleted + ' products');
+
+            // Reload page after 3 seconds
+            setTimeout(function() {
+                location.reload();
+            }, 3000);
+        } else {
+            showNotice('error', studiouWcpcm.i18n.deleteError || 'Failed to delete any products');
+        }
+
+        $submitBtn.prop('disabled', false);
+        $spinner.css('visibility', 'hidden');
+    }
+
+    /**
+     * Update progress bar
+     */
+    function updateProgressBar(percentage) {
+        $('#studiou-wcpcm-delete-progress-bar').css('width', percentage + '%');
+        $('#studiou-wcpcm-delete-progress-bar').attr('data-progress', percentage + '%');
+    }
+
     /**
      * Show notice message
-     * 
+     *
      * @param {string} type Notice type (success, error)
      * @param {string} message Notice message
      */
     function showNotice(type, message) {
         var $notice = $('<div class="studiou-wcpcm-notice studiou-wcpcm-notice-' + type + '"></div>').text(message);
         $('.studiou-wcpcm-notice-area').append($notice);
-        
+
         // Scroll to notice
         $('html, body').animate({
             scrollTop: $('.studiou-wcpcm-notice-area').offset().top - 50
         }, 500);
     }
-    
+
     // Additional debug: Check if elements exist after DOM load
     $(window).on('load', function() {
         console.log('STUDIOU WC: Window loaded');
@@ -384,6 +636,7 @@
         console.log('STUDIOU WC: Target select exists:', $('#target_category').length > 0);
         console.log('STUDIOU WC: Batch description form exists:', $('#studiou-wcpcm-batch-description-form').length > 0);
         console.log('STUDIOU WC: Batch description button exists:', $('#studiou-wcpcm-batch-description-button').length > 0);
+        console.log('STUDIOU WC: Delete products form exists:', $('#studiou-wcpcm-delete-products-form').length > 0);
     });
-    
+
 })(jQuery);

+ 315 - 7
studiou-wc-product-cat-manage/includes/class-studiou-wc-product-cat-manage-manipulator.php

@@ -34,13 +34,15 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
         // Register AJAX handlers
         add_action('wp_ajax_studiou_wcpcm_move_products', array($this, 'handle_move_products_ajax'));
         add_action('wp_ajax_studiou_wcpcm_batch_description', array($this, 'handle_batch_description_ajax'));
-        
+        add_action('wp_ajax_studiou_wcpcm_delete_products', array($this, 'handle_delete_products_ajax'));
+        add_action('wp_ajax_studiou_wcpcm_delete_products_batch', array($this, 'handle_delete_products_batch_ajax'));
+
         // Add a test AJAX handler to verify AJAX is working
         add_action('wp_ajax_studiou_wcpcm_test', array($this, 'handle_test_ajax'));
-        
+
         // Debug: Log that the handler is being registered
         if (defined('WP_DEBUG') && WP_DEBUG) {
-            error_log('STUDIOU WC: AJAX handlers registered - move_products and batch_description');
+            error_log('STUDIOU WC: AJAX handlers registered - move_products, batch_description, and delete_products');
         }
     }
     
@@ -527,14 +529,14 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
             'orderby' => 'name',
             'hide_empty' => false,
         );
-        
+
         $categories = get_terms($args);
         $categories_with_counts = array();
-        
+
         if (!is_wp_error($categories)) {
             foreach ($categories as $category) {
                 $product_count = $this->get_category_product_count($category->term_id);
-                
+
                 $categories_with_counts[] = array(
                     'id' => $category->term_id,
                     'name' => $category->name,
@@ -543,7 +545,313 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
                 );
             }
         }
-        
+
         return $categories_with_counts;
     }
+
+    /**
+     * Handle AJAX delete products request (initial request to get product IDs)
+     */
+    public function handle_delete_products_ajax() {
+        // Clean all output buffers and prevent any output
+        while (ob_get_level()) {
+            ob_end_clean();
+        }
+
+        // Start a new output buffer to catch any unwanted output
+        ob_start();
+
+        // Suppress PHP errors/notices during AJAX to prevent JSON corruption
+        $original_error_reporting = error_reporting();
+        error_reporting(0);
+
+        // Enable error logging only
+        if (defined('WP_DEBUG') && WP_DEBUG) {
+            error_log('STUDIOU WC: Delete products AJAX handler called');
+            error_log('STUDIOU WC: POST data: ' . print_r($_POST, true));
+        }
+
+        try {
+            // Check nonce
+            if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'studiou-wcpcm-nonce')) {
+                wp_send_json_error(array(
+                    'message' => __('Security check failed', 'studiou-wc-product-cat-manage')
+                ));
+            }
+
+            // Check permissions
+            if (!current_user_can('manage_woocommerce')) {
+                wp_send_json_error(array(
+                    'message' => __('You do not have sufficient permissions', 'studiou-wc-product-cat-manage')
+                ));
+            }
+
+            // Validate input
+            if (!isset($_POST['delete_categories'])) {
+                wp_send_json_error(array(
+                    'message' => __('Missing required parameters', 'studiou-wc-product-cat-manage')
+                ));
+            }
+
+            $category_ids = array_map('intval', $_POST['delete_categories']);
+
+            // Debug logging
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                error_log('STUDIOU WC: Delete products operation started - Categories: ' . implode(', ', $category_ids));
+            }
+
+            // Validate categories
+            if (empty($category_ids)) {
+                wp_send_json_error(array(
+                    'message' => __('Please select at least one category', 'studiou-wc-product-cat-manage')
+                ));
+            }
+
+            // Get all products from selected categories
+            $product_ids = $this->get_products_from_categories($category_ids);
+
+            // Debug logging
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                error_log('STUDIOU WC: Found ' . count($product_ids) . ' products to delete');
+            }
+
+            // Clean any output that might have been generated
+            ob_clean();
+
+            // Return product IDs for batch processing
+            wp_send_json_success(array(
+                'product_ids' => $product_ids,
+                'total_count' => count($product_ids),
+                'message' => sprintf(
+                    __('Found %d products to delete', 'studiou-wc-product-cat-manage'),
+                    count($product_ids)
+                )
+            ));
+
+        } catch (Exception $e) {
+            // Debug logging
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                error_log('STUDIOU WC: Delete products operation failed - ' . $e->getMessage());
+                error_log('STUDIOU WC: Exception trace: ' . $e->getTraceAsString());
+            }
+
+            // Clean any output that might have been generated
+            ob_clean();
+
+            wp_send_json_error(array(
+                'message' => sprintf(
+                    __('Error during delete operation: %s', 'studiou-wc-product-cat-manage'),
+                    $e->getMessage()
+                )
+            ));
+        } finally {
+            // Restore original error reporting
+            error_reporting($original_error_reporting);
+
+            // End the output buffer
+            ob_end_clean();
+        }
+    }
+
+    /**
+     * Handle AJAX delete products batch request (processes products in batches)
+     */
+    public function handle_delete_products_batch_ajax() {
+        // Clean all output buffers and prevent any output
+        while (ob_get_level()) {
+            ob_end_clean();
+        }
+
+        // Start a new output buffer to catch any unwanted output
+        ob_start();
+
+        // Suppress PHP errors/notices during AJAX to prevent JSON corruption
+        $original_error_reporting = error_reporting();
+        error_reporting(0);
+
+        // Enable error logging only
+        if (defined('WP_DEBUG') && WP_DEBUG) {
+            error_log('STUDIOU WC: Delete products batch AJAX handler called');
+            error_log('STUDIOU WC: POST data: ' . print_r($_POST, true));
+        }
+
+        try {
+            // Check nonce
+            if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'studiou-wcpcm-nonce')) {
+                wp_send_json_error(array(
+                    'message' => __('Security check failed', 'studiou-wc-product-cat-manage')
+                ));
+            }
+
+            // Check permissions
+            if (!current_user_can('manage_woocommerce')) {
+                wp_send_json_error(array(
+                    'message' => __('You do not have sufficient permissions', 'studiou-wc-product-cat-manage')
+                ));
+            }
+
+            // Validate input
+            if (!isset($_POST['product_ids'])) {
+                wp_send_json_error(array(
+                    'message' => __('Missing required parameters', 'studiou-wc-product-cat-manage')
+                ));
+            }
+
+            $product_ids = array_map('intval', $_POST['product_ids']);
+
+            // Debug logging
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                error_log('STUDIOU WC: Processing batch of ' . count($product_ids) . ' products for deletion');
+            }
+
+            // Delete products in this batch
+            $result = $this->delete_products_batch($product_ids);
+
+            // Debug logging
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                error_log('STUDIOU WC: Batch deletion completed - Deleted: ' . $result['deleted_count'] . ', Failed: ' . $result['failed_count']);
+            }
+
+            // Clean any output that might have been generated
+            ob_clean();
+
+            // Return success response
+            wp_send_json_success(array(
+                'deleted_count' => $result['deleted_count'],
+                'failed_count' => $result['failed_count'],
+                'failed_products' => $result['failed_products']
+            ));
+
+        } catch (Exception $e) {
+            // Debug logging
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                error_log('STUDIOU WC: Delete products batch operation failed - ' . $e->getMessage());
+                error_log('STUDIOU WC: Exception trace: ' . $e->getTraceAsString());
+            }
+
+            // Clean any output that might have been generated
+            ob_clean();
+
+            wp_send_json_error(array(
+                'message' => sprintf(
+                    __('Error during delete batch operation: %s', 'studiou-wc-product-cat-manage'),
+                    $e->getMessage()
+                )
+            ));
+        } finally {
+            // Restore original error reporting
+            error_reporting($original_error_reporting);
+
+            // End the output buffer
+            ob_end_clean();
+        }
+    }
+
+    /**
+     * Get all products from multiple categories
+     *
+     * @param array $category_ids Array of category IDs
+     * @return array Array of unique product IDs (including parent products with variants)
+     */
+    private function get_products_from_categories($category_ids) {
+        global $wpdb;
+
+        if (empty($category_ids)) {
+            return array();
+        }
+
+        $placeholders = implode(',', array_fill(0, count($category_ids), '%d'));
+
+        // Get all products from selected categories (both simple and parent products)
+        $sql = "
+            SELECT DISTINCT p.ID
+            FROM {$wpdb->posts} p
+            INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
+            INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
+            WHERE tt.term_id IN ($placeholders)
+            AND tt.taxonomy = 'product_cat'
+            AND p.post_type = 'product'
+            AND p.post_status IN ('publish', 'private', 'draft', 'pending', 'trash')
+        ";
+
+        $products = $wpdb->get_col($wpdb->prepare($sql, $category_ids));
+
+        if (defined('WP_DEBUG') && WP_DEBUG) {
+            error_log('STUDIOU WC: Found ' . count($products) . ' products in categories ' . implode(', ', $category_ids));
+        }
+
+        return $products ? array_map('intval', $products) : array();
+    }
+
+    /**
+     * Delete a batch of products and their variants
+     *
+     * @param array $product_ids Array of product IDs to delete
+     * @return array Result with counts
+     */
+    private function delete_products_batch($product_ids) {
+        $deleted_count = 0;
+        $failed_count = 0;
+        $failed_products = array();
+
+        foreach ($product_ids as $product_id) {
+            try {
+                // Get the product object
+                $product = wc_get_product($product_id);
+
+                if (!$product) {
+                    $failed_count++;
+                    $failed_products[] = $product_id;
+                    if (defined('WP_DEBUG') && WP_DEBUG) {
+                        error_log('STUDIOU WC: Product ' . $product_id . ' not found');
+                    }
+                    continue;
+                }
+
+                // If it's a variable product, delete all variations first
+                if ($product->is_type('variable')) {
+                    $variations = $product->get_children();
+                    if (defined('WP_DEBUG') && WP_DEBUG) {
+                        error_log('STUDIOU WC: Deleting ' . count($variations) . ' variations for product ' . $product_id);
+                    }
+
+                    foreach ($variations as $variation_id) {
+                        $variation = wc_get_product($variation_id);
+                        if ($variation) {
+                            $variation->delete(true); // true = force delete permanently
+                        }
+                    }
+                }
+
+                // Delete the product permanently
+                $result = $product->delete(true); // true = force delete permanently
+
+                if ($result) {
+                    $deleted_count++;
+                    if (defined('WP_DEBUG') && WP_DEBUG) {
+                        error_log('STUDIOU WC: Successfully deleted product ' . $product_id);
+                    }
+                } else {
+                    $failed_count++;
+                    $failed_products[] = $product_id;
+                    if (defined('WP_DEBUG') && WP_DEBUG) {
+                        error_log('STUDIOU WC: Failed to delete product ' . $product_id);
+                    }
+                }
+
+            } catch (Exception $e) {
+                $failed_count++;
+                $failed_products[] = $product_id;
+                if (defined('WP_DEBUG') && WP_DEBUG) {
+                    error_log('STUDIOU WC: Exception deleting product ' . $product_id . ': ' . $e->getMessage());
+                }
+            }
+        }
+
+        return array(
+            'deleted_count' => $deleted_count,
+            'failed_count' => $failed_count,
+            'failed_products' => $failed_products
+        );
+    }
 }

+ 39 - 3
studiou-wc-product-cat-manage/readme.md

@@ -1,6 +1,6 @@
 # QDR - Studiou WC Export/Import Product Categories
 
-**Version: 1.2**
+**Version: 1.3**
 
 A WordPress plugin that extends WooCommerce to provide batch export and import functionality for product categories, along with category manipulation tools.
 
@@ -18,7 +18,9 @@ The plugin also supports the WooCommerce Protected Categories extension, allowin
 - Support for WooCommerce Protected Categories visibility settings
 - **Move products between categories** in bulk
 - **Batch description update** for multiple categories
+- **Delete products in category** - permanently delete all products and variants from selected categories
 - User-friendly interface integrated with WooCommerce admin
+- Real-time progress tracking for bulk operations
 - Comprehensive validation and error reporting
 
 ## Requirements
@@ -99,6 +101,30 @@ The plugin uses CSV files with the following columns:
 - The description field supports multi-line text
 - All selected categories will receive exactly the same description
 
+#### Delete Products in Category
+
+1. Go to Products > Category Manipulations
+2. Scroll to the "Delete products in category" section
+3. Select one or more categories using the checkboxes
+   - Use "Check All" to select all categories
+   - Use "Uncheck All" to deselect all categories
+4. Click "Delete" button (you will be asked to confirm)
+5. Watch the progress bar as products are being deleted in batches
+6. Review the deletion statistics showing:
+   - Total products deleted
+   - Failed deletions (if any)
+   - Failed product IDs (if any)
+
+**Important Notes about Delete Products:**
+- **THIS OPERATION PERMANENTLY DELETES ALL PRODUCTS AND THEIR VARIANTS** from the selected categories
+- **THIS ACTION CANNOT BE UNDONE!**
+- Products are deleted using WooCommerce operations to ensure proper cleanup
+- Variable products will have all their variations deleted automatically
+- Products are processed in batches of 10 to prevent timeouts
+- A real-time progress bar shows the deletion progress
+- If a product belongs to multiple categories, it will still be deleted entirely from the system
+- The page will automatically reload after successful deletion to update category counts
+
 ## Sample CSV
 
 ```
@@ -124,7 +150,8 @@ After activation, the plugin adds the following menu items under **Products** in
 - Export Product Categories
 - **Category Manipulations**
   - Move products between categories
-  - **Batch description between categories**
+  - Batch description between categories
+  - **Delete products in category**
 
 ## License
 
@@ -141,10 +168,19 @@ For support, please visit [https://www.quadarax.com/plugins/studiou-wc-product-c
 
 ## Changelog
 
+### Version 1.3.0
+- **New feature: Delete products in category** - permanently delete all products and variants from selected categories
+- Real-time progress bar for deletion operations
+- Batch processing for large-scale deletions (processes 10 products at a time)
+- Automatic handling of variable products and their variations
+- Comprehensive deletion statistics and error reporting
+- Enhanced warning messages for destructive operations
+- Improved user interface with confirmation dialogs
+
 ### Version 1.2.0
 - Added Category Manipulations functionality
 - New feature: Move products between categories
-- **New feature: Batch description between categories**
+- New feature: Batch description between categories
 - Enhanced admin interface with better user feedback
 - Improved security with proper nonce validation
 - Added comprehensive documentation

+ 8 - 3
studiou-wc-product-cat-manage/studiou-wc-product-cat-manage.php

@@ -3,7 +3,7 @@
  * Plugin Name: QDR - Studiou WC Export/Import Product Categories
  * Plugin URI: https://www.quadarax.com/plugins/studiou-wc-product-cat-manage
  * Description: Allows batch import and export WooCommerce product categories
- * Version: 1.2.2
+ * Version: 1.3.0
  * Requires at least: 6.8.1
  * Requires PHP: 8.2
  * Author: Dalibor Votruba
@@ -23,7 +23,7 @@ if (!defined('WPINC')) {
 }
 
 // Define plugin constants
-if (!defined('STUDIOU_WCPCM_VERSION')) define('STUDIOU_WCPCM_VERSION', '1.2.2');
+if (!defined('STUDIOU_WCPCM_VERSION')) define('STUDIOU_WCPCM_VERSION', '1.3.0');
 if (!defined('STUDIOU_WCPCM_PLUGIN_DIR')) define('STUDIOU_WCPCM_PLUGIN_DIR', plugin_dir_path(__FILE__));
 if (!defined('STUDIOU_WCPCM_PLUGIN_URL')) define('STUDIOU_WCPCM_PLUGIN_URL', plugin_dir_url(__FILE__));
 if (!defined('STUDIOU_WCPCM_PLUGIN_BASENAME')) define('STUDIOU_WCPCM_PLUGIN_BASENAME', plugin_basename(__FILE__));
@@ -200,7 +200,7 @@ class Studiou_WC_Product_Cat_Manage {
                 array(),
                 STUDIOU_WCPCM_VERSION
             );
-            
+
             wp_enqueue_script(
                 'studiou-wcpcm-admin',
                 STUDIOU_WCPCM_PLUGIN_URL . 'assets/js/admin.js',
@@ -222,6 +222,11 @@ class Studiou_WC_Product_Cat_Manage {
                     'selectDifferentCategories' => __('Please select different source and target categories', 'studiou-wc-product-cat-manage'),
                     'batchDescriptionSuccess' => __('Batch description operation successful', 'studiou-wc-product-cat-manage'),
                     'batchDescriptionError' => __('Batch description operation error', 'studiou-wc-product-cat-manage'),
+                    'deleteSuccess' => __('Delete operation successful', 'studiou-wc-product-cat-manage'),
+                    'deleteError' => __('Delete operation error', 'studiou-wc-product-cat-manage'),
+                    'confirmDelete' => __('Are you sure you want to permanently delete ALL products in the selected categories? This action cannot be undone!', 'studiou-wc-product-cat-manage'),
+                    'selectAtLeastOneCategory' => __('Please select at least one category', 'studiou-wc-product-cat-manage'),
+                    'noProductsFound' => __('No products found in selected categories', 'studiou-wc-product-cat-manage'),
                 )
             ));
             

+ 63 - 0
studiou-wc-product-cat-manage/views/manipulations.php

@@ -138,6 +138,68 @@ $categories = $manipulator->get_categories_with_counts();
         </div>
     </div>
     
+    <!-- Delete Products in Category Operation -->
+    <div class="studiou-wcpcm-card">
+        <h2><?php echo esc_html__('Delete products in category', 'studiou-wc-product-cat-manage'); ?></h2>
+
+        <p><?php echo esc_html__('This operation allows you to permanently delete all products and their variants from selected categories. This action cannot be undone!', 'studiou-wc-product-cat-manage'); ?></p>
+
+        <form id="studiou-wcpcm-delete-products-form" method="post">
+            <table class="form-table">
+                <tr>
+                    <th scope="row">
+                        <label for="delete_categories_list"><?php echo esc_html__('Select Categories', 'studiou-wc-product-cat-manage'); ?></label>
+                    </th>
+                    <td>
+                        <div class="studiou-wcpcm-categories-list-container">
+                            <div class="studiou-wcpcm-list-actions">
+                                <button type="button" id="studiou-wcpcm-delete-check-all" class="button">
+                                    <?php echo esc_html__('Check All', 'studiou-wc-product-cat-manage'); ?>
+                                </button>
+                                <button type="button" id="studiou-wcpcm-delete-uncheck-all" class="button">
+                                    <?php echo esc_html__('Uncheck All', 'studiou-wc-product-cat-manage'); ?>
+                                </button>
+                            </div>
+                            <div class="studiou-wcpcm-categories-list" id="delete_categories_list">
+                                <?php foreach ($categories as $category): ?>
+                                    <label class="studiou-wcpcm-category-item">
+                                        <input type="checkbox" name="delete_categories[]" value="<?php echo esc_attr($category['id']); ?>">
+                                        <span><?php echo esc_html($category['display_name']); ?></span>
+                                    </label>
+                                <?php endforeach; ?>
+                            </div>
+                        </div>
+                        <p class="description"><?php echo esc_html__('Select the categories from which all products will be permanently deleted.', 'studiou-wc-product-cat-manage'); ?></p>
+                    </td>
+                </tr>
+            </table>
+
+            <div class="studiou-wcpcm-warning-message">
+                <p><strong><?php echo esc_html__('WARNING: This operation will permanently delete all products (including variants) in the selected categories. This action cannot be undone!', 'studiou-wc-product-cat-manage'); ?></strong></p>
+            </div>
+
+            <div class="submit-buttons">
+                <button type="submit" class="button button-primary button-danger" id="studiou-wcpcm-delete-products-button">
+                    <?php echo esc_html__('Delete', 'studiou-wc-product-cat-manage'); ?>
+                </button>
+                <span class="spinner"></span>
+            </div>
+        </form>
+
+        <div class="studiou-wcpcm-delete-progress" style="display: none;">
+            <h3><?php echo esc_html__('Deletion Progress', 'studiou-wc-product-cat-manage'); ?></h3>
+            <div class="studiou-wcpcm-progress-bar-container">
+                <div class="studiou-wcpcm-progress-bar" id="studiou-wcpcm-delete-progress-bar"></div>
+            </div>
+            <div class="studiou-wcpcm-progress-text" id="studiou-wcpcm-delete-progress-text"></div>
+        </div>
+
+        <div class="studiou-wcpcm-delete-results" style="display: none;">
+            <h3><?php echo esc_html__('Deletion Results', 'studiou-wc-product-cat-manage'); ?></h3>
+            <div id="studiou-wcpcm-delete-message"></div>
+        </div>
+    </div>
+
     <!-- Important Notes -->
     <div class="studiou-wcpcm-card">
         <h2><?php echo esc_html__('Important Notes', 'studiou-wc-product-cat-manage'); ?></h2>
@@ -147,6 +209,7 @@ $categories = $manipulator->get_categories_with_counts();
             <li><?php echo esc_html__('If a product is assigned to multiple categories including the source category, it will remain in the other categories and be moved to the target category.', 'studiou-wc-product-cat-manage'); ?></li>
             <li><?php echo esc_html__('The operation processes all products regardless of their status (published, draft, private).', 'studiou-wc-product-cat-manage'); ?></li>
             <li><?php echo esc_html__('The batch description operation will overwrite the existing descriptions for all selected categories.', 'studiou-wc-product-cat-manage'); ?></li>
+            <li><strong><?php echo esc_html__('The delete products operation will permanently delete all products and variants from the selected categories. This action cannot be undone!', 'studiou-wc-product-cat-manage'); ?></strong></li>
         </ul>
     </div>
 </div>