|
|
@@ -38,6 +38,8 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
|
|
|
add_action('wp_ajax_studiou_wcpcm_delete_products_batch', array($this, 'handle_delete_products_batch_ajax'));
|
|
|
add_action('wp_ajax_studiou_wcpcm_clear_media', array($this, 'handle_clear_media_ajax'));
|
|
|
add_action('wp_ajax_studiou_wcpcm_clear_media_batch', array($this, 'handle_clear_media_batch_ajax'));
|
|
|
+ add_action('wp_ajax_studiou_wcpcm_remove_unassigned_media', array($this, 'handle_remove_unassigned_media_ajax'));
|
|
|
+ add_action('wp_ajax_studiou_wcpcm_remove_unassigned_media_batch', array($this, 'handle_remove_unassigned_media_batch_ajax'));
|
|
|
|
|
|
// Add a test AJAX handler to verify AJAX is working
|
|
|
add_action('wp_ajax_studiou_wcpcm_test', array($this, 'handle_test_ajax'));
|
|
|
@@ -1003,12 +1005,22 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
|
|
|
* @return array Result with counts
|
|
|
*/
|
|
|
private function delete_media_batch($media_ids) {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
$deleted_count = 0;
|
|
|
$failed_count = 0;
|
|
|
$failed_media = array();
|
|
|
$affected_term_ids = array();
|
|
|
$taxonomy = $this->get_media_category_taxonomy();
|
|
|
|
|
|
+ // Ensure required file functions are loaded
|
|
|
+ if (!function_exists('wp_delete_attachment')) {
|
|
|
+ require_once ABSPATH . 'wp-admin/includes/post.php';
|
|
|
+ }
|
|
|
+ if (!function_exists('wp_delete_file')) {
|
|
|
+ require_once ABSPATH . 'wp-includes/functions.php';
|
|
|
+ }
|
|
|
+
|
|
|
error_log('STUDIOU WC MEDIA: delete_media_batch called with ' . count($media_ids) . ' media IDs');
|
|
|
|
|
|
foreach ($media_ids as $media_id) {
|
|
|
@@ -1030,16 +1042,73 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Force delete attachment and its files from disk
|
|
|
- $result = wp_delete_attachment($media_id, true);
|
|
|
+ // Get file paths BEFORE deletion
|
|
|
+ $file = get_attached_file($media_id);
|
|
|
+ $meta = wp_get_attachment_metadata($media_id);
|
|
|
+ $upload_dir = wp_get_upload_dir();
|
|
|
|
|
|
- if ($result) {
|
|
|
+ error_log('STUDIOU WC MEDIA: Deleting media ID ' . $media_id . ', file: ' . ($file ?: 'none'));
|
|
|
+
|
|
|
+ // Attempt standard WordPress deletion
|
|
|
+ wp_delete_attachment($media_id, true);
|
|
|
+
|
|
|
+ // Verify the post was actually deleted
|
|
|
+ $still_exists = $wpdb->get_var($wpdb->prepare(
|
|
|
+ "SELECT ID FROM {$wpdb->posts} WHERE ID = %d",
|
|
|
+ $media_id
|
|
|
+ ));
|
|
|
+
|
|
|
+ if ($still_exists) {
|
|
|
+ error_log('STUDIOU WC MEDIA: wp_delete_attachment did not delete post ' . $media_id . ', forcing manual deletion');
|
|
|
+
|
|
|
+ // Force manual deletion: remove term relationships
|
|
|
+ $all_taxonomies = get_object_taxonomies('attachment');
|
|
|
+ if (!empty($all_taxonomies)) {
|
|
|
+ wp_delete_object_term_relationships($media_id, $all_taxonomies);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Remove all post meta
|
|
|
+ $wpdb->delete($wpdb->postmeta, array('post_id' => $media_id));
|
|
|
+
|
|
|
+ // Remove the post itself
|
|
|
+ $wpdb->delete($wpdb->posts, array('ID' => $media_id));
|
|
|
+
|
|
|
+ // Clean cache
|
|
|
+ clean_post_cache($media_id);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Delete physical files from disk
|
|
|
+ if ($file && file_exists($file)) {
|
|
|
+ @unlink($file);
|
|
|
+ error_log('STUDIOU WC MEDIA: Deleted main file: ' . $file);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Delete thumbnail/intermediate sizes
|
|
|
+ if (!empty($meta['sizes']) && !empty($upload_dir['basedir'])) {
|
|
|
+ $file_dir = !empty($file) ? trailingslashit(dirname($file)) : '';
|
|
|
+ foreach ($meta['sizes'] as $size => $sizeinfo) {
|
|
|
+ if (!empty($sizeinfo['file'])) {
|
|
|
+ $intermediate_file = $file_dir . $sizeinfo['file'];
|
|
|
+ if (file_exists($intermediate_file)) {
|
|
|
+ @unlink($intermediate_file);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Final verification
|
|
|
+ $final_check = $wpdb->get_var($wpdb->prepare(
|
|
|
+ "SELECT ID FROM {$wpdb->posts} WHERE ID = %d",
|
|
|
+ $media_id
|
|
|
+ ));
|
|
|
+
|
|
|
+ if (!$final_check) {
|
|
|
$deleted_count++;
|
|
|
error_log('STUDIOU WC MEDIA: Successfully deleted media file ' . $media_id);
|
|
|
} else {
|
|
|
$failed_count++;
|
|
|
$failed_media[] = $media_id;
|
|
|
- error_log('STUDIOU WC MEDIA: Failed to delete media file ' . $media_id);
|
|
|
+ error_log('STUDIOU WC MEDIA: FAILED to delete media file ' . $media_id . ' even after manual cleanup');
|
|
|
}
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
@@ -1222,4 +1291,192 @@ class Studiou_WC_Product_Cat_Manage_Manipulator {
|
|
|
ob_end_clean();
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get count of unassigned unused media files
|
|
|
+ *
|
|
|
+ * @return int Count of media files not in any category and not referenced
|
|
|
+ */
|
|
|
+ public function get_unassigned_unused_media_count() {
|
|
|
+ $ids = $this->get_unassigned_unused_media_ids();
|
|
|
+ return count($ids);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get IDs of media files that are not assigned to any media category
|
|
|
+ * and not used as featured image, product gallery image, or attached to any post.
|
|
|
+ *
|
|
|
+ * @return array Array of attachment IDs
|
|
|
+ */
|
|
|
+ public function get_unassigned_unused_media_ids() {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ $taxonomy = $this->get_media_category_taxonomy();
|
|
|
+
|
|
|
+ // Base query: all attachments not used as featured image and not attached to a post
|
|
|
+ $sql = "
|
|
|
+ SELECT a.ID
|
|
|
+ FROM {$wpdb->posts} a
|
|
|
+ LEFT JOIN {$wpdb->postmeta} pm_thumb
|
|
|
+ ON a.ID = CAST(pm_thumb.meta_value AS UNSIGNED)
|
|
|
+ AND pm_thumb.meta_key = '_thumbnail_id'
|
|
|
+ WHERE a.post_type = 'attachment'
|
|
|
+ AND a.post_parent = 0
|
|
|
+ AND pm_thumb.meta_id IS NULL
|
|
|
+ ";
|
|
|
+
|
|
|
+ // If media category taxonomy exists, also exclude categorized media
|
|
|
+ if ($taxonomy) {
|
|
|
+ $sql .= $wpdb->prepare("
|
|
|
+ AND a.ID NOT IN (
|
|
|
+ SELECT tr.object_id
|
|
|
+ FROM {$wpdb->term_relationships} tr
|
|
|
+ INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
|
|
|
+ WHERE tt.taxonomy = %s
|
|
|
+ )
|
|
|
+ ", $taxonomy);
|
|
|
+ }
|
|
|
+
|
|
|
+ $media_ids = $wpdb->get_col($sql);
|
|
|
+
|
|
|
+ if (empty($media_ids)) {
|
|
|
+ return array();
|
|
|
+ }
|
|
|
+
|
|
|
+ $media_ids = array_map('intval', $media_ids);
|
|
|
+
|
|
|
+ // Filter out media used in WooCommerce product galleries (_product_image_gallery)
|
|
|
+ $gallery_meta = $wpdb->get_col(
|
|
|
+ "SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_product_image_gallery' AND meta_value != ''"
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!empty($gallery_meta)) {
|
|
|
+ $gallery_ids = array();
|
|
|
+ foreach ($gallery_meta as $gallery_value) {
|
|
|
+ $ids = array_map('intval', array_filter(explode(',', $gallery_value)));
|
|
|
+ $gallery_ids = array_merge($gallery_ids, $ids);
|
|
|
+ }
|
|
|
+ $gallery_ids = array_unique($gallery_ids);
|
|
|
+
|
|
|
+ if (!empty($gallery_ids)) {
|
|
|
+ $media_ids = array_diff($media_ids, $gallery_ids);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ error_log('STUDIOU WC MEDIA: Found ' . count($media_ids) . ' unassigned unused media files');
|
|
|
+
|
|
|
+ return array_values($media_ids);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Handle AJAX remove unassigned media request (get IDs)
|
|
|
+ */
|
|
|
+ public function handle_remove_unassigned_media_ajax() {
|
|
|
+ while (ob_get_level()) {
|
|
|
+ ob_end_clean();
|
|
|
+ }
|
|
|
+
|
|
|
+ ob_start();
|
|
|
+
|
|
|
+ $original_error_reporting = error_reporting();
|
|
|
+ error_reporting(0);
|
|
|
+
|
|
|
+ error_log('STUDIOU WC MEDIA: Remove unassigned media AJAX handler called');
|
|
|
+
|
|
|
+ try {
|
|
|
+ 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')));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!current_user_can('manage_woocommerce')) {
|
|
|
+ wp_send_json_error(array('message' => __('You do not have sufficient permissions', 'studiou-wc-product-cat-manage')));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $media_ids = $this->get_unassigned_unused_media_ids();
|
|
|
+
|
|
|
+ error_log('STUDIOU WC MEDIA: Found ' . count($media_ids) . ' unassigned unused media files to delete');
|
|
|
+
|
|
|
+ ob_clean();
|
|
|
+
|
|
|
+ wp_send_json_success(array(
|
|
|
+ 'media_ids' => $media_ids,
|
|
|
+ 'total_count' => count($media_ids),
|
|
|
+ 'message' => sprintf(
|
|
|
+ __('Found %d unused uncategorized media files to delete', 'studiou-wc-product-cat-manage'),
|
|
|
+ count($media_ids)
|
|
|
+ )
|
|
|
+ ));
|
|
|
+
|
|
|
+ } catch (Exception $e) {
|
|
|
+ error_log('STUDIOU WC MEDIA: Remove unassigned media failed - ' . $e->getMessage());
|
|
|
+ ob_clean();
|
|
|
+ wp_send_json_error(array(
|
|
|
+ 'message' => sprintf(__('Error: %s', 'studiou-wc-product-cat-manage'), $e->getMessage())
|
|
|
+ ));
|
|
|
+ } finally {
|
|
|
+ error_reporting($original_error_reporting);
|
|
|
+ ob_end_clean();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Handle AJAX remove unassigned media batch request
|
|
|
+ */
|
|
|
+ public function handle_remove_unassigned_media_batch_ajax() {
|
|
|
+ while (ob_get_level()) {
|
|
|
+ ob_end_clean();
|
|
|
+ }
|
|
|
+
|
|
|
+ ob_start();
|
|
|
+
|
|
|
+ $original_error_reporting = error_reporting();
|
|
|
+ error_reporting(0);
|
|
|
+
|
|
|
+ error_log('STUDIOU WC MEDIA: Remove unassigned media batch AJAX handler called');
|
|
|
+
|
|
|
+ try {
|
|
|
+ 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')));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!current_user_can('manage_woocommerce')) {
|
|
|
+ wp_send_json_error(array('message' => __('You do not have sufficient permissions', 'studiou-wc-product-cat-manage')));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isset($_POST['media_ids'])) {
|
|
|
+ wp_send_json_error(array('message' => __('Missing required parameters', 'studiou-wc-product-cat-manage')));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $media_ids = array_map('intval', $_POST['media_ids']);
|
|
|
+
|
|
|
+ error_log('STUDIOU WC MEDIA: Processing batch of ' . count($media_ids) . ' unassigned media for deletion');
|
|
|
+
|
|
|
+ $result = $this->delete_media_batch($media_ids);
|
|
|
+
|
|
|
+ error_log('STUDIOU WC MEDIA: Unassigned batch completed - Deleted: ' . $result['deleted_count'] . ', Failed: ' . $result['failed_count']);
|
|
|
+
|
|
|
+ ob_clean();
|
|
|
+
|
|
|
+ wp_send_json_success(array(
|
|
|
+ 'deleted_count' => $result['deleted_count'],
|
|
|
+ 'failed_count' => $result['failed_count'],
|
|
|
+ 'failed_media' => $result['failed_media']
|
|
|
+ ));
|
|
|
+
|
|
|
+ } catch (Exception $e) {
|
|
|
+ error_log('STUDIOU WC MEDIA: Unassigned media batch failed - ' . $e->getMessage());
|
|
|
+ ob_clean();
|
|
|
+ wp_send_json_error(array(
|
|
|
+ 'message' => sprintf(__('Error: %s', 'studiou-wc-product-cat-manage'), $e->getMessage())
|
|
|
+ ));
|
|
|
+ } finally {
|
|
|
+ error_reporting($original_error_reporting);
|
|
|
+ ob_end_clean();
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|